<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
	<channel>
		<title>Posts on The Danger Zone</title>
		<link>https://therealmjp.github.io/posts/</link>
		<description>Recent content in Posts on The Danger Zone</description>
		<generator>Hugo -- gohugo.io</generator>
		<language>en-us</language>
		<lastBuildDate>Sun, 07 Sep 2025 00:00:00 +0000</lastBuildDate>
		<atom:link href="https://therealmjp.github.io/posts/index.xml" rel="self" type="application/rss+xml" />
		
		<item>
			<title>Ten Years of D3D12</title>
			<link>https://therealmjp.github.io/posts/ten-years-of-d3d12/</link>
			<pubDate>Sun, 07 Sep 2025 00:00:00 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/ten-years-of-d3d12/</guid>
			<description>For those of us that have been using it from the start, it can be hard to believe that Direct3D 12 has been around for ten years now. Windows 10 was released on July 29th 2015, and D3D12 has been with us ever since. While it&amp;rsquo;s true that this is the longest we&amp;rsquo;ve gone between major D3D version updates, it&amp;rsquo;s also not fair to say that the API has remained static.</description>
			<content:encoded><![CDATA[<p><img src="/images/ten-years-of-d3d12/aging_damon.png" alt=""></p>
<p>For those of us that have been using it from the start, it can be hard to believe that Direct3D 12 has been around for ten years now. Windows 10 was released on July 29th 2015, and D3D12 has been with us ever since. While it&rsquo;s true that this is the longest we&rsquo;ve gone between major D3D version updates, it&rsquo;s also not fair to say that the API has remained static. On the contrary, D3D12 has received a steady stream of new interfaces, functions, and shader models. These updates have included some very major features that have gathered lots of press and attention, such as DXR or Work Graphs. But there&rsquo;s also been a long line of smaller features and quality-of-life updates that can make a developer&rsquo;s life easier. In addition, HLSL as a shader language has also seen steady updates, with some of them being rather transformative.</p>
<p>Given the recently-passed 10 year anniversary, I thought it would be a good time to look back at what&rsquo;s changed in the world of D3D12 since it was released. After that, I thought it would also be interesting to reflect on what&rsquo;s changed for me personally as my coding and development style has evolved along with APIs and programming languages. In some cases I&rsquo;ve been able to streamline things quite a bit thanks to API enhancements, and in others I&rsquo;ve just embraced new ways of doing things.</p>
<p>Let&rsquo;s dive In! Feel free to use the TOC below if you want to skip around.</p>
<div>
    <nav id="TableOfContents">
  <ul>
    <li><a href="#additions-to-the-core-d3d12-api">Additions To the Core D3D12 API</a>
      <ul>
        <li><a href="#programmable-sample-points">Programmable Sample Points</a></li>
        <li><a href="#view-instancing">View Instancing</a></li>
        <li><a href="#depth-bounds-test">Depth Bounds Test</a></li>
        <li><a href="#variable-rate-shading">Variable Rate Shading</a></li>
        <li><a href="#relaxed-format-casting">Relaxed Format Casting</a></li>
        <li><a href="#directly-writing-to-block-compressed-formats">Directly Writing To Block-Compressed Formats</a></li>
        <li><a href="#writable-r9g9b9e5-textures">Writable R9G9B9E5 Textures</a></li>
        <li><a href="#writebufferimmediate-and-openexistingheapfromaddress">WriteBufferImmediate and OpenExistingHeapFromAddress</a></li>
        <li><a href="#gpu-upload-heaps">GPU Upload Heaps</a></li>
        <li><a href="#executeindirect-incrementing-constant">ExecuteIndirect Incrementing Constant</a></li>
        <li><a href="#sampler-feedback">Sampler Feedback</a></li>
        <li><a href="#tight-resource-alignment">Tight Resource Alignment</a></li>
        <li><a href="#render-passes">Render Passes</a></li>
        <li><a href="#enhanced-barriers">Enhanced Barriers</a></li>
        <li><a href="#ray-tracing">Ray Tracing</a></li>
        <li><a href="#mesh-shaders">Mesh Shaders</a></li>
        <li><a href="#work-graphs">Work Graphs</a></li>
      </ul>
    </li>
    <li><a href="#new-shader-features">New Shader Features</a>
      <ul>
        <li><a href="#wave-ops">Wave Ops</a></li>
        <li><a href="#quad-ops">Quad Ops</a></li>
        <li><a href="#16-bit-floats-and-integers">16-bit floats and integers</a></li>
        <li><a href="#barycentrics">Barycentrics</a></li>
        <li><a href="#shader-libraries--linking">Shader Libraries &amp; Linking</a></li>
        <li><a href="#helper-lane-detection-and-control">Helper Lane Detection And Control</a></li>
        <li><a href="#dynamic-resources-resourcedescriptorheap">Dynamic Resources (ResourceDescriptorHeap)</a></li>
        <li><a href="#wavesize">WaveSize</a></li>
        <li><a href="#derivatives-in-compute">Derivatives In Compute</a></li>
        <li><a href="#64-bit-atomics-and-atomic-cas-for-floats">64-Bit Atomics and Atomic CAS For Floats</a></li>
        <li><a href="#raw-texture-gather">Raw Texture Gather</a></li>
        <li><a href="#writable-msaa-textures">Writable MSAA Textures</a></li>
        <li><a href="#samplecmplevel">SampleCmpLevel</a></li>
        <li><a href="#byteaddressbuffer-types-load-and-store">ByteAddressBuffer Types Load and Store</a></li>
        <li><a href="#sv_startvertexlocation--sv_startinstancelocation">SV_StartVertexLocation + SV_StartInstanceLocation</a></li>
        <li><a href="#enums">Enums</a></li>
        <li><a href="#templates">Templates</a></li>
        <li><a href="#operator-overloading">Operator Overloading</a></li>
        <li><a href="#bitfields">Bitfields</a></li>
        <li><a href="#c-style-function-overloading-and-for-loop-scoping">C++-Style Function Overloading and For Loop Scoping</a></li>
      </ul>
    </li>
    <li><a href="#developer-workflow-enhancements">Developer Workflow Enhancements</a>
      <ul>
        <li><a href="#agility-sdk--independent-devices">Agility SDK + Independent Devices</a></li>
        <li><a href="#debug-layer-callback">Debug Layer Callback</a></li>
        <li><a href="#device-removed-extended-data-dred">Device Removed Extended Data (DRED)</a></li>
      </ul>
    </li>
    <li><a href="#whats-changed-for-me-personally">What&rsquo;s Changed For Me Personally</a>
      <ul>
        <li><a href="#all-in-on-bindless-and-user-space-bindings">All-In On Bindless and &ldquo;User-Space Bindings&rdquo;</a></li>
        <li><a href="#hlsl-templates">HLSL Templates</a></li>
        <li><a href="#more-code-sharing-between-hlsl-and-c">More Code Sharing Between HLSL and C++</a></li>
        <li><a href="#designated-initializers">Designated Initializers</a></li>
        <li><a href="#printf-and-other-shader-debug-features">Printf and Other Shader Debug Features</a></li>
      </ul>
    </li>
    <li><a href="#conclusion">Conclusion</a></li>
  </ul>
</nav>
</div>
<h2 id="additions-to-the-core-d3d12-api">Additions To the Core D3D12 API</h2>
<h3 id="programmable-sample-points">Programmable Sample Points</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/ProgrammableSamplePositions.html">Spec Link</a></p>
<p>This is not a commonly used feature, since its use cases are pretty niche and <a href="../msaa-overview/">MSAA</a> is no longer popular. In short, this adds <a href="https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12graphicscommandlist1-setsamplepositions">a new API</a> that lets you provide your own custom MSAA subsample positions instead of using the rotated grid pattern that the &ldquo;standard&rdquo; sample provides:</p>
<p><img src="/images/ten-years-of-d3d12/standard_msaa_pattern.png" alt=""></p>
<p>The API allows you to specify the positions over a 2x2 quad of pixels rather than just a single pixel, which you could potentially use to get a <a href="https://citeseerx.ist.psu.edu/document?repid=rep1&amp;type=pdf&amp;doi=1bbf27a398f8f6a372b8500f06e737a5655208a9">more irregular sampling pattern</a> that changes aliasing characteristics. Or at least, you can do that if you still care about MSAA. :)</p>
<p>The other main use case is for software implementations of variable rate shading, which is something that Activition has <a href="https://research.activision.com/publications/archives/rendering-of-call-of-dutyinfinite-warfare">presented in the past</a> on <a href="https://research.activision.com/publications/2020-09/software-based-variable-rate-shading-in-call-of-duty--modern-war">more than one occasion</a>. It&rsquo;s also something I&rsquo;ve <a href="../programmable-sample-points/">dabbled in myself</a> in the past. The reason programmable sample points are useful is so that you can generate a &ldquo;half-res&rdquo; depth buffer with 4x MSAA and have the depth subsample positions exactly match what was used for the full-res pass. Of course these days you also have hardware VRS as another option (which we&rsquo;ll get to soon!), which renders this use case fairly niche.</p>
<h3 id="view-instancing">View Instancing</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/ViewInstancing.html">Spec Link</a></p>
<p>Another less-commonly-used feature is View Instancing. This provides API and shader mechanisms that try to make it easy and optimal to render in stereo for VR or 3D. The way it works is your draws will get instanced N times (for up to 4 views), and in any shader you can take <code>SV_ViewID</code> as an input to know which view you&rsquo;re drawing to. This allows you to choose and apply the appropriate view + projection matrices for that view. Then on the app side of things you specify whether you want each view to get mapped to a different viewport, or to a different slice of an array texture. You can also setup a bitfield mask on the app side, which allows you to cull separately for each view if you want to.</p>
<p>In theory this API could benefit hardware that has some native functionality for broadcasting to multiple views if used correctly, which as far as I know is really just Nvidia with its &ldquo;viewport broadcast&rdquo; feature of the GS. In practice I&rsquo;m not sure it really provided any serious benefit, since I imagine most apps would consume <code>SV_ViewID</code> in the vertex shader rather than trying to use it later in the pipeline. AMD also has never exposed more than <a href="https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_view_instancing_tier">Tier 1</a> support, which means there&rsquo;s some sort of &ldquo;draw-call level looping&rdquo; going on rather than anything fancy. If that&rsquo;s the case, you&rsquo;re likely better off by just doing it yourself with regular instancing + <code>SV_RenderTargetArrayIndex</code>/<code>SV_ViewportArrayIndex</code> being output by the vertex shader.</p>
<h3 id="depth-bounds-test">Depth Bounds Test</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/DepthBoundsTest.html">Spec Link</a></p>
<p>The depth bounds test is old&hellip;so old it was initially added to accelerate <em>stencil shadows</em>. Remember when Doom 3 was new? It&rsquo;s been exposed in other APIs in various ways, but somehow it never got officially added to D3D until D3D12. Either way it&rsquo;s now exposed if you have a good use for it. Basically how it works is on the command list you specify a min and max depth value which serve as the &ldquo;bounds&rdquo;. Then when you draw some triangles, each pixel checks the existing depth value (not the depth of the triangle being rasterized) and passes the depth test if the value is within the bounds that were provided.</p>
<p>I&rsquo;m not sure what use cases are still out there for this feature&hellip;I know some renderers have used it for old-school style deferred rendering where light bounding volumes were rasterized one at a time. Deferred decals that are rasterized as bounding boxes could also potentially benefit in the same way. There&rsquo;s also an old trick from the PS3 days for rendering deferred cascaded shadows one cascade at a time, where you would set the depth bounds to the cascade min/max depth and then draw a full-screen triangle.</p>
<h3 id="variable-rate-shading">Variable Rate Shading</h3>
<p><img src="/images/ten-years-of-d3d12/vrs.jpg" alt=""></p>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/VariableRateShading.html">Spec Link</a></p>
<p>Variable Rate Shading, or VRS for short, is all about trying to reduce expensive pixel shader threads for places where you don&rsquo;t need it. The idea is that you choose a &ldquo;shading rate&rdquo;, and then the pixel shader will only run for 1 / N pixels. The nice part is that you still get full-resolution depth testing, which you won&rsquo;t get if you draw things to a separate half-res render target. VRS also gives you very fine granularity for choosing the shading rate, which makes it much easier to mix lower shading rate content with full shading rate content. That can be particularly important for particles and transparents if not using OIT, since you need to draw them in back-to-front order. A separate half-res pass with a composite makes draw-based sorting impossible without introducing many low-res passes, which would end up negating any performance benefits.</p>
<p>The API allows you to specify the shading rate in 3 ways:</p>
<ul>
<li>Per-draw, from the command list</li>
<li>Per-primitive, by emitting <code>SV_ShadingRate</code> from a vertex shader/mesh shader/geometry shader</li>
<li>Per-tile in screenspace, by generating a shading-rate texture (with maximum 16x16 tile size)</li>
</ul>
<p>In particular the tile-based shading rate is useful if you want to try to analyze the previous frame contents and use that to automatically generate an optimized shading rate based on the variance of the content. Whereas per-draw is probably more useful for manually tagging half-res transparent or particle draws.</p>
<p>There&rsquo;s two main downsides to watch out for with VRS:</p>
<ul>
<li>The performance gains may not be as high as you&rsquo;d expect, because the hardware is still going to need to launch pixel shaders in 2x2 quads and is also still going to need to fill full waves before it can execute.</li>
<li>Any low-res draws or tiles will broadcast the coarse pixel output value to the actual pixels, effectively point sampling the results. So you probably want to add some extra spatial filtering and/or temporal reconstruction to get better quality.</li>
</ul>
<h3 id="relaxed-format-casting">Relaxed Format Casting</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/RelaxedCasting.html">Spec Link</a></p>
<p>This is not so much a new API feature as it as a relaxing of the rules around creating multiple views with &ldquo;compatible&rdquo; formats. Initially in D3D12 the rules were the same as D3D11, which required you to create a texture resource with a TYPELESS format if you were going to view it through multiple compatible formats. So for example you would use R16_TYPELESS if you were going to view the texture as both R16_UINT and R16_UNORM. This is a bit inconvenient, since you have to know in advance if you&rsquo;re going to view the resource with multiple formats. It can also result in pessimizations from the driver, since it may not be able to use certain compressions or optimizations if it has to assume that you will view the texture as both integer and floating-point data.</p>
<p>With relaxed format casting this is no longer necessary: you can create the resource fully &ldquo;typed&rdquo;, and then create compatible views all you&rsquo;d like. The catch is that you can&rsquo;t cast between floating-point and non-floating point formats, which avoids the situation you can get with TYPELESS where the driver might disable optimizations. This is especially nice for depth buffers, which previously required you to use TYPELESS if you were ever going to read the depth texture through a SRV.</p>
<h3 id="directly-writing-to-block-compressed-formats">Directly Writing To Block-Compressed Formats</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/VulkanOn12.html#format-list-casting">Spec Link</a></p>
<p>Historically, the only way for the GPU to write to a texture with a block compressed format was to copy to it. To allow for performing block compression on the GPU, there was a &ldquo;backdoor&rdquo; added in D3D10 that allowed you to copy from a 1/4 width and 1/4 height texture with an equivalent UINT format (R32G32_UINT or R32G32B32A32_UINT) into a BC-formatted texture. This is obviously not ideal, since it would be faster to just write directly to the final texture instead of requiring an intermediary copy. As part of Enhanced Barriers (which I&rsquo;ll cover later), D3D12 caught up with Vulkan and gained the ability to write directly to the BC texture through a UAV. The way it works is similar to Vulkan: when you create the texture you pass a &ldquo;castable formats&rdquo; array, and that allows you to include the equivalent UINT format that you&rsquo;ll use for writing:</p>
<ul>
<li>R32G32_UINT (8 bytes per block) for BC1 and BC4</li>
<li>R32G32B32A32_UINT (16 bytes per block) for BC2, BC3, BC5, BC6, and BC7</li>
</ul>
<h3 id="writable-r9g9b9e5-textures">Writable R9G9B9E5 Textures</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/D3D12R9G9B9E5Format.html">Spec Link</a></p>
<p><code>DXGI_FORMAT_R9G9B9E5_SHAREDEXP</code> is an interesting format: it&rsquo;s similar to <a href="https://bartwronski.com/2017/04/02/small-float-formats-r11g11b10f-precision/">R11G11B10_FLOAT</a> in that it allows for storing positive floating point RGB values with 4 bytes per texel, and provides the same effective range as fp10/fp11/fp16 by virtue of having same number (5) of exponent bits. However unlike fp10/11, with R9G9B9E5 each channel gets 9 mantissa bits instead of the 6 or 5 that you get with R11G11B10. Therefore it can provide more precision and less visible banding in some cases. The catch is that the exponent is shared among the 3 channels, so it&rsquo;s going to work best when the overall magnitude is highly correlated among the 3 channels.</p>
<p>Traditionally this was a read-only format, where GPUs could automatically decode from it when sampling as a texture but had no means of automatically encoding to it for RTV or UAV writes. Your only option would be to write to it through a UAV that uses R32_UINT, and encode the bits yourself. With this new feature, capable GPUs can indicate that they support writing to <code>DXGI_FORMAT_R9G9B9E5_SHAREDEXP</code> through render targets or typed UAVs.</p>
<p>At the time of writing, support for this feature seems to be limited to AMD GPUs that are RDNA2 or newer.</p>
<h3 id="writebufferimmediate-and-openexistingheapfromaddress">WriteBufferImmediate and OpenExistingHeapFromAddress</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/D3D12WriteBufferImmediate.html">Spec Link</a></p>
<p>If you write D3D12 code, you&rsquo;re eventually going to crash the GPU. When this happens, it&rsquo;s nice to have even some vague information about what the GPU was doing when it gave up the ghost. <code>WriteBufferImmediate</code> and <code>OpenExistingHeapFromAddress</code> are a pair of APIs that aim to let you gather this sort of information by implementing &ldquo;breadcrumbs&rdquo;. Basically you use <code>WriteBufferImmediate</code> to write some sort of signal value at various points in your command list recording. Then when your device is removed, you use <a href="https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device3-openexistingheapfromaddress">OpenExistingHeapFromAddress</a> to access the breadcrumb buffer on the CPU and inspect it to figure out the last-written breadcrumb.</p>
<p>This functionality is also used under-the-hood by <code>DRED</code>, which I&rsquo;ll cover in more detail later on.</p>
<h3 id="gpu-upload-heaps">GPU Upload Heaps</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/D3D12GPUUploadHeaps.html">Spec Link</a></p>
<p>One of the early pain points for beginner D3D12 programmers is dealing with the fact that dedicated video cards have their own physical pool of VRAM that&rsquo;s separate from the &ldquo;SysRAM&rdquo; that&rsquo;s used by the CPU. Traditionally VRAM has not been directly accessible to the CPU, aside from small 256 MB region of VRAM accessible through the Base Address Register (BAR). This leads to <a href="../gpu-memory-pool/">tough choices about where to place resources, and how to use the GPU to copy from SysRAM to VRAM</a>.</p>
<p>New systems and video cards have something called &ldquo;Resizable BAR&rdquo; which allows the OS to effectively map all of VRAM into the CPU address space. D3D12 exposes this functionality through GPU Upload Heaps. This is a new <code>D3D12_HEAP_TYPE_GPU_UPLOAD</code> enum value that you can use when creating a heap or committed resource, which can then be mapped into the CPU virtual address space using <code>Map()</code>. That means you can have the CPU directly update the contents of a resource that lives in VRAM, without having to use GPU copies. For CPU-updated buffers this a no-brainer: the data layout is linear and so you can just memcpy away. For textures it&rsquo;s a bit more complicated&hellip;they use an opaque layout, and so you have to use <a href="https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12resource-writetosubresource">WriteToSubresource</a> if you want to update the contents.</p>
<p>AMD started supporting ReBAR for RDNA2, and Nvidia started supporting it for Ampere. Intel&rsquo;s dedicated GPUs all support it. Note that you typically need to enable ReBAR in your UEFI settings before it can be used. GPU Upload Heaps are also unfortunately only available on Windows 11, since it required kernel changes.</p>
<h3 id="executeindirect-incrementing-constant">ExecuteIndirect Incrementing Constant</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/IndirectDrawing.html#incrementing-constant">Spec Link</a></p>
<p>ExecuteIndirect is really cool. It wraps up indirect drawing, indirect dispatches, and multi-draw/dispatch through a unified interface. But one major deficiency relative to Vulkan has been the lack of a &ldquo;DrawID&rdquo; that the shader can use to access its draw-specific or mesh-specific data. ExecuteIndirect is flexible enough to let you do this yourself by setting a root constant for each draw as part of your command signature, but it turns out that this requires some rather fancy footwork from drivers to make this run as expected. Whereas generating a DrawID is something a lot of hardware can do natively.</p>
<p>Incrementing constant (mostly) closes this gap by letting you have an implicit constant buffer value passed to your shader that increments for each draw or dispatch. While it&rsquo;s not quite as nice as a <code>SV_DrawID</code> would be, it gets the job done.</p>
<p>At the time of writing this, Nvidia is the only vendor that reports support for Tier 1.1 execute indirect support with incrementing constant (which it does for all hardware going back to Pascal). I assume AMD and Intel will catch up at some point.</p>
<h3 id="sampler-feedback">Sampler Feedback</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/IndirectDrawing.html#incrementing-constant">Spec Link</a></p>
<p>Those of you who were around for the launch of Nvidia&rsquo;s RTX 2000 series (AKA Turing) might remember some of their claims of support for &ldquo;texture-space shading&rdquo;. That sure sounded really cool, but it turns what they actually had was sampler feedback. Basically this is a feature that lets you figure out which texels (within some granularity) were accessed given a UV and texture description. This gives you some rather precise data that you could use as part of feedback system to determine residency for a sparse virtual texture (or which texels you should shade for texture-space shading).</p>
<p>The big downside is that the API is pretty complicated. You have to setup a resource for storing the initial opaque feedback data, you have to bind an extra descriptor, you have to add the feedback generation to the shader, you have to &ldquo;resolve&rdquo; the feedback data into a readable format, and then finally you have to copy back to the CPU if you&rsquo;re going to read it there. After all of that, you can access your info about which regions were sampled. In my experience it&rsquo;s a lot more unwieldy than simply doing an atomic write from your shader, and it&rsquo;s also more expensive. So I probably wouldn&rsquo;t recommend it unless you need very exact feedback.</p>
<p>Nvidia supports this for Turing or newer, while AMD supports it for RDNA2+. Intel supports it for Xe or newer.</p>
<h3 id="tight-resource-alignment">Tight Resource Alignment</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/D3D12TightPlacedResourceAlignment.html">Spec Link</a></p>
<p>From the start, D3D12 made the decision to impose fixed <a href="https://asawicki.info/news_1726_secrets_of_direct3d_12_resource_alignment">alignment</a> requirements for resources. This is simpler compared to Vulkan, where alignments are reported by the device properties at runtime. The big downside is that the D3D alignments can be much larger in some cases, which leads to a lot of waste. In particular the dreaded <code>D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT</code> is 64 KB for buffers, which can quickly bloat memory usage if creating many placed resources for tiny buffers.</p>
<p>With &ldquo;tight placed resource alignment&rdquo;, you can now pass a new <code>D3D12_RESOURCE_FLAG_USE_TIGHT_ALIGNMENT</code> flag that supports much smaller alignment values. For buffers the range is now 8 to 256 bytes, while textures have 8 byte to 64 KB alignment for non-MSAA (8B to 4 MB for MSAA). This makes small placed buffer resources much more practical.</p>
<p>As of writing, this feature is still in preview and is only available in the 1.716.1 preview Agility SDK.</p>
<h3 id="render-passes">Render Passes</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/RenderPasses.html">Spec Link</a></p>
<p>If your target GPU is a tiler, then you really want render passes. These replace calls to <code>OMSetRenderTargets</code> with pairs of <code>BeginRenderPass</code> and <code>EndRenderPass</code> calls. Importantly, <code>BeginRenderPass</code> takes a bunch of flags that indicate the apps expectations about the lifetime of the render target data as well as whether previous data should be preserved. These flags allow tilers (like the one in Qualcomm&rsquo;s Adreno line) to make optimal decisions about how to handle on-chip memory, and allow them to potentially skip loading data in and out of on-chip memory which consumes considerable bandwidth.</p>
<h3 id="enhanced-barriers">Enhanced Barriers</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/D3D12EnhancedBarriers.html">Spec Link</a></p>
<p>D3D12 and Vulkan ended up diverging quite a bit in how they initially exposed barriers in their respective APIs. D3D12 opted for a &ldquo;resource state&rdquo; abstraction, where barriers would indicate &ldquo;transitions&rdquo; from one state to another which the driver could use to infer which steps are required for ensuring proper data visibility and synchronization. Vulkan opted for a more explicit approach where the thread synchronization, cache flush requirements, and texture data transformations were all represented as separate pairs of before/after access points.</p>
<p>While it was perhaps an interesting idea, D3D12&rsquo;s approach ended up being rather confusing and suboptimal in practice. Representing every visibility change as a transition gave some programmers the wrong mental model of what GPUs were actually doing under the hood, and in other cases the drivers were forced to insert excess flushes to ensure safety. By far the worst part was the concept of <a href="https://learn.microsoft.com/en-us/windows/win32/direct3d12/using-resource-barriers-to-synchronize-resource-states-in-direct3d-12">resource state &ldquo;promotion&rdquo; and &ldquo;decay&rdquo;</a>. The underlying concept is actually fairly simple if you look at it from hardware level: when you submit a batch of command lists through <code>ExecuteCommandLists</code>, the runtime enforces an implicit synchronization and cache flush after that batch finishes. This means there&rsquo;s no need to synchronize because of a dependency that spans multiple ECL batches: there was already a global sync/flush after the first ECL batch, so no need to do it again. There&rsquo;s no implicit layout changes for textures since that can&rsquo;t be done without knowing the future usage of the texture, so that still needs to be done with barriers as usual. The problem is that exposing this implicit sync through the legacy barrier system was awkward and messy: it simply doesn&rsquo;t map cleanly to an abstraction that uses &ldquo;resource states&rdquo; for everything. This is why the documentation around implicit promotion and decay is so convoluted: it has to somehow describe how certain implicit state transitions are allowed in certain situations, while excluding anything that would cause a layout change for textures.</p>
<p>Enhanced Barriers essentially takes the Vulkan approach, where you have sync, flush, and layout options as part of the barrier. This is perhaps more initially challenging to wrap your head around as a beginner compared to legacy barriers, but in the long run it ends up being much simpler IMO. The implicit sync/flushes from ECL batches is now very easy to account for with Enhanced Barriers: you can skip any barriers that would only do one of those things if the dependency crosses ECL batches, but you still have to issue layout transition barriers. There&rsquo;s also a few more benefits spelled out in the Enhanced Barrier spec, such as avoiding cases where previously the driver would require an additional flush. You can also now discard a resource as part of the barrier, which is another very welcome convenience that can save you additional commands and barriers.</p>
<h3 id="ray-tracing">Ray Tracing</h3>
<p><img src="/images/ten-years-of-d3d12/dxr.png" alt=""></p>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html">Spec Link</a></p>
<p>I imagine I hardly need to explain DXR here, as this is perhaps the most marketed and well-known addition to D3D12. In case you&rsquo;ve somehow missed all of that, DXR brings hardware ray tracing support to D3D12. It&rsquo;s exposed in two ways:</p>
<ol>
<li>The DXR 1.0 <code>DispatchRays</code> pipeline, where you get a very structured setup of providing RayGen, ClosestHit, Miss, AnyHit, or Intersection shaders as part of a big &ldquo;RTPSO&rdquo;. With this setup you trace rays from your RayGen shader (which is like a very simplified compute shader), and then based on what that ray hits (or doesn&rsquo;t hit) the GPU will automatically invoke the right shader for you.</li>
<li>The DXR 1.1 <code>RayQuery</code> pipeline, also known as &ldquo;inline tracing&rdquo;, which much more directly exposes the hardware ray tracing functionality to existing shader types. With this setup there&rsquo;s no &ldquo;RTPSO&rdquo; or &ldquo;shader tables&rdquo;, you just issue the query as part of a loop and then get the intersection info. From there it&rsquo;s completely up to you what you do in response to a hit, and there&rsquo;s no automatic calling of hit shaders or anything like that.</li>
</ol>
<p>For either approach you need to build an &ldquo;acceleration structure&rdquo; for your scene, which contains the BVH or whatever format the hardware is using to accelerate ray/triangle intersections. These AS builds happen on the GPU, and you have to initiate them by providing a list of meshes along with pointers to their position and index data. These &ldquo;bottom level acceleration structures&rdquo; (BLAS) can then be instanced into a &ldquo;top level acceleration structure&rdquo; (TLAS), which can allow you to replicate the same mesh or meshes in multiple locations without duplicating the memory. You can also move meshes around by only rebuilding the simpler TLAS rather than having to fully rebuild the BLAS. Animated/deformable meshes are much trickier though&hellip;these need to have their BLAS constantly rebuilt as the positions move from frame to frame. For these you have the option of doing a more expensive BLAS rebuild, or doing a cheaper BLAS refit that reuses the same BVH tree structure. For the latter you need to be careful though, as the quality of the tree can become worse with more refits if the vertex positions drift too far from the original positions that were used for building the BLAS. If this happens, ray traces will take longer against that mesh since more traversal steps will be needed. In such cases it can be helpful to periodically perform a full rebuild of the BLAS after N refits. In practice there&rsquo;s quite a few trade-offs that you can make regarding AS build time, trace performance, and memory consumption.</p>
<p>A future DXR 1.2 update is going to add <a href="https://github.com/microsoft/hlsl-specs/blob/main/proposals/0027-shader-execution-reordering.md">Shader Execution Reordering</a>, which allows some GPUs to re-sort and re-pack waves based on some user-provided sort key. The intention there is that you can reduce divergence for hit shading by re-packing your waves based on a material ID or similar, and that way you end up with more complete waves executing a single shader path.</p>
<p>DXR is fully supported on AMD RDNA2 or newer, Nvidia Turing or newer, and Intel Xe-HPG or newer. Nvidia does also support the more limited Tier 1 on certain Pascal GPUs, which only supports DXR 1.0-style RayGen tracing and not RayQuery/inline tracing. This hardware actually has no build-in hardware acceleration for ray tracing, so personally I wouldn&rsquo;t recommend bothering with that unless it&rsquo;s your only option.</p>
<h3 id="mesh-shaders">Mesh Shaders</h3>
<p><img src="/images/ten-years-of-d3d12/MeshShaderPipeline.png" alt=""></p>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/MeshShader.html">Spec Link</a></p>
<p><a href="https://gpuopen.com/learn/mesh_shaders/mesh_shaders-from_vertex_shader_to_mesh_shader/">Mesh Shaders</a> are another well-known addition that originally came from Nvidia&rsquo;s Turing architecture. Mesh Shaders actually have two separate parts: the Mesh Shader, and the Amplification Shader. The latter is optional, so we&rsquo;ll talk about the Mesh Shader first.</p>
<p>When you use Mesh Shaders, you no longer use the well-known Vertex Shader, Hull Shader, Domain Shader, or Geometry Shader phases for processing your vertices prior to rasterization. Instead, Mesh Shaders expose a more compute-like setup where you have a thread group that produces a batch of vertices and local indices that together form a batch of triangles. The main thing you gain is significant flexibility: there&rsquo;s no longer any fixed-function index buffers or fixed-function vertex fetch, it&rsquo;s completely up to your code to decide how to produce the batch of triangles. Since you&rsquo;re working with a thread group that can make use of groupshared memory, you can also coordinate between threads in a group to work together to produce the output data. This allows for interesting possibilities of procedurally generating geometry, calculating per-triangle data, or de-compressing vertex data.</p>
<p>The typical way that Mesh Shaders get used is for <a href="https://gdcvault.com/play/1023109/Optimizing-the-Graphics-Pipeline-With">meshlets</a>, also known as clusters. Basically you split your meshes into chunks of up to N vertices and M triangles (say 64 vertices and 128 triangles), and you can then cull those meshlets individually to get fine-grained culling. These map very well to mesh shaders, since you can have one thread group for each meshlet. Inside of a mesh shader it&rsquo;s then possible to do things like de-compressing the quantized and normalized vertex data based on the meshlet bounds. Or you can even cull at the individual triangle level if you want to, and only output the triangles and vertices that pass culling. Care is needed though when generating meshlets and writing the mesh shader. With mesh shaders you forego any automatic &ldquo;caching&rdquo; of vertex shader outputs, and it&rsquo;s up to you to <a href="https://zeux.io/2023/01/16/meshlet-size-tradeoffs/">reduce duplicate vertex loads and transformations</a> by minimizing the number of &ldquo;shared&rdquo; vertices between meshlets. Different GPUs also have different performance characteristics around vertex/triangle output counts for mesh shaders, or culling individual triangles.</p>
<p>Amplification Shaders are the other half of what you get with Mesh Shaders. As I mentioned these are optional, as you can invoke Mesh Shader groups directly if you&rsquo;d like. If you have an Amplification Shader, this is an additional shader stage that runs right before the Mesh Shader phase. Amplification Shaders are also compute-like in that they are dispatched as thread groups, but their special ability is that they can dispatch Mesh Shader groups from the shader with <code>DispatchMesh()</code>. When you do this you pass a payload along, which the Mesh Shader can then use to figure out which meshlet (or similar) needs to be process. The natural use case is to do culling and/or LOD selection right in the Amplification Shader, and then only dispatch mesh shader groups for meshlets that pass culling. This can be super convenient since you don&rsquo;t need to setup separate compute shader dispatches, output buffers, or indirect arguments: it all happens right in the graphics pipeline. On some hardware this may also provide some benefits in terms of keeping more of the data &ldquo;on-chip&rdquo;. But on the other hand, for other hardware it might be a less optimal than doing a compute + indirect dispatch approach since the latter may do a better job of completely filling large GPUs with work. In particular AMD can struggle with Amplification Shaders that cull too much (or Mesh Shaders that don&rsquo;t do much work), since they use a ring buffer through the command processor that can fill up.</p>
<h3 id="work-graphs">Work Graphs</h3>
<p><img src="/images/ten-years-of-d3d12/work_graphs.png" alt=""></p>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/WorkGraphs.html">Spec Link</a></p>
<p><a href="https://gpuopen.com/learn/gpu-work-graphs/gpu-work-graphs-part1/">Work Graphs</a> are another rather new and well-publicized addition to D3D12. They&rsquo;re also a bit complicated, but I&rsquo;ll try to give a <em>very</em> brief overview. In short, Work Graphs are a new paradigm for having the GPU feed work to itself. Rather than relying on ExecuteIndirect, you can setup a graph of compute nodes where one node can launch groups of threads for another node that it&rsquo;s connected to. So for example you might have a node that classifies a tile of pixels, and then it chooses one of N specialized nodes based on that classification. The first node can then launch a grid of threads using that selected node, effectively choosing the appropriate shader to use on-the-fly without any intermediate steps. There are definitely some limitations&hellip;in particular the topology of the graph has to be statically known when building the PSO before executing a graph. There&rsquo;s is also no recursion allowed across groups of nodes, you can only recurse into the same node. But it&rsquo;s still quite flexible and powerful compared with the traditional way of launching indirect dispatches.</p>
<p>Since Work Graphs are quite new, there&rsquo;s still multiple big question marks around them. In particular, performance. On the RDNA3 hardware that launched Work Graphs, it was definitely quite easy to create simple graphs that were handily outperformed by an ExecuteIndirect equivalent. However AMD has also provided some <a href="https://www.youtube.com/watch?v=QQP6-JF64DQ">more complex counterexamples</a> where they were able to (modestly) beat the performance of ExecuteIndirect while also using far less memory. With performance improvements not being guaranteed, it&rsquo;s harder to judge if Work Graphs will truly take off as the future way of scheduling GPU-driven compute. It&rsquo;s something you sort-of need to go all-in on if you&rsquo;re going to get the benefits, and it&rsquo;s not clear yet if the benefits justify such a drastic change.</p>
<p>Work Graphs also have preview support for mesh nodes, which are essentially just Mesh Shader thread groups. This means you can directly create triangles and rasterize them from a Work Graph, which potentially allows building a GPU-driven mesh pipeline within a single large graph. This mesh nodes feature has yet to be released at the time of writing, and is available in the 1.715.0 or newer preview Agility SDK.</p>
<p>Currently, Work Graphs are supported on RDNA3 or newer for AMD, and Ampere or newer for Nvidia. Intel has no support across their product line.</p>
<h2 id="new-shader-features">New Shader Features</h2>
<h3 id="wave-ops">Wave Ops</h3>
<p><a href="https://github.com/microsoft/DirectXShaderCompiler/wiki/Wave-Intrinsics">Doc Link</a></p>
<p>Wave operations are a powerful and difficult-to-use addition to HLSL that came along when DXC/DXIL was first released. These intrinsics allow for simple communication between threads within a &ldquo;wave&rdquo; (also known as a warp, wavefront, or subgroup), which can be much faster and simpler than communicating through groupshared or global memory. The simplest of these are the broadcast operations, which allow one thread to see the value of a variable in a particular neighboring thread. The two intrinsics that can do this are <code>WaveReadLaneFirst</code> and <code>WaveReadLaneAt</code>, the latter of which allows you to specify the index of the thread/lane from which the value will be broadcast. Another common operation is the &ldquo;ballot&rdquo; (<code>WaveActiveBallot</code>) operation, which gives you a bitmask indicating if a condition is true or false across the entire wave. This can then be used to build higher level operations to determine if a condition is true for all threads in the wave, or if it&rsquo;s true for any thread in the wave. These two operations are conveniently exposed as <code>WaveActiveAllTrue</code> and <code>WaveActiveAnyTrue</code>, and can be useful for crafting branches that never have divergence. Finally, there&rsquo;s the reduction and scan intrinsics. The reductions, such as <code>WaveActiveSum</code>, perform some mathematical operation across all threads in the wave and return the result. Whereas the <a href="https://en.wikipedia.org/wiki/Prefix_sum">scan</a> intrinsics, such as <code>WavePrefixCountBits</code>, will perform a prefix sum or prefix product across the wave and leave each thread with the cumulative result up to that lane.</p>
<p>Wave operations are part of many interesting low-level optimizations, such as <a href="https://flashypixels.wordpress.com/2018/11/10/intro-to-gpu-scalarization-part-1/">scalarization</a>. However they have to be used with considerable care to not produce undefined results. Wave sizes vary across different hardware vendors, or even across different shaders on the same hardware. It&rsquo;s very very easy to write some wave-aware code that happens to work on the one wave size you&rsquo;ve tested, but breaks badly on a different wave size. In general the HLSL language was also designed around the idea of threads being completely independent, and wave operations completely break those assumptions in similar ways to groupshared memory. This again makes it very easy to introduce undefined behavior, particularly when it comes to branching and inactive lanes.</p>
<p>AMD hardware exclusively used Wave64 up through Vega, but all RDNA+ GPUs support both Wave32 and Wave64. Nvidia exclusively supports Wave32 across its entire product line. Intel historically has been quite different with their wave sizes, and has used some combination of 8, 16, and 32-sized waves depending on the architecture.</p>
<h3 id="quad-ops">Quad Ops</h3>
<p><a href="https://github.com/microsoft/DirectXShaderCompiler/wiki/Wave-Intrinsics#quad-wide-shuffle-operations">Doc Link</a></p>
<p>Quad operations are very similar to wave operations, except that they operate on the 4 pixel shader threads that form 2x2 quads during rasterization. A lot of these quad operations could already be done in earlier versions of HLSL through clever usage of <code>ddx_fine()</code> and <code>ddy_fine()</code>, but the quad operations make things a lot simpler. In particular you can pretty easily implement a <a href="https://blog.selfshadow.com/publications/overdraw-in-overdrive/">quad overshading visualizer</a> using a few quad operations combined with the input coverage mask.</p>
<h3 id="16-bit-floats-and-integers">16-bit floats and integers</h3>
<p><a href="https://github.com/microsoft/DirectXShaderCompiler/wiki/16-Bit-Scalar-Types">Doc Link</a></p>
<p>DXC and Shader Model 6.2 (re-)introduced <a href="../shader-fp16/">lower-precision types and math operations to HLSL</a>. In particular, 16-bit float, integer, and unsigned integer types. If nothing else, these are great for packing data more tightly into buffers without having to write manual conversion and bit-packing code. But if used carefully, it&rsquo;s possible to get improved ALU throughput and/or reduced register usage for a shader. However this is pretty tricky to pull off in practice when retrofitting existing code, since it&rsquo;s very easy to accidentally introduce hidden implicit conversions between fp16 and fp32. Additionally, some hardware (AMD notably) can only achieve higher math throughput through use of &ldquo;packed&rdquo; instructions that perform the same fp16 math operation on two independent sets of inputs and outputs.</p>
<p>16-bit types are supported on AMD Vega or newer, Nvidia Turing or newer, and all DX12-capable Intel GPUs.</p>
<h3 id="barycentrics">Barycentrics</h3>
<p><a href="https://github.com/microsoft/DirectXShaderCompiler/wiki/SV_Barycentrics">Doc Link</a></p>
<p>Barycentrics are essentially coordinates on the surface of the triangle that can be used to interpolate the per-vertex attributes from the 3 triangle vertices. Most modern hardware does this interpolation with some degree of software at the beginning of the pixel shader, and thus they can expose the barycentrics used for this purpose to the shader author. This allows you to implement your own interpolation if you wish, potentially compressing and packing the attributes more compactly than they otherwise would be if using automatic interpolation. In addition, you can also do neat things like anti-aliased wireframe rendering directly in the pixel shader without requiring an additional pass (or a dreaded geometry shader!)</p>
<p>Barycentrics are supported on AMD RDNA 2 or newer, Nvidia Turing or newer, and Intel Xe2 or newer.</p>
<h3 id="shader-libraries--linking">Shader Libraries &amp; Linking</h3>
<p>The HLSL <code>lib_6_x</code> profile for compiling shader libraries was originally added as part of Shader Model 6.3 for use with ray tracing state objects. While it&rsquo;s not a well-advertised feature, they can also be used for <a href="../dxil-linking/">general offline shader linking</a> of standard graphics and compute shaders. Basically you compile your libraries first with the entry points tagged with the appropriate shader stage attribute, and then you can use DXC to link the entry point with functions defined in a library. It seems DXC then optimizes across the link boundary, so in theory it shouldn&rsquo;t result in worse DXIL codegen compared to compiling without linking. This makes it a potentially useful option for reducing compilation times.</p>
<h3 id="helper-lane-detection-and-control">Helper Lane Detection And Control</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/HLSL_ShaderModel6_6.html#is-helper-lane">Spec Link</a></p>
<p>Helper lanes are special pixel shader threads that are spawned in order to &ldquo;fill out&rdquo; missing entries from the 2x2 quads that pixel shaders are launched in. These lanes do not produce any kind of output, but still perform the usual calculations alongside its neighbors so that derivative operations work correctly when differencing across the quad. The standard way to get these is at triangle edges: all pixel shader threads within a quad have to belong to the same triangle, and so a partially-covered quad will have helper lanes spawned to fill out the quad (even if that pixel isn&rsquo;t covered by any triangle). However it can also happen dynamically during the execution of a pixel shader. In particular a <code>discard</code> will &ldquo;demote&rdquo; a pixel shader thread to a helper, so that the remaining &ldquo;alive&rdquo; threads in the wave can continue calculating derivatives and sampling textures as usual. In either case, the <code>IsHelperLane()</code> intrinisic can be used to identify these threads and perform special case logic in the presence of them.</p>
<h3 id="dynamic-resources-resourcedescriptorheap">Dynamic Resources (ResourceDescriptorHeap)</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_6_DynamicResources.html">Spec Link</a></p>
<p>The first release of D3D12 supported bindless resources, but you had to do so with some somewhat clunky usage of unbounded resource arrays in HLSL combined with some root signature boilerplate. On top of that you also had to make sure you bound your &ldquo;one big descriptor table&rdquo; to the root signature on each command list every time you changed the root signature. Shader model 6.6 and dynamic resources makes all of that go away. Instead you just get a magic <code>ResourceDescriptorHeap</code> array that you can access from anywhere, and it gives you the resource that lives at the provided index of the bound descriptor heap. With this you can drop nearly everything from your root signature if you want, and instead just provide a few root constants or root CBVs/SRVs that the shader can use to grab whatever descriptor indices it needs. While not technically required for bindless, it makes things much much simpler and nicer. This is particularly true for <code>StructuredBuffer</code>, which in the old way required you to declare a separate unbounded resource array for every possible struct type that you used as an element.</p>
<p>To go along with <code>ResourceDescriptorHeap</code>, there&rsquo;s also <code>SamplerDescriptorHeap</code> which works exactly the same except for the sampler descriptor heap. This makes bindless samplers just as simple if that&rsquo;s what you need.</p>
<h3 id="wavesize">WaveSize</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_6_WaveSize.html">Spec Link</a></p>
<p>As I mentioned earlier, wave ops are really tough to use effectively in a way that works across all possible wave sizes. This is especially true since the wave size is not known at compile time, so any logic for separate wave sizes would have to expressed as conditionals on <code>WaveGetLaneCount()</code>. The <code>[WaveSize(N)]</code> intrinsic from Shader Model 6.6 aims to help here by allowing compute shader to specify that can only work with the specified wave size. This is enforced at runtime, where PSO creation will fail if the wave size is not supported by the current device and driver. While this helps, the situation is still tricky since you will want to compile the same shader N times for each possible wave size you want to support. You also likely want to have some sort of fall back that doesn&rsquo;t use wave operations if you don&rsquo;t support every possible wave size that D3D allows.</p>
<h3 id="derivatives-in-compute">Derivatives In Compute</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_6_Derivatives.html">Spec Link</a></p>
<p>Traditionally quads were something that only existed in pixel shaders, which meant that by extension pixel shaders were the only stage that could use derivative intrinsics or texture sampling functions with implicit LOD mip level selection. This Shader Model 6.6 feature brings quads to compute shaders as well, by specifying some rules about how certain threads within the thread group end up getting mapped to quad indices. If you carefully respect these rules when writing your shader, you can then use the regular <code>Texture2D.Sample</code> function to get mip selection without having to pass gradients or calculate the mip level yourself. Note that this spec still does not fully specify how threads get mapped to waves within a thread group, so care must be taken not to make assumptions about waves are arranged in compute.</p>
<h3 id="64-bit-atomics-and-atomic-cas-for-floats">64-Bit Atomics and Atomic CAS For Floats</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_6_Int64_and_Float_Atomics.html">Spec Link</a></p>
<p>Prior to this feature, atomic operations could only be performed on 32-bit integers. Shader Model 6.6 changes this, and now allows you to perform atomics on 64-bit integers in buffers and textures. This is surely handy if you&rsquo;re going to the nanite route of software rasterizing into a visibility buffer. In addition, SM 6.6 adds very limited support for atomics on fp32 values. In particular, it introduces <code>InterlockedCompareStoreFloatBitwise</code> and <code>InterlockedCompareExchangeFloatBitwise</code>. These essentially treat the float bits as a uint32_t, and perform a regular atomic CAS.</p>
<h3 id="raw-texture-gather">Raw Texture Gather</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_7_Advanced_Texture_Ops.html#raw-gather">Spec Link</a></p>
<p>Raw Texture Gather is an interesting Shader Model 6.7 feature that allows you to perform a 4-texel gather on certain integer texture formats. If you have a R16_UINT, R32_UINT or R32G32_UINT format texture, you can gather a 2x2 quad of texels and have them returned to the shader as raw unfiltered bytes. This bypasses previous restrictions on using Gather with an integer texture format.</p>
<h3 id="writable-msaa-textures">Writable MSAA Textures</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_7_Advanced_Texture_Ops.html#writable-msaa-textures">Spec Link</a></p>
<p>As the same suggests, this Shader Model 6.7 features allows shaders to directly write to MSAA textures through the new <code>RWTexture2DMS</code> and <code>RWTexture2DMSArray</code> HLSL resource types. Previously you could only write to them through a render target view, or through copy/resolve operations.</p>
<h3 id="samplecmplevel">SampleCmpLevel</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_7_Advanced_Texture_Ops.html#samplecmplevel">Spec Link</a></p>
<p><code>SampleCmpLevel</code> is like <code>SampleCmpLevelZero</code>, except that it lets you pass a specific mip level instead of only being able to sample the 0th mip level.</p>
<h3 id="byteaddressbuffer-types-load-and-store">ByteAddressBuffer Types Load and Store</h3>
<p><a href="https://github.com/microsoft/DirectXShaderCompiler/wiki/Buffer-Packing#byteaddressbuffer-templated-loadstore-operations">Doc Link</a></p>
<p>This is not a shader model feature, but just something that DXC now supports at the language level. Instead of only being able to load a <code>uint/2/3/4</code> from a <code>ByteAddressBuffer</code>, there&rsquo;s now a templated <code>Load&lt;T&gt;</code> method that allows you to load any arbitrary type or struct at an offset (with 4-byte alignment, or 2-byte alignment if 16-bit types are supported). There&rsquo;s also a matching <code>Store&lt;T&gt;</code> function on <code>RWByteAddressBuffer</code> that works the same way, except for storing. To accompany these new methods, DXC also added support for <code>sizeof()</code>. This allows you to load from arrays of arbitrary types, similar to <code>StructuredBuffer</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">MyStruct</span> <span class="n">s</span> <span class="o">=</span> <span class="n">byteAddressBuffer</span><span class="p">.</span><span class="n">Load</span><span class="o">&lt;</span><span class="n">MyStruct</span><span class="o">&gt;</span><span class="p">(</span><span class="n">index</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">MyStruct</span><span class="p">));</span>
</span></span></code></pre></div><h3 id="sv_startvertexlocation--sv_startinstancelocation">SV_StartVertexLocation + SV_StartInstanceLocation</h3>
<p><a href="https://microsoft.github.io/hlsl-specs/proposals/0015-extended-command-info.html">Spec Link</a></p>
<p>Many people are caught off guard when they find out that the <code>BaseVertexLocation</code> and <code>StartInstanceLocation</code> parameters of <code>DrawIndexedInstanced</code> do not affect the <code>SV_VertexID</code> and <code>SV_InstanceID</code> system value inputs for the vertex shader. Instead, they only affect fixed-function vertex fetch that occurs in the input assembler. This is especially confusing since GL and Vulkan have not worked this way, and have mechanisms to provide the vertex and instance offsets to the vertex shader. This new feature closes the gap by providing new <code>SV_StartVertexLocation</code> and <code>SV_StartInstanceLocation</code> system value inputs that contain the value of the <code>BaseVertexLocation</code> and <code>StartInstanceLocation</code> parameters. This can be particularly useful with GPU-driven rendering pipelines that make use of ExecuteIndirect, since it provides additional way to pass data along to the resulting draw.</p>
<h3 id="enums">Enums</h3>
<p>Weirdly, HLSL never supported C-style enums until DXC added support for HLSL 2017. Now you can do the usual enum and enum class declarations, just like you would in C++.</p>
<h3 id="templates">Templates</h3>
<p><a href="https://devblogs.microsoft.com/directx/announcing-hlsl-2021/#template-functions-and-data-types">Announcement Link</a></p>
<p>HLSL 2021 added limited support for template types and functions to HLSL. This is obviously a major change with huge implications on how the language is authored, both good and bad. However it is unquestionably powerful and flexible, allowing pure templated HLSL code to be used in many places where either macros or code duplication had to be used previously. For example you could make <a href="https://blog.traverseresearch.nl/bindless-rendering-templates-8920ca41326f">wrappers around bindless resource descriptors</a>, or go nuts and make <a href="https://github.com/TheRealMJP/SHforHLSL">templated spherical harmonics math libraries</a>. Just be sure to watch out for compile times and inscrutable compilation errors.</p>
<h3 id="operator-overloading">Operator Overloading</h3>
<p><a href="https://devblogs.microsoft.com/directx/announcing-hlsl-2021/#member-operator-overloading">Announcement Link</a></p>
<p>Along with templates, HLSL 2021 allows for overloading a limited subset of operators for user-defined types. The usual arithmetic, bitwise, comparison, function, and array subscript operators all supported. The main exception are pointer-related operators like <code>*</code> and <code>&amp;</code>, and operators that require returning a reference to this like <code>+=</code>.</p>
<h3 id="bitfields">Bitfields</h3>
<p><a href="https://devblogs.microsoft.com/directx/announcing-hlsl-2021/#bitfield-members-in-data-types">Announcement Link</a></p>
<p>Bitfields are often useful for tightly packing lots of data into a limited number of bits, which is something we tend to do a lot on GPUs. With bitfield support, you no longer thave to write manual masking and bit shifting to stuff values into smaller than 16 bits.</p>
<h3 id="c-style-function-overloading-and-for-loop-scoping">C++-Style Function Overloading and For Loop Scoping</h3>
<p><a href="https://devblogs.microsoft.com/directx/announcing-hlsl-2021/">Announcement Link</a></p>
<p>One of the weird quirks of HLSL was that it had wacky overload resolution. Rather than overloading purely based on the number and type of the arguments like C++, it would essentially &ldquo;destructure&rdquo; custom types into their primitive components and overload based on that. This could be very surprising, to say the least. HLSL 2021 fixes this and uses the usual overload rules that you&rsquo;re probably used to.</p>
<p>Another odd quirk that was resolved in HLSL 2021 was for loop scoping for anything declared in the <code>for</code> block. Pre-2021, anything inside of the <code>for</code> lived in the parent scope of the loop. This meant that if you had <code>for (uint i = 0; i &lt; length; ++i) { }</code> the <code>i</code> variable would persist after the loop was finished. With HLSL 2021, <code>i</code> would only exist in the loop body instead which is how C++ and C99 handles it.</p>
<h2 id="developer-workflow-enhancements">Developer Workflow Enhancements</h2>
<h3 id="agility-sdk--independent-devices">Agility SDK + Independent Devices</h3>
<p>As you&rsquo;ve no doubt surmised from reading this far, D3D12 has changed an awful lot since it was first released. However in the early days this changes could be rather slow to roll out, since they had to be tied to major Windows 10 updates. That&rsquo;s not great for an API that&rsquo;s constantly evolving, and it&rsquo;s <em>really</em> not great for developers who need to ensure a minimum available feature set across their potential user base. The <a href="https://devblogs.microsoft.com/directx/gettingstarted-dx12agility/">Agility SDK</a> aimed to fix both of these issues by decoupling major DX12 releases and additions from the OS release cycle, and instead bundling them into a D3D12Core.dll that could be shipped to end users. The way it works is a bit interesting: rather than an app only using the D3D12Core.dll that it shipped with, the D3D loader will also look at the installed OS version of D3D12Core.dll and use whichever is newer. That way apps can still get bug fixes and updates, but still ensure they have a floor for available features.</p>
<p>The developer story for the Agility SDK was originally a bit clunky. It was easy enough to get a D3D12Core.dll, the debug layer dll, and the corresponding d3d12.h header. You had to be careful with your include paths to make sure you&rsquo;re including the newer d3d12.h rather than the one from the Windows SDK, but that&rsquo;s not too hard to do. However the really annoying part was setting up the &ldquo;magic&rdquo; exports on your exe that let the D3D loader know that you want to use an Agility SDK. These required exporting a string containing a relative path to where D3D12Core.dll could be found, as well as an integer representing the intended Agility SDK version. This is a bit annoying for packaging as well as development builds&hellip;for various reasons you didn&rsquo;t want D3D12Core.dll to live in the same folder as your executable, and so you had to place it in a known subfolder relative to the executable. However the real dealbreaker was for app plugins that wanted to use a D3D12 device. Since the &ldquo;magic&rdquo; exports have to be on the executable, there&rsquo;s no way for a plugin to specify the Agility SDK that it wants. So it&rsquo;s stuck with whatever the base app specifies&hellip;which is likely no Agility SDK at all. The <a href="https://microsoft.github.io/DirectX-Specs/d3d/IndependentDevices.html">Independent Devices</a> feature fixes both of these problems by providing an API that can specify the Agility SDK details. Doing this required D3D12 to change its &ldquo;one device per process&rdquo; rule that it used previously, which is why the feature is called &ldquo;Independent Devices&rdquo;.</p>
<h3 id="debug-layer-callback">Debug Layer Callback</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/MessageCallback.html">Spec Link</a></p>
<p>The debug layer is obviously super valuable and useful for catching programmer mistakes and undefined behavior before it happens. It also has a handy feature where you can instruct the layer to break into the debugger when an error occurs, which makes it more useful for catching the line of code that actually generated the error. However the error message itself was always output to the debugger output stream, which is displayed in Visual Studio but is otherwise very hard to interpret yourself. This could be especially frustrating if you come from Vulkan, where the validation layer allows you to install a callback that gets passed the details of the error right when it&rsquo;s generated. D3D12 finally caught up with the release of Windows 11, which adds a new API that allows you to install a callback that gets called whenever the debug layer generates a message. You can then assert, print, log, etc. however you wish.</p>
<h3 id="device-removed-extended-data-dred">Device Removed Extended Data (DRED)</h3>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/DeviceRemovedExtendedData.html">Spec Link</a></p>
<p>Overall the story for debugging and resolving GPU crashes is pretty terrible on PC. When it happens, you&rsquo;re often on the other side of multiple abstractions and black boxes which makes it very very difficult to find the root cause. DRED aims to help a bit with this near-impossible task by providing some information about what the GPU was doing (or rather has already done) when it crashed on you. Basically it uses <code>WriteBufferImmediate</code> to automatically generate &ldquo;breadcrumbs&rdquo; for various commands recorded on the command list, such as PIX markers or draw calls. Then when the device is removed, you can use the DRED APIs to dump out the state of each command buffer with an indicator of which breadcrumbs were reached by the GPU. With any luck, this can point you to the particular pass or dispatch that caused the crash. In the case of a page fault DRED can also provide a list of resource debug names that correspond to the page fault address&hellip;although in practice I&rsquo;ve found that this information is often missing for unknown reasons.</p>
<h2 id="whats-changed-for-me-personally">What&rsquo;s Changed For Me Personally</h2>
<h3 id="all-in-on-bindless-and-user-space-bindings">All-In On Bindless and &ldquo;User-Space Bindings&rdquo;</h3>
<p>Bindless might seem like a fun party trick at first, or something you only use in situations where you truly need to the shader to decide which resources it wants to use. But once you go all-in, it can have a transformative effect on your codebase and the way you work. Even in cases where you know up-front exactly which textures and buffers a shader will need, in my experience it&rsquo;s an unquestionably nicer experience to do things through bindless rather than using old slot-based APIs or clunkily assembling descriptor tables. This is <em>especially</em> true with SM 6.6 type bindless with ResourceDescriptorHeap. Essentially every resource descriptor you can access just turns into a simple <code>uint32_t</code> that you can put wherever you want. I like to call it &ldquo;user-space bindings&rdquo; since that <code>uint32_t</code> is no longer opaque, and doesn&rsquo;t need to live in a descriptor heap or a descriptor set or some other place where you don&rsquo;t actually own the memory. You can put in structs, arrays, textures, anything you want.</p>
<p>On the host side of things, the potential for simplification can be enormous. Root signatures in particular can be sidelined almost completely, it&rsquo;s completely viable to have just a handful or even one global root signature that gets used for everything. All you need to care about in your root signature is how you pass your initial &ldquo;root&rdquo; data to a shader, which might be some root constants, root CBVs, or root SRVs. Once you have that, all of your other resources can be accessed by pulling in descriptor indices. With a non-bindless setup you might find yourself doing things like reflecting shaders to generate an &ldquo;optimal&rdquo; root signature, or creating many root signatures to handle many different cases of ways that you would organize various resources into descriptor tables that are bucketed by the frequency by which the table bindings change. With bindless, that&rsquo;s all gone! Root signatures can get stuffed into some corner of your codebase that deals with booting up the D3D12 device, and you may never have to think about it again. My &ldquo;universal&rdquo; root signature that I use in my home codebase looks like this, and I create it during startup:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">D3D12_ROOT_PARAMETER1</span> <span class="n">rootParameters</span><span class="p">[</span><span class="n">NumUniversalRootSignatureParams</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Constant buffers
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span><span class="p">(</span><span class="n">uint32</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">NumUniversalRootSignatureConstantBuffers</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">rootParameters</span><span class="p">[</span><span class="n">URS_ConstantBuffers</span> <span class="o">+</span> <span class="n">i</span><span class="p">].</span><span class="n">ParameterType</span> <span class="o">=</span> <span class="n">D3D12_ROOT_PARAMETER_TYPE_CBV</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">rootParameters</span><span class="p">[</span><span class="n">URS_ConstantBuffers</span> <span class="o">+</span> <span class="n">i</span><span class="p">].</span><span class="n">ShaderVisibility</span> <span class="o">=</span> <span class="n">D3D12_SHADER_VISIBILITY_ALL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">rootParameters</span><span class="p">[</span><span class="n">URS_ConstantBuffers</span> <span class="o">+</span> <span class="n">i</span><span class="p">].</span><span class="n">Descriptor</span><span class="p">.</span><span class="n">RegisterSpace</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">rootParameters</span><span class="p">[</span><span class="n">URS_ConstantBuffers</span> <span class="o">+</span> <span class="n">i</span><span class="p">].</span><span class="n">Descriptor</span><span class="p">.</span><span class="n">ShaderRegister</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">rootParameters</span><span class="p">[</span><span class="n">URS_ConstantBuffers</span> <span class="o">+</span> <span class="n">i</span><span class="p">].</span><span class="n">Descriptor</span><span class="p">.</span><span class="n">Flags</span> <span class="o">=</span> <span class="n">D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// AppSettings
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">rootParameters</span><span class="p">[</span><span class="n">URS_AppSettings</span><span class="p">].</span><span class="n">ParameterType</span> <span class="o">=</span> <span class="n">D3D12_ROOT_PARAMETER_TYPE_CBV</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">rootParameters</span><span class="p">[</span><span class="n">URS_AppSettings</span><span class="p">].</span><span class="n">ShaderVisibility</span> <span class="o">=</span> <span class="n">D3D12_SHADER_VISIBILITY_ALL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">rootParameters</span><span class="p">[</span><span class="n">URS_AppSettings</span><span class="p">].</span><span class="n">Descriptor</span><span class="p">.</span><span class="n">RegisterSpace</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">rootParameters</span><span class="p">[</span><span class="n">URS_AppSettings</span><span class="p">].</span><span class="n">Descriptor</span><span class="p">.</span><span class="n">ShaderRegister</span> <span class="o">=</span> <span class="n">AppSettings</span><span class="o">::</span><span class="n">CBufferRegister</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">rootParameters</span><span class="p">[</span><span class="n">URS_AppSettings</span><span class="p">].</span><span class="n">Descriptor</span><span class="p">.</span><span class="n">Flags</span> <span class="o">=</span> <span class="n">D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Static samplers
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">D3D12_STATIC_SAMPLER_DESC</span> <span class="n">staticSamplers</span><span class="p">[</span><span class="n">uint64</span><span class="p">(</span><span class="n">SamplerState</span><span class="o">::</span><span class="n">NumValues</span><span class="p">)]</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span><span class="p">(</span><span class="n">uint32</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">uint32</span><span class="p">(</span><span class="n">SamplerState</span><span class="o">::</span><span class="n">NumValues</span><span class="p">);</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">staticSamplers</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">GetStaticSamplerState</span><span class="p">(</span><span class="n">SamplerState</span><span class="p">(</span><span class="n">i</span><span class="p">),</span> <span class="n">i</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">D3D12_SHADER_VISIBILITY_ALL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">D3D12_ROOT_SIGNATURE_DESC1</span> <span class="n">rootSignatureDesc</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="n">rootSignatureDesc</span><span class="p">.</span><span class="n">NumParameters</span> <span class="o">=</span> <span class="n">ArraySize_</span><span class="p">(</span><span class="n">rootParameters</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">rootSignatureDesc</span><span class="p">.</span><span class="n">pParameters</span> <span class="o">=</span> <span class="n">rootParameters</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">rootSignatureDesc</span><span class="p">.</span><span class="n">NumStaticSamplers</span> <span class="o">=</span> <span class="n">ArraySize_</span><span class="p">(</span><span class="n">staticSamplers</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">rootSignatureDesc</span><span class="p">.</span><span class="n">pStaticSamplers</span> <span class="o">=</span> <span class="n">staticSamplers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">rootSignatureDesc</span><span class="p">.</span><span class="n">Flags</span> <span class="o">=</span> <span class="n">D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">DX12</span><span class="o">::</span><span class="n">CreateRootSignature</span><span class="p">(</span><span class="o">&amp;</span><span class="n">UniversalRootSignature</span><span class="p">,</span> <span class="n">rootSignatureDesc</span><span class="p">);</span>
</span></span></code></pre></div><p>A &ldquo;bindful&rdquo; engine might have code that carefully manages the lifetime of descriptor tables, maybe even using hashes or similar to determine whether a table can be re-used or a new table has to be built. And perhaps also you have hooks for your texture streaming or CPU-updated buffers to know when to invalidate your descriptor sets, since the SRV descriptor had to change. Once again, all gone! Poof! You do still need to have a double-buffered descriptor heap and make sure you don&rsquo;t update descriptors that are still being accessed by the GPU, but that&rsquo;s straightforward and also something you can likely manage in one centralized place by having a queue of updates. In my home codebase, binding some resources and constants looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">MeshConstants</span> <span class="n">meshConstants</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">World</span> <span class="o">=</span> <span class="n">world</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">View</span> <span class="o">=</span> <span class="n">camera</span><span class="p">.</span><span class="n">ViewMatrix</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">WorldViewProjection</span> <span class="o">=</span> <span class="n">world</span> <span class="o">*</span> <span class="n">camera</span><span class="p">.</span><span class="n">ViewProjectionMatrix</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">CameraPos</span> <span class="o">=</span> <span class="n">camera</span><span class="p">.</span><span class="n">Position</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Time</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">ElapsedTime</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">VertexBuffer</span> <span class="o">=</span> <span class="n">model</span><span class="o">-&gt;</span><span class="n">VertexBuffer</span><span class="p">().</span><span class="n">SRV</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">MeshletBuffer</span> <span class="o">=</span> <span class="n">model</span><span class="o">-&gt;</span><span class="n">MeshletBuffer</span><span class="p">().</span><span class="n">SRV</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">MeshletBoundsBuffer</span> <span class="o">=</span> <span class="n">model</span><span class="o">-&gt;</span><span class="n">MeshletBoundsBuffer</span><span class="p">().</span><span class="n">SRV</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">MeshletVerticesBuffer</span> <span class="o">=</span> <span class="n">model</span><span class="o">-&gt;</span><span class="n">MeshletVerticesBuffer</span><span class="p">().</span><span class="n">SRV</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">MeshletTrianglesBuffer</span> <span class="o">=</span> <span class="n">model</span><span class="o">-&gt;</span><span class="n">MeshletTrianglesBuffer</span><span class="p">().</span><span class="n">SRV</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">camera</span><span class="p">.</span><span class="n">GetFrustumPlanes</span><span class="p">(</span><span class="n">meshConstants</span><span class="p">.</span><span class="n">FrustumPlanes</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">DX12</span><span class="o">::</span><span class="n">BindTempConstantBuffer</span><span class="p">(</span><span class="n">cmdList</span><span class="p">,</span> <span class="n">meshConstants</span><span class="p">,</span> <span class="n">URS_ConstantBuffers</span> <span class="o">+</span> <span class="mi">0</span><span class="p">,</span> <span class="n">CmdListMode</span><span class="o">::</span><span class="n">Graphics</span><span class="p">);</span>
</span></span></code></pre></div><p>It&rsquo;s dead simple: I init a struct with a bunch of constants as well as SRV descriptor indices, which can live together side-by-side instead of having to be kept separate. Then in <code>BindTempConstantBuffer</code> I grab some temporary frame-lifetime CPU-writable buffer memory, copy in the struct contents, and bind it to the root signature as a root CBV in one go. I could also trivially have nested structures with more descriptor indices if I want, or have this live in a persistent buffer instead of creating one on-the-fly. Either way it&rsquo;s all something I can control and handle however I want, which is incredibly freeing.</p>
<p>On the shader side it&rsquo;s perhaps less-transformative, but bindless is still cool. One aspect that can cause some friction at first is that you don&rsquo;t just have the resources available as globals in HLSL. Instead you explicitly grab the resources using <code>ResourceDescriptorHeap</code> and then do what you want. In the long term though I think this is a very positive change vs. requiring resources to be globals, and can make the code a whole lot cleaner and composable once you lean into it. If you really want you can also load your HLSL resources into static global variables to mimic the old style, but I don&rsquo;t particularly recommend that unless you&rsquo;re adapting a large existing codebase. And of course once you have bindless, you can also do very fancy things in your shaders like traversing your own data structures or sampling arbitrary material textures based on a material ID.</p>
<p>For ray tracing in particular bindless is a lifesaver, and a massive simplification over juggling local root signatures and shader binding tables. In fact I wrote a whole chapter about that in <a href="https://www.realtimerendering.com/raytracinggems/rtg2/">Ray Tracing Gems II</a>!</p>
<p>The main downside with &ldquo;bindless everywhere&rdquo; tends to be in tooling and introspection. When bindings are known statically at draw/dispatch time, it&rsquo;s trivial for tools like RenderDoc and PIX to intercept those bindings and display them for you later when you&rsquo;re viewing a capture. On a similar note, validation layers also have a much easier time with old-style bindings since they need to know which resources a particular operation accessed. With bindless there&rsquo;s no way to know which resources actually got accessed without running the shader, which is basically what these tools now have to do if you want accurate information. They will patch your shader bytecode to insert new instructions that can store out introspection data to a buffer, and then gather that data after running your draw or dispatch to know which resources got accessed. This does actually work, but adds significant complexity on the tooling side that didn&rsquo;t exist previously. On a similar note shader reflection APIs can also be rendered nearly useless for determining which resources a shader uses, so you may need a new approach if you&rsquo;re relying on that information.</p>
<h3 id="hlsl-templates">HLSL Templates</h3>
<p>Templates always inspire mixed feelings in me. They are undeniably useful and powerful, but come with an endless list of ways they can blow up in your face. Whether it&rsquo;s bloated compile times, indecipherable error messages, or insane SFINAE gymnastics that cause programmers to consider taking up farming: the potential downsides are numerous and well-documented. But again, undeniably useful. Having carefully introduced templates into my own shader code, I am of the opinion that adding them was the right call and we&rsquo;re better off having them than not. But I try to be at least as cautious with them as I am with my C++ code, in that I generally reach for simpler lower-tech solutions whenever I can even if it adds a bit of copy/pasted code.</p>
<p>So what have I used them for? The list is pretty long at this point, but here&rsquo;s some highlights:</p>
<ul>
<li>They&rsquo;re immediately useful for functions that you want to be able to use with both fp32 and fp16 data types. No need to duplicate the function, just write it once. You can also make functions vector-size agnostic, but that&rsquo;s a little clunkier. For an example of both of these things you can have a look at my <a href="https://github.com/TheRealMJP/SHforHLSL/blob/main/SH.hlsli">SHforHLSL library</a> that implements types and functions for working with low-order spherical harmonics. In fact it&rsquo;s probably also a good case for trying to figure out what&rsquo;s &ldquo;going too far&rdquo; with templates. In particular I think templating on the monochrome vs RGB variants hurts the readability of the code in some places. For a library where you don&rsquo;t really touch the code this might be fine, but in more actively-developed code I would probably be annoyed with it. If you compare against the <a href="https://github.com/TheRealMJP/SHforHLSL/blob/main/SH_Lite.hlsli">non-templated Lite version</a>, the non-templated code can end up looking a good bit cleaner.</li>
<li>Similarly, templating on the texture type can be nice for implementing things like filtering functions where the code could easily handle float/float2/float3/float4 types.</li>
<li>For some code that I&rsquo;ve worked on, I ended up making an algorithm that used a templated &ldquo;writer&rdquo; type. This let me use the same code and have it write its outputs to a buffer, or alternatively have it log its results using a shader printf.</li>
<li>Speaking of shader printf&hellip;my <a href="https://github.com/TheRealMJP/EarlyZTest/blob/main/SampleFramework12/v1.04/Shaders/ShaderDebug.hlsl">own implementation</a> uses templates for handling the format string as well as packing the arguments into the debug buffer.</li>
<li>I never actually used this, but I did sketch out a <a href="https://godbolt.org/z/rdn698nMT">proof-of-concept</a> for a buffer wrapper that could emulate Buffer Device Address functionality that you have in Vulkan. It&rsquo;s pretty clunky compared to actual language support for such a thing, but it&rsquo;s nice that you can do it.</li>
</ul>
<p>Just as with C++ I think templates are potentially a liability, and it&rsquo;s very easy to over-use them. But at the same time I still think they have be a large net positive if used judiciously. There&rsquo;s definitely room for very reasonable arguments that HLSL should have chosen a different direction here instead of following C++&hellip;<a href="https://shader-slang.org/docs/understanding-generics/">Slang&rsquo;s generics</a> are probably the most usable counter-example at the moment. Personally I&rsquo;m generally a pragmatist and think there&rsquo;s a large value in using existing paradigms that are well-understood, and I&rsquo;m not sure if Slang&rsquo;s divergence is a win in the long run. But time will tell, and I&rsquo;ll definitely pay attention to what happens in that space!</p>
<h3 id="more-code-sharing-between-hlsl-and-c">More Code Sharing Between HLSL and C++</h3>
<p>A <a href="https://enginearchitecture.org/2025.htm#4">lot</a> <a href="https://enginearchitecture.org/2025.htm#1">of</a> <a href="https://advances.realtimerendering.com/s2024/index.html#serac">engines</a> invest considerable amounts of engineering and code into trying to automatically manage the boundaries between C++ and shader code. These tend to be some combination of shader reflection, code generation, and/or runtime layers that make it possible for engine code to provide the data and bindings that the shader needs. This can be a rather useful investment: it allows engines to hide specific details of how bindings work, or even transparently account for <a href="https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules">wacky HLSL constant buffer alignment rules</a>. But this can be a double-edged sword: it can result in esoteric peculiarities specific to an engine, and can require considerable maintenance to keep such layers working through new features and/or APIs.</p>
<p>Lately I find myself drawn to the much simpler option of simply sharing structs, functions, and other code between my HLSL and C++ where it makes sense. This is helped along a bit by the latest HLSL language features, where it&rsquo;s now fine to use enums, templates, and methods as much as you&rsquo;d like. But in particular, using bindless for everything makes it even more viable. Like I mentioned earlier, with bindless you can effectively represent any resource descriptor as 32-bit integer or handle that you store freely in structs. This means you can trivially store all of a shader&rsquo;s various constants and resources in one more structs that you share between C++ and HLSL, and both C++ and HLSL will also stay up-to-date without the need for codegen or reflection. One major wrinkle here is constant buffers, which unfortunately after 10 years of D3D12 <em>still</em> have the annoying legacy packing rules for vectors and arrays. For such cases you would have to manually account for alignment issues, or ignore constant buffers altogether. Fortunately the latter approach is increasingly viable on modern hardware.</p>
<p>In reality this &ldquo;just share&rdquo; approach is probably going to be more viable for simpler scenarios. Larger engines will likely have more complex binding mechanisms to deal with, and may have to deal with weird platforms where sharing is less viable. So it&rsquo;s certainly not a panacea, but I do think it&rsquo;s increasingly becoming a worthwhile option.</p>
<h3 id="designated-initializers">Designated Initializers</h3>
<p>This is not strictly related to graphics or APIs, but I do rather like designated initializers in C++20. They&rsquo;re nicer than having to set struct members one at a time, and they have less compiler + non-optimized runtime overhead than &ldquo;builder&rdquo; patterns that use method chaining. I generally like to pair them with structs that have default values specified through member initializers, and then the designated initializers can &ldquo;override&rdquo; the default value. With this pattern you can even emulate named arguments, which can be much easier to read than a soup of parameters. For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">RawBufferInit</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">NumElements</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">bool32</span> <span class="n">CreateUAV</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">bool32</span> <span class="n">Dynamic</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">bool32</span> <span class="n">CPUAccessible</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">void</span><span class="o">*</span> <span class="n">InitData</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ID3D12Heap</span><span class="o">*</span> <span class="n">Heap</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">HeapOffset</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">Name</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">spotLightClusterBuffer</span><span class="p">.</span><span class="n">Initialize</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">NumElements</span> <span class="o">=</span> <span class="n">numXYZTiles</span> <span class="o">*</span> <span class="n">AppSettings</span><span class="o">::</span><span class="n">SpotLightElementsPerCluster</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">CreateUAV</span> <span class="o">=</span> <span class="nb">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Name</span> <span class="o">=</span> <span class="s">&#34;Spot Light Cluster Buffer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>In my experience this is great for resource initialization, and also for barriers. I like to have helpers that set you up for common barrier situations, and then use designated initializers to override the specifics:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">DepthWritableBarrierDesc</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">FirstAccess</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">Discard</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3D12_BARRIER_SYNC</span> <span class="n">SyncBefore</span> <span class="o">=</span> <span class="n">D3D12_BARRIER_SYNC_ALL_SHADING</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3D12_BARRIER_ACCESS</span> <span class="n">AccessBefore</span> <span class="o">=</span> <span class="n">D3D12_BARRIER_ACCESS_SHADER_RESOURCE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3D12_BARRIER_LAYOUT</span> <span class="n">LayoutBefore</span> <span class="o">=</span> <span class="n">D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">StartArraySlice</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">NumArraySlices</span> <span class="o">=</span> <span class="kt">uint32_t</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">DX12</span><span class="o">::</span><span class="n">Barrier</span><span class="p">(</span><span class="n">cmdList</span><span class="p">,</span> <span class="n">depthBuffer</span><span class="p">.</span><span class="n">DepthWritableBarrier</span><span class="p">({</span> <span class="p">.</span><span class="n">FirstAccess</span> <span class="o">=</span> <span class="nb">true</span> <span class="p">}));</span>
</span></span></code></pre></div><p>This is where I now get on my soapbox to rant about the specific implementation of designated initializers in C++, which requires that the order of your initializer match the order in which the members are declared. It&rsquo;s setup this way to avoid nasty footguns where the initialization/construction of one member depends on another member&hellip;but it&rsquo;s deeply unfortunate that this particular edge case hurts the usability of the feature for the common 99% of cases where you don&rsquo;t really care about the init order. Clang does at least have a way to demote this into a warning, but MSVC does not as far as I&rsquo;m aware.</p>
<h3 id="printf-and-other-shader-debug-features">Printf and Other Shader Debug Features</h3>
<p>Once again this is not something that requires the latest D3D and HLSL toys to pull off, but if you are on the latest and greatest it becomes quite a bit easier to do. In particular:</p>
<ul>
<li>SM 6.6-style bindless makes it possible to make writable debug buffers globally available to any shader without any modifications for app code. This means if you have shader hot reloading, you can just add some debug printing code to your shader without restarting or recompiling the app. <a href="https://github.com/TheRealMJP/DXRPathTracer/blob/4536ef8b5c7c5fa03ea7b3109601a00452fd0784/SampleFramework12/v1.05/Shaders/ShaderDebug.hlsli#L17">In my own code</a> I achieve this by reserving a particular known index in the descriptor heap for the debug buffer, and hard-code that index into the .hlsli file that shaders include for debug functionality.</li>
<li>HLSL templates and RWByteAddressBuffer typed stores make it much more convenient to stuff heterogenous data into your debug buffer (useful for things like printf arguments)</li>
<li>While not an official feature, it&rsquo;s <a href="../hlsl-printf/">possible</a> to (ab-)use DXC&rsquo;s template functionality to work with actual string literals for printf-like functionality, which is much simpler and more natural than the alternatives.</li>
</ul>
<p>Having a reliable working shader printf can be a game changer, and can almost make you forget how awful the debugging situation is on PC. What I like to do is have some utility functions that allow you to filter the debug functionality down to a particular thread, potentially by filtering output to the thread/pixel under the mouse cursor. Then I will use helper macros that print out the state of local variables, which serves as a sort-of live debugger for a thread. Once you have this setup you can make changes to shader, and continue inspecting the values of the intermediate variables to see the end result of your changes.</p>
<p>Another useful trick that I now get a lot of mileage out of is <a href="https://bsky.app/profile/mjp123.bsky.social/post/3lxq7hphmg22a">drawing debug wireframe shapes directly from a shader</a>. The process for this starts out rather similar to shader printf: you have some globally-accessible debug buffer, and your shader functions dump the shape transform + properties into that buffer. Then towards the end of the frame, you perform an indirect draw for each shape to consume the data from the debug buffer and draw the shape to the screen. It takes a bit of plumbing to get it all setup, but once it works it can be incredibly useful for debugging. It can also be a rather convenient way of implementing dev-only debug visualizations that you otherwise would have had to do on the CPU, or with bespoke shaders. For example, if you&rsquo;re performing culling in your a compute shader you can draw colored bounding spheres to indicate the result of the culling operation.</p>
<p>Here&rsquo;s an example of using both printf and debug drawing together for debugging a ray generation shader, so you can see what I mean:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">ShaderDebug</span><span class="o">::</span><span class="n">FilterIfCursorOnPos</span><span class="p">(</span><span class="n">pixelCoord</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">DebugPrint_</span><span class="p">(</span><span class="s">&#34;RayGen Pixel XY: {0}&#34;</span><span class="p">,</span> <span class="n">pixelCoord</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">DebugPrintVar_</span><span class="p">(</span><span class="n">ray</span><span class="p">.</span><span class="n">Origin</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">DebugPrintVar_</span><span class="p">(</span><span class="n">ray</span><span class="p">.</span><span class="n">Direction</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">DebugPrintVar_</span><span class="p">(</span><span class="n">payload</span><span class="p">.</span><span class="n">HitT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">float3</span> <span class="n">arrowColor</span> <span class="o">=</span> <span class="n">payload</span><span class="p">.</span><span class="n">Radiance</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">arrowColor</span><span class="o">/=</span> <span class="n">max</span><span class="p">(</span><span class="n">max</span><span class="p">(</span><span class="n">max</span><span class="p">(</span><span class="n">arrowColor</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="n">arrowColor</span><span class="p">.</span><span class="n">y</span><span class="p">),</span> <span class="n">arrowColor</span><span class="p">.</span><span class="n">z</span><span class="p">),</span> <span class="mf">0.0001f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">float3</span> <span class="n">arrowEnd</span> <span class="o">=</span> <span class="n">ray</span><span class="p">.</span><span class="n">Origin</span> <span class="o">+</span> <span class="n">ray</span><span class="p">.</span><span class="n">Direction</span> <span class="o">*</span> <span class="n">payload</span><span class="p">.</span><span class="n">HitT</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">ShaderDebug</span><span class="o">::</span><span class="n">DrawArrow</span><span class="p">(</span><span class="n">ray</span><span class="p">.</span><span class="n">Origin</span><span class="p">,</span> <span class="n">arrowEnd</span><span class="p">,</span> <span class="n">float4</span><span class="p">(</span><span class="n">arrowColor</span><span class="p">,</span> <span class="mf">1.0f</span><span class="p">));</span>
</span></span></code></pre></div><h2 id="conclusion">Conclusion</h2>
<p>Thanks for reading this far! Hopefully you got an idea of the scope of things that have changed in D3D12 over the past 10 years, and also found it interesting to read about my own experiences. See you next time!</p>
]]></content:encoded>
		</item>
		
		<item>
			<title>To Early-Z, or Not To Early-Z</title>
			<link>https://therealmjp.github.io/posts/to-earlyz-or-not-to-earlyz/</link>
			<pubDate>Sun, 13 Apr 2025 00:00:00 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/to-earlyz-or-not-to-earlyz/</guid>
			<description>Depth In The Logical Rendering Pipeline Where Does Early-Z Fit In? When Does Early-Z Have To Be Disabled? Discard/Alpha Test Pixel Shader Depth Export UAVs/Storage Textures/Storage Buffers Forcing Early-Z Forced Early-Z With UAVs And Depth Writes Rasterizer Order Views/Fragment Shader Interlock Summary and Conclusion One of the things we often take for granted on GPUs is the idea of early Z testing. It&amp;rsquo;s the main reason why Z prepasses exist at all, and it&amp;rsquo;s one of the things that has allowed forward rendering to remain viable without being completely overwhelmed by pixel shader overdraw (instead it merely gets overwhelmed by quad overshading, but I digress).</description>
			<content:encoded><![CDATA[<p><img src="/images/early-z/early-z.png" alt=""></p>
<div>
    <nav id="TableOfContents">
  <ul>
    <li><a href="#depth-in-the-logical-rendering-pipeline">Depth In The Logical Rendering Pipeline</a></li>
    <li><a href="#where-does-early-z-fit-in">Where Does Early-Z Fit In?</a></li>
    <li><a href="#when-does-early-z-have-to-be-disabled">When Does Early-Z Have To Be Disabled?</a>
      <ul>
        <li><a href="#discardalpha-test">Discard/Alpha Test</a></li>
        <li><a href="#pixel-shader-depth-export">Pixel Shader Depth Export</a></li>
        <li><a href="#uavsstorage-texturesstorage-buffers">UAVs/Storage Textures/Storage Buffers</a></li>
      </ul>
    </li>
    <li><a href="#forcing-early-z">Forcing Early-Z</a>
      <ul>
        <li><a href="#forced-early-z-with-uavs-and-depth-writes">Forced Early-Z With UAVs And Depth Writes</a></li>
        <li><a href="#rasterizer-order-viewsfragment-shader-interlock">Rasterizer Order Views/Fragment Shader Interlock</a></li>
      </ul>
    </li>
    <li><a href="#summary-and-conclusion">Summary and Conclusion</a></li>
  </ul>
</nav>
</div>
<p>One of the things we often take for granted on GPUs is the idea of early Z testing. It&rsquo;s the main reason why Z prepasses exist at all, and it&rsquo;s one of the things that has allowed forward rendering to remain viable without being completely overwhelmed by pixel shader overdraw (instead it merely gets overwhelmed by quad overshading, but I digress). Despite its ubiquity and decades of use, I find that early Z can still be confusing and is often misunderstood. This is for a few reasons, but personally I think it&rsquo;s because it&rsquo;s usually deployed as a &ldquo;magic&rdquo; optimization that&rsquo;s not really supposed to be noticeable or observable by the programmer (aside from performance, of course). That lack of explicit-ness tends to make it unclear as to whether early Z is actually working unless you look at the performance and/or pixel shader execution statistics. In light of that, I&rsquo;m going to use this post to give an overview of what &ldquo;Early-Z&rdquo; is, how it works, and when it doesn&rsquo;t work. At the end, I&rsquo;ll finish with a handy table to summarize the useful information.</p>
<p>While explaining things, I&rsquo;ll include insights and screenshots from my <a href="https://github.com/TheRealMJP/EarlyZTest">DX12 Early-Z Tester</a> that you can find on GitHub. This testing app can create many of the scenarios that we&rsquo;ll discuss below, while also displaying the number of pixel shader invocations as a way to determine whether or not Early-Z culling occurred.</p>
<p>I would also like to make it clear up-front that this article is primarily focused on desktop-class graphics hardware, and not mobile. While much of what&rsquo;s here likely applies to mobile GPUs, I am not familiar enough with them to confidently explain how they may or not diverge from their desktop cousins.</p>
<h2 id="depth-in-the-logical-rendering-pipeline">Depth In The Logical Rendering Pipeline</h2>
<p>Graphics APIs such as D3D, Vulkan, and GL are specified in terms of what&rsquo;s called a <em>logical</em> pipeline for rendering. This pipeline is intentionally abstract and not strictly tied to a specific hardware implementation, and instead dictates the behavior that the programmer can expect to see when the draw a triangle. This necessarily gives IHVs and drivers a lot of latitude to structure their hardware in different ways, as well as to change their implemention over generations of hardware. For example, the <a href="https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-graphics-pipeline">D3D logical pipeline</a> looks like this:</p>
<p><img src="/images/early-z/d3d_pipeline.jpg" alt="The D3D logical rendering pipeline"></p>
<p>This pipeline hasn&rsquo;t fundamentally changed for D3D12 (aside from the addition of mesh shaders) and is very similar in GL/Vulkan, albeit with different terminology. In all cases the actual depth buffer operations are specified to happen at the very end of this logical pipeline after the pixel/fragment shader has run. D3D calls this stage the <a href="https://learn.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage">Output-Merger</a> or OM for short, and it&rsquo;s the logical stage where writes/blending to render target occurs and also where depth/stencil operations logically occur. While it might seem weird that depth operations are the end if you&rsquo;re looking at things from a modern viewpoint, if you apply a historical context it really makes sense:</p>
<ul>
<li>The main purpose of depth/stencil operations is to control whether anything gets written to the render targets, so it makes sense that it would be in the same logical stage where write/blend operations occur</li>
<li>The pixel shader can do certain operations, such as discard/alpha test, that also affect depth operations</li>
<li>The earliest GPUs with hardware depth buffers performed depth operations at the end, since it was originally a visibility algorithm and not so much an optimization</li>
</ul>
<p>The important bit here is that this &ldquo;logical&rdquo; structure fits what you see in your results, regardless of how the GPU actually produced them. The GPU might have had an explicit binning phase, or may have combined your VS + HS + DS all in to one hardware shader stage, but either way you see that depth testing has produced the render target and depth buffer values that you would expect.</p>
<p>Looking through the lens of the logical pipeline, it should also be apparent why writes to UAVs/storage buffers/storage textures from a pixel/fragment shader are unaffected by the depth test by default. After all, depth operations come <em>after</em> the pixel shader and so there&rsquo;s no reason to expect otherwise. In practice things are of course more complex when it comes to UAVs, but more on that later.</p>
<h2 id="where-does-early-z-fit-in">Where Does Early-Z Fit In?</h2>
<p>Ok, so obviously we know that the logical pipeline does not tell the full story here since you&rsquo;re reading an article about Early-Z. So how does it work, if the pipeline says depth operations happen at the very end? The answer is that drivers have enough wiggle room to be &ldquo;sneaky&rdquo; and silently move depth operations up before the pixel shader actually runs so that it can cull the pixel shader entirely:</p>
<p><img src="/images/early-z/d3d_pipeline_early_z.png" alt="The D3D logical rendering pipeline with early-z added"></p>
<p>Basically the driver will look at the current pixel shader and render states, and make a call as to whether it would be safe to cull pixel shader executions and still produce the same observable results that the logical pipeline dictates. If we&rsquo;re talking about bog-standard opaque rendering that only writes to a render target this actually really simple: culling the pixel shader is fine since the only observable output is the render target writes, and so there&rsquo;s no visible difference between culling the render target write and culling the entire pixel shader before it even runs. This works even if depth writes are enabled: the early Z operations can test the existing Z buffer value first and commit the Z write if the test passes, while also allowing the pixel shader to run. Later on if some other pixel/fragment ends up having a depth that also passes the depth test due to being closer, that&rsquo;s fine since the render target results will just get overwritten. In that regard the order of the drawn triangles doesn&rsquo;t matter, you get the same result either way in the end. There&rsquo;s of course a <em>performance</em> difference from the ordering: with depth writes enabled you ideally want to order triangles from closer to further so that you have fewer &ldquo;wasted&rdquo; pixel shader threads that ultimately don&rsquo;t contribute. Or alternatively you can use the popular approach of a depth-only prepass, which effectively guarantees that the depth buffer is pre-seeded with the closest depth and thus the pixel shader will (mostly) only run for those visible pixels. Either way, it&rsquo;s pretty clever that hardware and drivers can do this sort of thing to improve performance while still conforming to the logical pipeline!</p>
<p>Let&rsquo;s try a simple test case in our test app to verify this. For this test case and others shared here, I&rsquo;ll be running them on my AMD RX 7900 XT. First, let&rsquo;s draw two triangles in back-to-front order with the red triangle being further from the camera:</p>
<p><img src="/images/early-z/z-tester-back-to-front.png" alt=""></p>
<p>You can see that in this case we end up with 648000 pixel shader invocations for these triangles. Let&rsquo;s now reverse the draw order so that we draw with the more optimal front-to-back ordering:</p>
<p><img src="/images/early-z/z-tester-front-to-back.png" alt=""></p>
<p>This time we only shaded 440640 pixels, which is substantially fewer! This is Early-Z in action, working the way we would expect.</p>
<p>One thing to keep in mind is that even for cases where Early-Z is considered &ldquo;safe&rdquo; to do, it&rsquo;s still ultimately up to the driver and the hardware to make that call unless you force its hand (more on that later). The driver may decide not to based on heuristics, since the hardware might have to take extra steps to be able to perform Early-Z culling. The granularity at which pixels are culled as part of Early-Z can also vary depending on the hardware in question. The earliest PC hardware to use Early-Z did so with &ldquo;hierarchical depth buffers&rdquo; (Hi-Z for short), where the hardware would maintain a separate buffer containing the min and max depth values for an NxN region of the depth buffer. Such a structure is useful, since it allows for fast and coarse z cull without requiring the bandwidth of reading the full per-pixel depth buffer values. During the D3D10 era both ATI/AMD and Nvidia gained the ability to perform fine-grained early Z tests as well, but <a href="https://fgiesen.wordpress.com/2011/07/08/a-trip-through-the-graphics-pipeline-2011-part-7/">hierarchical depth buffers are still used</a> to coarsely cull before performing the more expensive fine-grained tests.</p>
<h2 id="when-does-early-z-have-to-be-disabled">When Does Early-Z Have To Be Disabled?</h2>
<p>In the standard opaque rendering case we mentioned earlier, we discussed how it was &ldquo;safe&rdquo; for the driver to cull the pixel shader if the Early-Z test failed. But clearly there are cases where the driver can&rsquo;t do this since it would violate the rules of the logical pipeline. Let&rsquo;s now go through the most common cases.</p>
<h3 id="discardalpha-test">Discard/Alpha Test</h3>
<p>This is probably the most common one to encounter, and probably also mentioned the most in the context of &ldquo;things that break Early-Z&rdquo;. As a quick recap, if the pixel shader executes a <code>discard</code> operation (or uses it indirectly through the <code>clip()</code> intrinsic) then all depth, stencil, and render target writes skipped for that pixel. This clearly has implications for Early-Z if depth writes are enabled, since the depth write can&rsquo;t actually be committed until the pixel shader runs to completion. At first thought, it may seem like the driver has no choice but to fall back to &ldquo;Late-Z&rdquo; in the case of pixel shader discard + depth writes. While this is certainly a possibility, many GPUs are still able to perform some degree of early depth testing to reject and cull pixel shader executions even if it has to defer the depth write until afterwards. The reason GPUs can do this is because discard doesn&rsquo;t <em>change</em> the z/depth of the pixel that was produced by interpolating the triangle&rsquo;s vertex positions: it merely makes the write of that depth conditional. Therefore it is still &ldquo;safe&rdquo; to cull a pixel shader thread with discard if the rasterized depth fails the depth test based on the current depth buffer contents. You can see this called out in this <a href="https://www.slideshare.net/slideshow/dx11-performancereloaded/46398197">older slide from GDC 2013</a>, which shows how the coarse hierarchical depth tests still occur while deferring depth writes until after the pixel shader runs:</p>
<p><img src="/images/early-z/discard_early_z.png" alt="A diagram from a joint AMD/Nvidia presentation from GDC 2013 showing how coarse depth cull remains enabled when discard is used with depth writes"></p>
<p>While &ldquo;splitting&rdquo; the depth test in this way can allow Early-Z culling to still occur for discard scenarios, the fact that depth is written later in the pipeline can potentially have adverse effects on our Early-Z culling rate. To see this in action, let&rsquo;s revisit the front-to-back test case we looked at earlier. Except this time, in the pixel shader we&rsquo;ll have a <code>discard</code> that never actually gets executed:</p>
<p><img src="/images/early-z/z-tester-front-to-back-discard.png" alt=""></p>
<p>With this scenario we get about 582000 PS invocations, with the final value fluctuating slightly from frame-to-frame. This cull rate is not nearly as great was what we achieved in the no-discard case, since a lot of pixels from the further triangle are going to get accepted by the Early-Z test before the Z buffer has been updated by the closer triangle. We can try to verify this hypothesis by inserting a global barrier between the draws to force the first draw to finish before starting the second:</p>
<p><img src="/images/early-z/z-tester-front-to-back-discard-barrier.png" alt=""></p>
<p>With the barrier (which to be clear, you wouldn&rsquo;t want to do in practice!) we now get an Early-Z culling rate that&rsquo;s much closer to what we got without the discard. We can also confirm with the testing app that clearing the depth buffer to the closest possible value results in no pixel shaader invocations. This suggests that it can be good practice to try to render all of your non-alpha-test opaques first, or at least ones that have a good chance of occluding things. Only performing discard in a depth prepass followed by a full opaque pass with EQUAL depth testing can also be effective for ensuring that the heavier opaque pixel shader doesn&rsquo;t execute for occluded pixels.</p>
<p>Meanwhile, if depth writes are <em>not</em> enabled then Early-Z is totally compatible with shader discard. For that case the depth buffer doesn&rsquo;t need to be updated, so it&rsquo;s completely safe to perform the depth test early and cull the pixel shader before it runs. This means that discard can be used freely as an optimization in particles and other transparent draws without worrying about affecting Early-Z.</p>
<p>One very important thing to note is that the <em>implications to Early-Z apply as long as your compiled shader instruction stream contains any discard at all!</em> This is still true even if no pixel ever ends up being discarded, since the driver must make decisions about Early-Z up-front before actually running your shader. Therefore a runtime branch on a constant or uniform to avoid a discard can still have Early-Z implications even if that branch is never taken.</p>
<p>It&rsquo;s also worth noting that exporting coverage from the pixel shader via <code>SV_Coverage</code>/<code>gl_SampleMask</code> has exactly the same implications as performing a <code>discard</code>, so the same rules apply in terms of Early-Z.</p>
<h3 id="pixel-shader-depth-export">Pixel Shader Depth Export</h3>
<p>Typically the depth value used for depth testing is generated by interpolating Z from the triangle vertices. However pixel/fragment shaders also have the ability to fully &ldquo;override&rdquo; this depth value if they choose to. In HLSL this is done with the <code>SV_Depth</code> attribute, whereas in GLSL you make use of the <code>gl_FragDepth</code> global. Depth export can be a useful tool, but it naturally creates issues for Early-Z. Short of being able to see into the future, there&rsquo;s simply no way for the hardware to know what the depth value is going to be without executing the pixel shader. Thus the hardware has no choice but to fall back to Late-Z for any shaders that manually export a depth value. Notably the situation is the same regardless of whether or not depth writes are enabled: in both cases Late-Z is the only safe option that respects the logical pipeline. We can confirm this behavior with our testing app:</p>
<p><img src="/images/early-z/z-tester-arbitrary-depth.png" alt=""></p>
<h4 id="conservative-depth-export">Conservative Depth Export</h4>
<p>D3D11 later added variants of depth export that are collectively known as <a href="../d3d11-features/">conservative depth export</a>. These variants allow you to add a constraint on the depth value output by the pixel shader in the form of an inequality. For example, <code>SV_DepthGreaterEqual</code> allows you to generate a depth value with the constraint that it must be greater than or equal to the &ldquo;natural&rdquo; pixel depth that was calculated by interpolating the triangle vertex positions. The reason these exist is that this constraint can potentially allow the hardware to safely enable a degree of Early-Z functionality despite the final depth not being known until the pixel shader executes. For that to occur, the inequality has to &ldquo;oppose&rdquo; the inequality used as part of the depth render state. Returning to our <code>SV_DepthGreaterEqual</code> example: let&rsquo;s say we have depth writes disabled but depth testing enabled with a <code>LESS_EQUAL</code> depth function. In this scenario the driver and hardware can determine that the pixel shader can only <em>increase</em> the natural depth from interpolation, which means that if the natural depth fails the depth test the pixel will always fail no matter the results of the shader. Therefore the pixel shader can be safely culled with no visible effect on the outputs. This is very similar to the situation for shader discard, where Early-Z can also be enabled for this case since the shader can&rsquo;t invalidate the results of the Early-Z test.</p>
<p>Just as with <code>discard</code>, things get more complicated when depth writes are combined with conservative depth export. In this case depth writes can&rsquo;t be committed before the pixel shader runs, since the final depth value isn&rsquo;t known yet. Therefore it must be postponed until the Late-Z stage. Potentially the hardware can still perform an Early-Z cull as it does for the discard case, assuming it&rsquo;s capable of such a thing. On such hardware conservative depth export retains an advantage over arbitrary depth export even when depth writes are enabled, with the same caveats about reduced Early-Z culling rates that apply for discard. We can confirm this in our testing app:</p>
<p><img src="/images/early-z/z-tester-conservative-depth.png" alt=""></p>
<p>Here we very early end up with the full 648000 shader invocations, which is actually even worse than what we encountered with discard. Forcing a barrier once again gives us an almost-perfect culling rate:</p>
<p><img src="/images/early-z/z-tester-conservative-depth-barrier.png" alt=""></p>
<h3 id="uavsstorage-texturesstorage-buffers">UAVs/Storage Textures/Storage Buffers</h3>
<p>Buckle up everyone, because this is where things get even more complicated. As I alluded to earlier, pixel shaders that make use of arbitrary writes to textures or buffers will also cause the hardware to switch to Late-Z. The underlying assumption made by drivers that enable Early-Z is that the pixel shader is a &ldquo;pure&rdquo; function with well-defined outputs and no side effects. Writes to UAVs/storage buffers/storage textures violate these assumptions, since by nature they are side effects of running the shader. Faced with such a shader, drivers have no choice but to fall back to using Late-Z so that depth testing does not affect the values written to the UAVs.</p>
<p>&hellip;unless Early-Z is forced on, which is a feature that was added to APIs alongside shader-writable resources. So let&rsquo;s talk about that next.</p>
<h2 id="forcing-early-z">Forcing Early-Z</h2>
<p>While the &ldquo;UAVs ignore depth testing&rdquo; behavior makes sense through the lens of the logical pipeline, it&rsquo;s clearly not a useful behavior for a lot of things you can do with arbitrary pixel shader writes. As an example straight from 2009, consider an Order Independent Transparency algorithm that appends transparent fragments to a linked list so they can be sorted later. Appending transparent fragments that are occluded by opaque surfaces makes no sense, and manually performing depth testing in the shader doesn&rsquo;t save you the full cost of executing the pixel shader the way that Early-Z cull does. The Early-Z hardware is <em>right there</em>, we should just be able to use it!</p>
<p>D3D11 addressed this need by letting you tag your pixel shader entry points with a then-new <code>[earlydepthstencil]</code> attribute. While a bit of a mouthful, this attribute <a href="https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#16.12%20Pixel%20Shader%20Execution%20Control:%20Force%20Early/Late%20Depth/Stencil%20Test">does exactly what it says it does</a>: it forces all depth and stencil operations to happen <em>before</em> the pixel shader executes, and requires that the pixel shader be culled entirely if the test fails. This gives us exactly what we want for the OIT case described earlier: we get both the results and the optimizations we want from the depth test, and we can still do fancy UAV stuff. In fact you don&rsquo;t even necessarilly need to use this on pixel shaders with UAVs, it can be applied to any pixel shader where you want to force the behavior.</p>
<p>Now this is clearly great and useful, but before slapping that attribute on every shader one has to consider what it means to fully move all depth reads and writes to before the pixel shader. For example, any kind of depth export is completely incompatible with this attribute<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and those shader depth outputs are completely ignored. More subtly, it also breaks <code>discard</code> behavior if depth writes are enabled: the <code>discard</code> will still prevent render target writes, but the depth buffer write will have already happened before the <code>discard</code> occurs and thus the new depth will still be written. Here&rsquo;s what it looks like in practice:</p>
<p><img src="/images/early-z/forceearlyz-discard.png" alt="Images showing alpha-test foliage without and with forcing Early-Z"></p>
<p>Notice how the leaves in the right image are still applying alpha testing, but they&rsquo;re occluding each other as if there were no alpha testing. This could be quite unexpected if you didn&rsquo;t think it all the way through, and is probably not ever what you want!</p>
<p>Here&rsquo;s a simpler example from the testing app that shows what&rsquo;s happening even more clearly:</p>
<p><img src="/images/early-z/z-tester-discard-forced-early-z.png" alt=""></p>
<p>You&rsquo;re generally fine if you have depth writes enabled with <code>[earlydepthstencil]</code> as long as you&rsquo;re not using <code>discard</code>, depth exports, UAVs, or anything else that we already mentioned as causing issues for automatically enabling Early-Z. In such cases performing the depth test + write early is fine since there&rsquo;s no possibility for the pixel shader to modify the depth in any way, and render target writes already have implicit ordering guarantees.</p>
<h3 id="forced-early-z-with-uavs-and-depth-writes">Forced Early-Z With UAVs And Depth Writes</h3>
<p>Ok so let&rsquo;s say we really wanna get crazy and use UAVs together with depth writes and forced Early-Z. What happens in this case? The short answer is that you can still get a race condition between overlapping pixels, just like any other usage of UAVs from the pixel shader. While the Early-Z depth test + update <em>can</em> cull an already-occluded pixel before its shader has a chance to run, this will only happen if the triangle containing the culled pixel was submitted <em>after</em> the triangle that occludes it. If the triangles are not submitted in this order (or they intersect), then the closer and further pixels can run concurrently and will end up racing each other.</p>
<p>Here&rsquo;s two examples of that this ends up looking like:</p>
<p><img src="/images/early-z/forceearlyz-uav.png" alt="The D3D logical rendering pipeline with Early-Z added"></p>
<p>This is a bit of a bummer, since it means you&rsquo;re essentially stuck with regular fixed-function render target writes if you need to also enable hardware depth writes<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. This may force you to require a depth prepass, and it&rsquo;s not ideal to have such a requirement for correctness of our results. However there is one more option that we have yet to explore&hellip;.</p>
<h3 id="rasterizer-order-viewsfragment-shader-interlock">Rasterizer Order Views/Fragment Shader Interlock</h3>
<p>Before we wrap up, I wanted to touch on how <a href="https://learn.microsoft.com/en-us/windows/win32/direct3d11/rasterizer-order-views">ROVs</a>/<a href="https://registry.khronos.org/vulkan/specs/latest/man/html/VK_EXT_fragment_shader_interlock.html">FSI</a> factors into the Early-Z + depth write situation. ROVs guarantee two things:</p>
<ol>
<li>That pixel shaders mapping to the same pixel coordinate will not have overlapping access to the ROV resource, and can safely perform read/modify/write operations without requiring atomic operations</li>
<li>That accesses to ROV resources happen in API submission order, similar to how render target writes/blending works</li>
</ol>
<p>If you&rsquo;re not using depth writes, then the results you get from ROVs + <code>[earlydepthstencil]</code> are straightforward: you&rsquo;ll only get writes from pixels that pass the depth test, and those writes will happen in submission order. This is very handy for OIT and other similar techniques. But what about if depth writes are enabled? In this case it seems like we should again get results that match old-school render target writes/blends, where the last visible write is from the closest pixel that ultimately passes the Z test. Getting these expected z-order results assumes that Early-Z tests happen in order of API submission, such that you get one of two results when two pixels overlap:</p>
<ul>
<li>The &ldquo;closer&rdquo; pixel performs Z operations first, and so the &ldquo;further&rdquo; pixel shader thread is culled before it even runs</li>
<li>The &ldquo;further&rdquo; pixel performs Z operations first, but the &ldquo;top&rdquo; pixel is guaranteed for its write to happen second (and thus be visible) because of ROV rules</li>
</ul>
<p>If we think about it, this really the same situation that we have if we just use regular render target writes instead of ROVs: the writes happen in submission order, and so the Z operations must also happen in submission order to get the correct results.</p>
<p>From some simple tests I&rsquo;ve performed on my AMD RX 7900 XT, it does seem that this works! But like anything mentioned in this article, you should try to verify yourself on the hardware that you care on. I&rsquo;ve tested successfully in more complex scenes, and it also works as expected in the testing app:</p>
<p><img src="/images/early-z/z-tester-rovs.png" alt=""></p>
<p>Regardless, this approach would still be incompatible with <code>discard</code> or depth export just like any other pixel shader that forces Early-Z. And of course there&rsquo;s the inherent performance overhead of ROVs, which varies depending on the hardware, the amount of parallelism, the amount of overdraw, and the size of the critical section in the shader.</p>
<h2 id="summary-and-conclusion">Summary and Conclusion</h2>
<p>To sum things up, here&rsquo;s a table listing the expected implicit behavior that a driver will use for Early-Z if you don&rsquo;t force it:</p>
<table>
<thead>
<tr>
<th style="text-align:center">Shader Features</th>
<th style="text-align:center">Depth Writes Enabled</th>
<th style="text-align:center">Expected Early-Z Behavior</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">None</td>
<td style="text-align:center">No</td>
<td style="text-align:center">Likely Early-Z</td>
</tr>
<tr>
<td style="text-align:center">discard</td>
<td style="text-align:center">No</td>
<td style="text-align:center">Likely Early-Z</td>
</tr>
<tr>
<td style="text-align:center">depth export</td>
<td style="text-align:center">No</td>
<td style="text-align:center">Always Late-Z</td>
</tr>
<tr>
<td style="text-align:center">conservative depth export</td>
<td style="text-align:center">No</td>
<td style="text-align:center">Likely Early-Z if opposing the depth test direction, otherwise Late-Z</td>
</tr>
<tr>
<td style="text-align:center">UAV writes</td>
<td style="text-align:center">No</td>
<td style="text-align:center">Always Late-Z</td>
</tr>
<tr>
<td style="text-align:center">None</td>
<td style="text-align:center">Yes</td>
<td style="text-align:center">Likely Early-Z</td>
</tr>
<tr>
<td style="text-align:center">discard</td>
<td style="text-align:center">Yes</td>
<td style="text-align:center">Possibly Early-Z with additional Late-Z write/test, potentially reduced early PS culling rate</td>
</tr>
<tr>
<td style="text-align:center">depth export</td>
<td style="text-align:center">Yes</td>
<td style="text-align:center">Always Late-Z</td>
</tr>
<tr>
<td style="text-align:center">conservative depth export</td>
<td style="text-align:center">Yes</td>
<td style="text-align:center">Possibly Early-Z + Late Z if opposing the depth test direction, with potentially reduced early PS culling rate. Otherwise Late-Z</td>
</tr>
<tr>
<td style="text-align:center">UAV writes</td>
<td style="text-align:center">Yes</td>
<td style="text-align:center">Always Late-Z</td>
</tr>
</tbody>
</table>
<p>And now let&rsquo;s also sum up the expected behavior when forcing Early-Z using <code>[earlydepthstencil]</code>:</p>
<table>
<thead>
<tr>
<th style="text-align:center">Shader Features</th>
<th style="text-align:center">Depth Writes Enabled</th>
<th style="text-align:center">Expected Behavior</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">None</td>
<td style="text-align:center">No</td>
<td style="text-align:center">Correct results</td>
</tr>
<tr>
<td style="text-align:center">discard</td>
<td style="text-align:center">No</td>
<td style="text-align:center">Correct results</td>
</tr>
<tr>
<td style="text-align:center">depth export</td>
<td style="text-align:center">No</td>
<td style="text-align:center"><strong>Depth export is ignored!</strong></td>
</tr>
<tr>
<td style="text-align:center">conservative depth export</td>
<td style="text-align:center">No</td>
<td style="text-align:center"><strong>Depth export is ignored!</strong></td>
</tr>
<tr>
<td style="text-align:center">UAV writes</td>
<td style="text-align:center">No</td>
<td style="text-align:center">UAV writes will only happen for pixels that pass the depth test, but overlapping pixels are unordered</td>
</tr>
<tr>
<td style="text-align:center">ROV writes</td>
<td style="text-align:center">No</td>
<td style="text-align:center">UAV writes will only happen for pixels that pass the depth test, and writes happen in submission order</td>
</tr>
<tr>
<td style="text-align:center">None</td>
<td style="text-align:center">Yes</td>
<td style="text-align:center">Corerct results</td>
</tr>
<tr>
<td style="text-align:center">discard</td>
<td style="text-align:center">Yes</td>
<td style="text-align:center"><strong>Incorrect results, discard will not affect Z writes!</strong></td>
</tr>
<tr>
<td style="text-align:center">depth export</td>
<td style="text-align:center">Yes</td>
<td style="text-align:center"><strong>Depth export is ignored!</strong></td>
</tr>
<tr>
<td style="text-align:center">conservative depth export</td>
<td style="text-align:center">Yes</td>
<td style="text-align:center"><strong>Depth export is ignored!</strong></td>
</tr>
<tr>
<td style="text-align:center">UAV writes</td>
<td style="text-align:center">Yes</td>
<td style="text-align:center">UAV writes will not match Z order! Overlapping pixels can race.</td>
</tr>
<tr>
<td style="text-align:center">ROV writes</td>
<td style="text-align:center">Yes</td>
<td style="text-align:center">ROV writes will match the expected Z-tested results</td>
</tr>
</tbody>
</table>
<p>Hopefully these tables and the article itself gave you some intuition for understanding when the driver is sneaking in Early-Z automatically, while also giving you an understanding of what exactly happens when you force Early-Z on for your pixel shader. Thanks for reading!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>AMD has a Vulkan extension called <a href="https://docs.vulkan.org/features/latest/features/proposals/VK_AMD_shader_early_and_late_fragment_tests.html">VK_AMD_shader_early_and_late_fragment_tests</a> that relaxes the requirement of having no depth exports when forcing Early-Z. Since AMD hardware can support both early and late Z tests, it can allow the early Z test to still be used for initially rejecting fragments while not committing the Z write until after the (conservative) depth has been exported by the fragment shader.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>If you want to forego hardware depth writes and tests, you can use atomics to combine some number of depth bits with some remaining &ldquo;payload&rdquo; bits. This is most viable with a visiblity buffer, since even with 64-bit atomics you likely only have 32-bits of payload to work with.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
		</item>
		
		<item>
			<title>Shader Printf in HLSL and DX12</title>
			<link>https://therealmjp.github.io/posts/hlsl-printf/</link>
			<pubDate>Sun, 07 Jan 2024 00:00:00 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/hlsl-printf/</guid>
			<description>Overall Approach Setting Up The Print Buffer The &amp;ldquo;Magic&amp;rdquo; Debug Info Buffer Dealing With The String Problem A Cursed Path Packing It All Into A Buffer Reading Back On The CPU Going Beyond Printf CR LF Unless you&amp;rsquo;re fortunate enough to to be working exclusively in Cuda, debugging GPU shaders is still very much &amp;ldquo;not great&amp;rdquo; in the year 2024. Tools like RenderDoc and PIX are amazing and do provide the ability to step through a shader and inspect variables, but they&amp;rsquo;re fundamentally tied to a &amp;ldquo;capture&amp;rdquo; workflow.</description>
			<content:encoded><![CDATA[<p><img src="/images/shader-printf.png" alt=""></p>
<div>
    <nav id="TableOfContents">
  <ul>
    <li><a href="#overall-approach">Overall Approach</a></li>
    <li><a href="#setting-up-the-print-buffer">Setting Up The Print Buffer</a></li>
    <li><a href="#the-magic-debug-info-buffer">The &ldquo;Magic&rdquo; Debug Info Buffer</a></li>
    <li><a href="#dealing-with-the-string-problem">Dealing With The String Problem</a>
      <ul>
        <li><a href="#a-cursed-path">A Cursed Path</a></li>
      </ul>
    </li>
    <li><a href="#packing-it-all-into-a-buffer">Packing It All Into A Buffer</a></li>
    <li><a href="#reading-back-on-the-cpu">Reading Back On The CPU</a></li>
    <li><a href="#going-beyond-printf">Going Beyond Printf</a></li>
    <li><a href="#cr-lf">CR LF</a></li>
  </ul>
</nav>
</div>
<p>Unless you&rsquo;re fortunate enough to to be working exclusively in Cuda, debugging GPU shaders is still very much &ldquo;not great&rdquo; in the year 2024. Tools like <a href="https://renderdoc.org/">RenderDoc</a> and <a href="https://devblogs.microsoft.com/pix/introduction/">PIX</a> are amazing and do provide the ability to step through a shader and inspect variables, but they&rsquo;re fundamentally tied to a &ldquo;capture&rdquo; workflow. This means that you need to run your game or app from RenderDoc/PIX in capture mode (which adds overhead), and then capture at least a single frame. Then after the capture is finished you can analyze it, find your draw or dispatch, and finally select which thread you&rsquo;d like to debug. Once you&rsquo;re debugging things generally work, but it&rsquo;s still possible you&rsquo;ll encounter issues due to the way that debugging works in these tools. They are are not simply inspecting the state of an executing (suspended) thread like you would expect from a CPU debugger, instead they work by either emulating shader instructions on the CPU or by patching the shader bytecode to emit intermediate program state to a buffer that can be read by the CPU. Don&rsquo;t get me wrong, these tools are great to have and it&rsquo;s fantastic that they can be used to debug shaders at all. But it&rsquo;s still not enough, and generally more debugging tools are needed for unusually tough problems. Even if vendor-agnostic GPU debuggers were as good as they are on CPU, you would still probably want to reach for some other tools depending on the situation.</p>
<p>In CPU land, the venerable <code>printf</code> and all of its related functions are commonly used for debugging and diagnostics. However we have not yet had a cross-vendor/cross-API way to do the same for shaders. To be fair it&rsquo;s a lot more complicated on a GPU! Shaders run in batches of thousands or even millions of threads, and run on a completely separate processor than what the OS and all of its facilities run on. Despite those issues, Vulkan/SPIR-V <a href="https://www.lunarg.com/wp-content/uploads/2021/08/Using-Debug-Printf-02August2021.pdf">does actually provide a printf</a> that&rsquo;s available from both GLSL and HLSL<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. It comes with caveats though. In particular it&rsquo;s setup so that the messages can be intercepted by a validation layer or by RenderDoc, which makes it more difficult for the engine/app itself to obtain the messages and process them. And of course this doesn&rsquo;t help if you&rsquo;re target D3D12 or other APIs and you&rsquo;d like your prints to work on those platforms as well.</p>
<p>In this article I&rsquo;ll walk through how to build your own shader printf completely in software, using HLSL and D3D12 as the target language and API. The concepts here can be adapted to any API and shader language, although the shading language and compile can (significantly) impact how you handle strings in your shaders,</p>
<h2 id="overall-approach">Overall Approach</h2>
<p>The printf implementation I&rsquo;m going to describe is going to work like this:</p>
<ul>
<li>Every frame, have the GPU clear a big buffer that we&rsquo;ll use to store the print strings and arguments</li>
<li>Make a debug info buffer SRV available at a &ldquo;magic&rdquo; known descriptor index, which effectively makes it globally available to all shaders through a <code>#include</code>
<ul>
<li>This buffer will have a descriptor index for the print buffer, along with some additional info useful for debugging</li>
</ul>
</li>
<li>When a shader wants to print, it will use an atomic to allocate some space in the big print buffer and stuff the string + data into it</li>
<li>After recording every frame, copy the print buffer to a CPU-accessible readback buffer so that the data can be read back. Then every print can be decoded from the buffer and logged somewhere.</li>
</ul>
<p>Pretty simple in concept! But as always the devil is in the details. Lets go though them one by one.</p>
<h2 id="setting-up-the-print-buffer">Setting Up The Print Buffer</h2>
<p>For our print buffer we don&rsquo;t need anything fancy at all, just a &ldquo;big enough&rdquo; buffer that&rsquo;s writable by the GPU. In my sample framework I do it like this, and also create a pair of matching readback buffers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">PrintBuffer</span><span class="p">.</span><span class="n">Initialize</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">NumElements</span> <span class="o">=</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span>  <span class="o">*</span> <span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">CreateUAV</span> <span class="o">=</span> <span class="nb">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Name</span> <span class="o">=</span> <span class="sa">L</span><span class="s">&#34;Shader Debug Print Buffer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span><span class="p">(</span><span class="n">ReadbackBuffer</span><span class="o">&amp;</span> <span class="nl">buffer</span> <span class="p">:</span> <span class="n">PrintReadbackBuffers</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">buffer</span><span class="p">.</span><span class="n">Initialize</span><span class="p">(</span><span class="n">PrintBuffer</span><span class="p">.</span><span class="n">InternalBuffer</span><span class="p">.</span><span class="n">Size</span><span class="p">);</span>
</span></span></code></pre></div><p><code>PrintBuffer</code> in this case is a <code>RawBuffer</code> which ends up being a <code>RWByteAddressBuffer</code> in the shader. Every frame it gets cleared to all 0&rsquo;s, which I do with a utility function that just uses a compute shader to do the clear (since <code>ClearUnorderedAccessViewUINT</code> is painful to use in D3D12):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">DX12</span><span class="o">::</span><span class="n">ClearRawBuffer</span><span class="p">(</span><span class="n">cmdList</span><span class="p">,</span> <span class="n">PrintBuffer</span><span class="p">,</span> <span class="n">Uint4</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">DX12</span><span class="o">::</span><span class="n">Barrier</span><span class="p">(</span><span class="n">cmdList</span><span class="p">,</span> <span class="n">PrintBuffer</span><span class="p">.</span><span class="n">InternalBuffer</span><span class="p">.</span><span class="n">WriteToWriteBarrier</span><span class="p">());</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">ClearRawBuffer</span><span class="p">(</span><span class="n">ID3D12GraphicsCommandList</span><span class="o">*</span> <span class="n">cmdList</span><span class="p">,</span> <span class="k">const</span> <span class="n">RawBuffer</span><span class="o">&amp;</span> <span class="n">buffer</span><span class="p">,</span> <span class="k">const</span> <span class="n">Uint4</span><span class="o">&amp;</span> <span class="n">clearValue</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cmdList</span><span class="o">-&gt;</span><span class="n">SetComputeRootSignature</span><span class="p">(</span><span class="n">UniversalRootSignature</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">cmdList</span><span class="o">-&gt;</span><span class="n">SetPipelineState</span><span class="p">(</span><span class="n">clearRawBufferPSO</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Assert_</span><span class="p">(</span><span class="n">buffer</span><span class="p">.</span><span class="n">UAV</span> <span class="o">!=</span> <span class="n">uint32</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">ClearRawBufferConstants</span> <span class="n">cbData</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">ClearValue</span> <span class="o">=</span> <span class="n">clearValue</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">DescriptorIdx</span> <span class="o">=</span> <span class="n">buffer</span><span class="p">.</span><span class="n">UAV</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">Num16ByteElements</span> <span class="o">=</span> <span class="n">uint32</span><span class="p">(</span><span class="n">AlignTo</span><span class="p">(</span><span class="n">buffer</span><span class="p">.</span><span class="n">NumElements</span> <span class="o">*</span> <span class="n">buffer</span><span class="p">.</span><span class="n">Stride</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> <span class="o">/</span> <span class="mi">16</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="n">BindTempConstantBuffer</span><span class="p">(</span><span class="n">cmdList</span><span class="p">,</span> <span class="n">cbData</span><span class="p">,</span> <span class="n">URS_ConstantBuffers</span> <span class="o">+</span> <span class="mi">0</span><span class="p">,</span> <span class="n">CmdListMode</span><span class="o">::</span><span class="n">Compute</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">uint32</span> <span class="n">dispatchX</span> <span class="o">=</span> <span class="n">DispatchSize</span><span class="p">(</span><span class="n">cbData</span><span class="p">.</span><span class="n">Num16ByteElements</span><span class="p">,</span> <span class="n">clearRawBufferTGSize</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">cmdList</span><span class="o">-&gt;</span><span class="n">Dispatch</span><span class="p">(</span><span class="n">dispatchX</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>That&rsquo;s it for the print buffer setup! Next we&rsquo;ll look at our &ldquo;magic&rdquo; debug info buffer.</p>
<h2 id="the-magic-debug-info-buffer">The &ldquo;Magic&rdquo; Debug Info Buffer</h2>
<p>In my sample framework I use shader hot-reloading quite frequently, since it provides a super-quick iteration loop that doesn&rsquo;t require re-starting the app. Often one of the reasons for hot-reloading a shader is to add some temporary debugging code in order to figure out why something isn&rsquo;t working correctly. To that end, I wanted to have the ability to add debug prints to any shader without having to change the bindings and re-compile the C++ code. This could potentially be done by either always adding an extra descriptor index to binding structs or by ensuring it was always bound to the root signature, but instead I opted to lean on <a href="https://devblogs.microsoft.com/directx/hlsl-shader-model-6-6/">Shader Model 6.6 bindless</a> by just placing a buffer SRV descriptor at a &ldquo;known&rdquo; static index shared between C++ and GPU code. That makes the buffer available to anyone regardless of what root signature they use or what else is going on in the shader, which is nice. On the C++ side it&rsquo;s simple: I just added a way to allocate a specific descriptor index from my <code>DescriptorHeap</code> type, create an SRV in that descriptor slot, and free the original SRV descriptor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">DebugInfoBuffer</span><span class="p">.</span><span class="n">Initialize</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">NumElements</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">DebugInfo</span><span class="p">)</span> <span class="o">/</span> <span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Dynamic</span> <span class="o">=</span> <span class="nb">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">CPUAccessible</span> <span class="o">=</span> <span class="nb">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">Name</span> <span class="o">=</span> <span class="sa">L</span><span class="s">&#34;Debug Info Buffer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="n">PersistentDescriptorAlloc</span> <span class="n">alloc</span> <span class="o">=</span> <span class="n">DX12</span><span class="o">::</span><span class="n">SRVDescriptorHeap</span><span class="p">.</span><span class="n">AllocatePersistent</span><span class="p">(</span><span class="n">MagicDebugBufferIndex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">DX12</span><span class="o">::</span><span class="n">SRVDescriptorHeap</span><span class="p">.</span><span class="n">FreePersistent</span><span class="p">(</span><span class="n">DebugInfoBuffer</span><span class="p">.</span><span class="n">SRV</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">DebugInfoBuffer</span><span class="p">.</span><span class="n">SRV</span> <span class="o">=</span> <span class="n">alloc</span><span class="p">.</span><span class="n">Index</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span><span class="p">(</span><span class="n">uint32</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">ArraySize_</span><span class="p">(</span><span class="n">alloc</span><span class="p">.</span><span class="n">Handles</span><span class="p">);</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">D3D12_SHADER_RESOURCE_VIEW_DESC</span> <span class="n">srvDesc</span> <span class="o">=</span> <span class="n">DebugInfoBuffer</span><span class="p">.</span><span class="n">SRVDesc</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">DX12</span><span class="o">::</span><span class="n">Device</span><span class="o">-&gt;</span><span class="n">CreateShaderResourceView</span><span class="p">(</span><span class="n">DebugInfoBuffer</span><span class="p">.</span><span class="n">Resource</span><span class="p">(),</span> <span class="o">&amp;</span><span class="n">srvDesc</span><span class="p">,</span> <span class="n">alloc</span><span class="p">.</span><span class="n">Handles</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Then every CPU frame we just need to fill that buffer with fresh data:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">DebugInfo</span> <span class="n">debugInfo</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">PrintBuffer</span> <span class="o">=</span> <span class="n">PrintBuffer</span><span class="p">.</span><span class="n">SRV</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">PrintBufferSize</span> <span class="o">=</span> <span class="n">uint32</span><span class="p">(</span><span class="n">PrintBuffer</span><span class="p">.</span><span class="n">InternalBuffer</span><span class="p">.</span><span class="n">Size</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">CursorXY</span> <span class="o">=</span> <span class="p">{</span> <span class="n">cursorX</span><span class="p">,</span> <span class="n">cursorY</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">DebugInfoBuffer</span><span class="p">.</span><span class="n">MapAndSetData</span><span class="p">(</span><span class="o">&amp;</span><span class="n">debugInfo</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">debugInfo</span><span class="p">)</span> <span class="o">/</span> <span class="mi">4</span><span class="p">);</span>
</span></span></code></pre></div><p>In the shader it&rsquo;s no fuss at all to get the buffer through <code>ResourceDescriptorHeap</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// In a header file shared between shaders and C++:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">struct</span> <span class="nc">DebugInfo</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DescriptorIndex</span> <span class="n">PrintBuffer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ShaderUint</span> <span class="n">PrintBufferSize</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ShaderUint2</span> <span class="n">CursorXY</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">SharedConstant_</span> <span class="n">DescriptorIndex</span> <span class="n">MagicDebugBufferIndex</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// In ShaderDebug.hlsli
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">DebugInfo</span> <span class="nf">GetDebugInfo</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ByteAddressBuffer</span> <span class="n">debugBuffer</span> <span class="o">=</span> <span class="n">ResourceDescriptorHeap</span><span class="p">[</span><span class="n">MagicDebugBufferIndex</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">debugBuffer</span><span class="p">.</span><span class="n">Load</span><span class="o">&lt;</span><span class="n">DebugInfo</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In addition to the index of the print buffer descriptor, I have the mouse cursor position in there as well since that&rsquo;s useful for &ldquo;print data for the pixel under the cursor&rdquo; scenarios. But you could add other things too: debug flags or floats that you can use in the shader without having to explicitly add new ones, frame indices or other program state, whatever you want.</p>
<h2 id="dealing-with-the-string-problem">Dealing With The String Problem</h2>
<p>Now that we have access to our magic debug info buffer which then gives us access to our print buffer, we can start building up the functionality needed to write data into that buffer. This is where things unfortunately get rather dicey in HLSL, which has no native support for working with strings or even a <code>char</code> type. The SPIR-V printf kinda works around this, since you only pass a literal to a <code>printf</code> intrinsic and then the compiler handles the rest. But this does not help us at all if we need to do our own processing of the string, which we need to do for our home-grown printf implementation. One workaround I&rsquo;ve used myself and seen used elsewhere is to declare <code>uint</code> arrays of character literals, and pass those around. This works nicely enough with existing language support, but it&rsquo;s about as ugly as you&rsquo;d imagine:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">const</span> <span class="n">uint</span> <span class="n">printStr</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="sc">&#39;T&#39;</span><span class="p">,</span> <span class="sc">&#39;h&#39;</span><span class="p">,</span> <span class="sc">&#39;i&#39;</span><span class="p">,</span> <span class="sc">&#39;s&#39;</span><span class="p">,</span> <span class="sc">&#39; &#39;</span><span class="p">,</span> <span class="sc">&#39;i&#39;</span><span class="p">,</span> <span class="sc">&#39;s&#39;</span><span class="p">,</span> <span class="sc">&#39; &#39;</span><span class="p">,</span> <span class="sc">&#39;a&#39;</span><span class="p">,</span> <span class="sc">&#39; &#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                          <span class="sc">&#39;t&#39;</span><span class="p">,</span> <span class="sc">&#39;e&#39;</span><span class="p">,</span> <span class="sc">&#39;r&#39;</span><span class="p">,</span> <span class="sc">&#39;r&#39;</span><span class="p">,</span> <span class="sc">&#39;i&#39;</span><span class="p">,</span> <span class="sc">&#39;b&#39;</span><span class="p">,</span> <span class="sc">&#39;l&#39;</span><span class="p">,</span> <span class="sc">&#39;e&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                          <span class="sc">&#39; &#39;</span><span class="p">,</span> <span class="sc">&#39;w&#39;</span><span class="p">,</span> <span class="sc">&#39;a&#39;</span><span class="p">,</span> <span class="sc">&#39;y&#39;</span><span class="p">,</span> <span class="sc">&#39; &#39;</span><span class="p">,</span> <span class="sc">&#39;t&#39;</span><span class="p">,</span> <span class="sc">&#39;o&#39;</span><span class="p">,</span> <span class="sc">&#39; &#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                          <span class="sc">&#39;w&#39;</span><span class="p">,</span> <span class="sc">&#39;r&#39;</span><span class="p">,</span> <span class="sc">&#39;i&#39;</span><span class="p">,</span> <span class="sc">&#39;t&#39;</span><span class="p">,</span> <span class="sc">&#39;e&#39;</span><span class="p">,</span> <span class="sc">&#39; &#39;</span><span class="p">,</span> <span class="sc">&#39;a&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                          <span class="sc">&#39; &#39;</span><span class="p">,</span> <span class="sc">&#39;s&#39;</span><span class="p">,</span> <span class="sc">&#39;t&#39;</span><span class="p">,</span> <span class="sc">&#39;r&#39;</span><span class="p">,</span> <span class="sc">&#39;i&#39;</span><span class="p">,</span> <span class="sc">&#39;n&#39;</span><span class="p">,</span> <span class="sc">&#39;g&#39;</span><span class="p">,</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">DebugPrint</span><span class="p">(</span><span class="n">printStr</span><span class="p">);</span>
</span></span></code></pre></div><p>While this is not unworkable, it&rsquo;s very ugly and takes significantly longer to write strings this way. You could potentially use editor tooling to help alleviate this&hellip; in fact I used to have a Sublime Text add-in that I wrote which could convert back and forth between a string literal and an array of char literals. But that&rsquo;s at best a band aid.</p>
<p>Another alternative would be to write or use some kind of custom pre-processor that can extract the string literals from the shader and replace them with something else. This could seamlessly replace a literal with that <code>uint</code> array monstrosity above. Or you could potentially take it even further and replace the string with some kind of hash or token, and then store the string somewhere accessible on the CPU side so that it can be looked up when the print buffer is read back and resolved. That last approach could potentially be quite a bit more efficient on the shader side by drastically reducing the amount of data that needs to be written to the buffer, but it is more complicated. In particular adding any kind of shader pre-processing is a heavy-handed step, and the decision to take on the associated burdens should not be treated lightly. Some engines have already chosen to do this for other reasons, in which case adding string literal handling would be a smaller incremental cost.</p>
<p>Really neither of these solutions are ideal, particularly for a smaller codebase being used for experimenting or messing around. I filed a <a href="https://github.com/microsoft/DirectXShaderCompiler/issues/4015">GitHub issue on the DXC repo</a> with a request for better string support back in October 2021, but as of January 2024 it still has not had a resolution.</p>
<h3 id="a-cursed-path">A Cursed Path</h3>
<p><em>Update 05/04/2025: as of the February 2025 release of DXC, the compiler will no longer let you pass a string literal as an argument for a templated function. This new error message was a <a href="https://github.com/microsoft/DirectXShaderCompiler/issues/6877">fix</a> for the below code pattern causing crashes in SPIR-V codegen. That means the StrLen function in the code below no longer works. As a workaround, the for loop on the character count can be replaced with a <a href="https://github.com/TheRealMJP/EarlyZTest/blob/05fa5d8fb42da9f95ef44a66a127bfc676d0ba02/SampleFramework12/v1.04/Shaders/ShaderDebug.hlsl#L425">while loop that checks for the null terminator</a>. Either way, please comment on <a href="https://github.com/microsoft/hlsl-specs/issues/279">this feature request</a> to indicate your support if you care about strings in HLSL. Original article contents follow.</em></p>
<p><em>Before I go any further I&rsquo;ll warn that this code is going to be ugly, and is almost certainly relying on unintended functionality in the compiler that just happened to be exposed through templates. It&rsquo;s very possible this won&rsquo;t work in the future, so I would not rely on this for anything critical and perhaps it would be safest to avoid it altogether. You&rsquo;ve been warned!</em></p>
<p>While experimenting with <a href="https://devblogs.microsoft.com/directx/announcing-hlsl-2021/">HLSL 2021</a>, I stumbled upon a way to use a combination of template and macro hacks to work with string literals and extract the characters as integers. It turns out that literals can be passed to templated functions that expect an array of type <code>T</code>, where <code>T</code> is ultimately <code>char</code> even though that type is intentionally not exposed. While indexing into that array doesn&rsquo;t seem to work, we can make a simple <code>strlen</code> implementation that just uses the size of the array to determine the length of the string literal:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="n">uint</span> <span class="n">N</span><span class="o">&gt;</span> <span class="n">uint</span> <span class="n">StrLen</span><span class="p">(</span><span class="n">T</span> <span class="n">str</span><span class="p">[</span><span class="n">N</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Includes the null terminator
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="n">N</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Nice! As for indexing into the characters of the literal, while we can&rsquo;t do that from a templated function we <em>can</em> do it by indexing into the literal itself. For example, <code>&quot;Hello&quot;[2]</code>. This means we have to resort to a loop in a macro, but for me this is an acceptable cost of doing business. But we still have one more problem: because of the way that <code>char</code> has been disabled inside of DXC, you can&rsquo;t actually do useful things with it. This includes any arithmetic, or even casting it to an <code>int</code> or <code>uint</code>. However it turns that there is <em>one</em> thing you can do with it, which is compare it to another character literal. Therefore <code>&quot;Hello&quot;[2] == 'l'</code> will evaluate to <code>true</code>. While this isn&rsquo;t immediately useful, it <em>does</em> mean we can write the world&rsquo;s most cursed <code>CharToUint</code> function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span> <span class="n">uint</span> <span class="n">CharToUint</span><span class="p">(</span><span class="n">in</span> <span class="n">T</span> <span class="n">c</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">&#39;A&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">65</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">&#39;B&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">66</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">&#39;C&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">67</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">&#39;D&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">68</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">&#39;E&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">69</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">&#39;F&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">70</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="sc">&#39;G&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">71</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// ...and about 90 more cases to handle
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span></code></pre></div><p>Putting it all together, we can finally make a macro that can process the string literal one at a time so that it can be packed into a buffer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#define DebugPrint(str, ...) do {                            \
</span></span></span><span class="line"><span class="cl"><span class="cp">    ShaderDebug::DebugPrinter printer;                       \
</span></span></span><span class="line"><span class="cl"><span class="cp">    printer.Init();                                          \
</span></span></span><span class="line"><span class="cl"><span class="cp">    const uint strLen = ShaderDebug::StrLen(str);            \
</span></span></span><span class="line"><span class="cl"><span class="cp">    for(uint i = 0; i &lt; strLen; ++i)                         \
</span></span></span><span class="line"><span class="cl"><span class="cp">        printer.AppendChar(ShaderDebug::CharToUint(str[i])); \
</span></span></span><span class="line"><span class="cl"><span class="cp">    printer.StringSize = printer.ByteCount;                  \
</span></span></span><span class="line"><span class="cl"><span class="cp">    printer.AppendArgs(__VA_ARGS__);                         \
</span></span></span><span class="line"><span class="cl"><span class="cp">    printer.Commit(ShaderDebug::GetDebugInfo());             \
</span></span></span><span class="line"><span class="cl"><span class="cp">} while(0)
</span></span></span></code></pre></div><p>Amazing? Terrible? Helpful? Morally dangerous? I&rsquo;m honestly not sure, you&rsquo;ll have to decide for yourself.</p>
<h2 id="packing-it-all-into-a-buffer">Packing It All Into A Buffer</h2>
<p>Now that we&rsquo;ve covered strings, we&rsquo;re ready to look how the string and any arguments get packed into our print buffer. You may have noticed that in my <code>DebugPrint</code> macro, I&rsquo;ve got a <code>DebugPrinter</code> type that&rsquo;s doing most of the heavy lifting. Let&rsquo;s hook at how that&rsquo;s written:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">DebugPrinter</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="k">const</span> <span class="n">uint</span> <span class="n">BufferSize</span> <span class="o">=</span> <span class="mi">256</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="k">const</span> <span class="n">uint</span> <span class="n">BufferSizeInBytes</span> <span class="o">=</span> <span class="n">BufferSize</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uint</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">InternalBuffer</span><span class="p">[</span><span class="n">BufferSize</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">ByteCount</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">StringSize</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">ArgCount</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">Init</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span><span class="p">(</span><span class="n">uint</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">BufferSize</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">InternalBuffer</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">ByteCount</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">StringSize</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">ArgCount</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="nf">CurrBufferIndex</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">ByteCount</span> <span class="o">/</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="nf">CurrBufferShift</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span><span class="n">ByteCount</span> <span class="o">%</span> <span class="mi">4</span><span class="p">)</span> <span class="o">*</span> <span class="mi">8</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">AppendChar</span><span class="p">(</span><span class="n">uint</span> <span class="n">c</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span><span class="p">(</span><span class="n">ByteCount</span> <span class="o">&lt;</span> <span class="n">BufferSizeInBytes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">InternalBuffer</span><span class="p">[</span><span class="n">CurrBufferIndex</span><span class="p">()]</span> <span class="o">|=</span> <span class="p">((</span><span class="n">c</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">CurrBufferShift</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">            <span class="n">ByteCount</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span></code></pre></div><p>Basically we have an internal buffer of <code>uint</code> that we use to store the converted characters, packed as 1 of the 4 bytes in the <code>uint</code>. As we loop over the string literal, we just keep incrementing <code>ByteCount</code> and packing in the data. However for a <code>printf</code> we also want to be able to handle arguments, that way we can print integers and floats and the like. For those we will also pack the data into the internal buffer, but we will also store a special code before each argument to provide the CPU code with the type and size of the argument:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">ArgCode</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DebugPrint_Uint</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DebugPrint_Uint2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DebugPrint_Uint3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DebugPrint_Uint4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DebugPrint_Int</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DebugPrint_Int2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DebugPrint_Int3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DebugPrint_Int4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DebugPrint_Float</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DebugPrint_Float2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DebugPrint_Float3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">DebugPrint_Float4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">NumDebugPrintArgCodes</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="n">uint</span> <span class="n">N</span><span class="o">&gt;</span> <span class="kt">void</span> <span class="n">AppendArgWithCode</span><span class="p">(</span><span class="n">ArgCode</span> <span class="n">code</span><span class="p">,</span> <span class="n">T</span> <span class="n">arg</span><span class="p">[</span><span class="n">N</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">ByteCount</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span> <span class="o">&gt;</span> <span class="n">BufferSizeInBytes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">ArgCount</span> <span class="o">&gt;=</span> <span class="n">MaxDebugPrintArgs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">AppendChar</span><span class="p">(</span><span class="n">code</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span><span class="p">(</span><span class="n">uint</span> <span class="n">elem</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">elem</span> <span class="o">&lt;</span> <span class="n">N</span><span class="p">;</span> <span class="o">++</span><span class="n">elem</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span><span class="p">(</span><span class="n">uint</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">b</span> <span class="o">&lt;</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">T</span><span class="p">);</span> <span class="o">++</span><span class="n">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">AppendChar</span><span class="p">(</span><span class="n">asuint</span><span class="p">(</span><span class="n">arg</span><span class="p">[</span><span class="n">elem</span><span class="p">])</span> <span class="o">&gt;&gt;</span> <span class="p">(</span><span class="n">b</span> <span class="o">*</span> <span class="mi">8</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">ArgCount</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>To make things work nicely with the arguments passed to the macro, we have some trampoline functions that append each argument individually and pass the correct code to <code>AppendArgWithCode</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">AppendArg</span><span class="p">(</span><span class="n">uint</span> <span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="n">x</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="n">AppendArgWithCode</span><span class="p">(</span><span class="n">DebugPrint_Uint</span><span class="p">,</span> <span class="n">a</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">AppendArg</span><span class="p">(</span><span class="n">uint2</span> <span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">a</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="n">x</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="n">x</span><span class="p">.</span><span class="n">y</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="n">AppendArgWithCode</span><span class="p">(</span><span class="n">DebugPrint_Uint2</span><span class="p">,</span> <span class="n">a</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">AppendArg</span><span class="p">(</span><span class="n">uint3</span> <span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">a</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="n">x</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="n">x</span><span class="p">.</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">.</span><span class="n">z</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="n">AppendArgWithCode</span><span class="p">(</span><span class="n">DebugPrint_Uint3</span><span class="p">,</span> <span class="n">a</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// More of these for floats, signed integers, etc.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">AppendArgs</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T0</span><span class="o">&gt;</span> <span class="kt">void</span> <span class="n">AppendArgs</span><span class="p">(</span><span class="n">T0</span> <span class="n">arg0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">AppendArg</span><span class="p">(</span><span class="n">arg0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T0</span><span class="p">,</span> <span class="k">typename</span> <span class="n">T1</span><span class="o">&gt;</span> <span class="kt">void</span> <span class="n">AppendArgs</span><span class="p">(</span><span class="n">T0</span> <span class="n">arg0</span><span class="p">,</span> <span class="n">T1</span> <span class="n">arg1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">AppendArg</span><span class="p">(</span><span class="n">arg0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">AppendArg</span><span class="p">(</span><span class="n">arg1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T0</span><span class="p">,</span> <span class="k">typename</span> <span class="n">T1</span><span class="p">,</span> <span class="k">typename</span> <span class="n">T2</span><span class="o">&gt;</span> <span class="kt">void</span> <span class="n">AppendArgs</span><span class="p">(</span><span class="n">T0</span> <span class="n">arg0</span><span class="p">,</span> <span class="n">T1</span> <span class="n">arg1</span><span class="p">,</span> <span class="n">T2</span> <span class="n">arg2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">AppendArg</span><span class="p">(</span><span class="n">arg0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">AppendArg</span><span class="p">(</span><span class="n">arg1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">AppendArg</span><span class="p">(</span><span class="n">arg2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// More of these for higher arg counts
</span></span></span></code></pre></div><p>This allows the macro to just do <code>printer.AppendArgs(__VA_ARGS__);</code> and it all works.</p>
<p>Finally we have one last <code>Commit()</code> method on <code>DebugPrinter</code> that actually stores everything into the <code>RWByteAddressBuffer</code> along with a special header:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">Commit</span><span class="p">(</span><span class="n">in</span> <span class="n">DebugInfo</span> <span class="n">debugInfo</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">ByteCount</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Round up to the next multiple of 4 since we work with 4-byte alignment for each print
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">ByteCount</span> <span class="o">=</span> <span class="p">((</span><span class="n">ByteCount</span> <span class="o">+</span> <span class="mi">3</span><span class="p">)</span> <span class="o">/</span> <span class="mi">4</span><span class="p">)</span> <span class="o">*</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">RWByteAddressBuffer</span> <span class="n">printBuffer</span> <span class="o">=</span> <span class="n">ResourceDescriptorHeap</span><span class="p">[</span><span class="n">debugInfo</span><span class="p">.</span><span class="n">PrintBuffer</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Increment the atomic counter to allocate space to store the bytes
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">const</span> <span class="n">uint</span> <span class="n">numBytesToWrite</span> <span class="o">=</span> <span class="n">ByteCount</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">DebugPrintHeader</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">offset</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">printBuffer</span><span class="p">.</span><span class="n">InterlockedAdd</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">numBytesToWrite</span><span class="p">,</span> <span class="n">offset</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Account for the atomic counter at the beginning of the buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">offset</span> <span class="o">+=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uint</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">((</span><span class="n">offset</span> <span class="o">+</span> <span class="n">numBytesToWrite</span><span class="p">)</span> <span class="o">&gt;</span> <span class="n">debugInfo</span><span class="p">.</span><span class="n">PrintBufferSize</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Store the header
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">DebugPrintHeader</span> <span class="n">header</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">header</span><span class="p">.</span><span class="n">NumBytes</span> <span class="o">=</span> <span class="n">ByteCount</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">header</span><span class="p">.</span><span class="n">StringSize</span> <span class="o">=</span> <span class="n">StringSize</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">header</span><span class="p">.</span><span class="n">NumArgs</span> <span class="o">=</span> <span class="n">ArgCount</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">printBuffer</span><span class="p">.</span><span class="n">Store</span><span class="o">&lt;</span><span class="n">DebugPrintHeader</span><span class="o">&gt;</span><span class="p">(</span><span class="n">offset</span><span class="p">,</span> <span class="n">header</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">offset</span> <span class="o">+=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">DebugPrintHeader</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Store the buffer data
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span><span class="p">(</span><span class="n">uint</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">ByteCount</span> <span class="o">/</span> <span class="mi">4</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">printBuffer</span><span class="p">.</span><span class="n">Store</span><span class="p">(</span><span class="n">offset</span> <span class="o">+</span> <span class="p">(</span><span class="n">i</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uint</span><span class="p">)),</span> <span class="n">InternalBuffer</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In <code>Commit()</code> we&rsquo;re assuming there&rsquo;s a counter at the start of the print buffer that indicates how many bytes have been written to it. By atomically incrementing that counter we can safely &ldquo;allocate&rdquo; some space for our print data, and also ensure that enough space is left so that we can early-out if the buffer is full. Once the counter is incremented we fill out our small header and write that first, then write the contents of the internal buffer one <code>uint</code> at a time.</p>
<p>While there&rsquo;s some complexity and ugliness here that we need to hide in an included file, actually doing a print in the shader code is about as easy as it gets:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">float3</span> <span class="n">color</span> <span class="o">=</span> <span class="n">ComputeColor</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">DebugPrint</span><span class="p">(</span><span class="s">&#34;The color is {0}&#34;</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span>
</span></span></code></pre></div><p>Since we&rsquo;re embedding the argument type as a code in the buffer, there&rsquo;s no need for printf-style format specifiers and we can instead use argument IDs like in <a href="https://en.cppreference.com/w/cpp/utility/format/format">std::format</a> or C#&rsquo;s <a href="https://learn.microsoft.com/en-us/dotnet/api/system.string.format?view=net-8.0">String.Format</a>.</p>
<h2 id="reading-back-on-the-cpu">Reading Back On The CPU</h2>
<p>In order to have the CPU process all of the print data that&rsquo;s been generated on the GPU, we need to issue some commands that copies the print buffer to a CPU-accessible readback buffer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">EndRender</span><span class="p">(</span><span class="n">ID3D12GraphicsCommandList7</span><span class="o">*</span> <span class="n">cmdList</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">PIXMarker</span> <span class="n">marker</span><span class="p">(</span><span class="n">cmdList</span><span class="p">,</span> <span class="s">&#34;ShaderDebug - EndRender&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">DX12</span><span class="o">::</span><span class="n">Barrier</span><span class="p">(</span><span class="n">cmdList</span><span class="p">,</span> <span class="n">PrintBuffer</span><span class="p">.</span><span class="n">WriteToReadBarrier</span><span class="p">(</span> <span class="p">{</span> <span class="p">.</span><span class="n">SyncAfter</span> <span class="o">=</span> <span class="n">D3D12_BARRIER_SYNC_COPY</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                                             <span class="p">.</span><span class="n">AccessAfter</span> <span class="o">=</span> <span class="n">D3D12_BARRIER_ACCESS_COPY_SOURCE</span> <span class="p">}</span> <span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">ReadbackBuffer</span><span class="o">&amp;</span> <span class="n">readbackBuffer</span> <span class="o">=</span> <span class="n">PrintReadbackBuffers</span><span class="p">[</span><span class="n">DX12</span><span class="o">::</span><span class="n">CurrentCPUFrame</span> <span class="o">%</span> <span class="n">DX12</span><span class="o">::</span><span class="n">RenderLatency</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">cmdList</span><span class="o">-&gt;</span><span class="n">CopyResource</span><span class="p">(</span><span class="n">readbackBuffer</span><span class="p">.</span><span class="n">Resource</span><span class="p">,</span> <span class="n">PrintBuffer</span><span class="p">.</span><span class="n">Resource</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Doing the copy to the readback buffer allows us to map and read the oldest generated print buffer data every frame so that we can process it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">DX12</span><span class="o">::</span><span class="n">CurrentCPUFrame</span> <span class="o">&gt;=</span> <span class="n">DX12</span><span class="o">::</span><span class="n">RenderLatency</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">ReadbackBuffer</span><span class="o">&amp;</span> <span class="n">readbackBuffer</span> <span class="o">=</span> <span class="n">PrintReadbackBuffers</span><span class="p">[(</span><span class="n">DX12</span><span class="o">::</span><span class="n">CurrentCPUFrame</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="n">DX12</span><span class="o">::</span><span class="n">RenderLatency</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">DebugPrintReader</span> <span class="nf">printReader</span><span class="p">(</span><span class="n">readbackBuffer</span><span class="p">.</span><span class="n">Map</span><span class="o">&lt;</span><span class="n">uint8</span><span class="o">&gt;</span><span class="p">(),</span> <span class="n">uint32</span><span class="p">(</span><span class="n">readbackBuffer</span><span class="p">.</span><span class="n">Size</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">while</span><span class="p">(</span><span class="n">printReader</span><span class="p">.</span><span class="n">HasMoreData</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">DebugPrintHeader</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">const</span> <span class="n">DebugPrintHeader</span> <span class="n">header</span> <span class="o">=</span> <span class="n">printReader</span><span class="p">.</span><span class="n">Consume</span><span class="o">&lt;</span><span class="n">DebugPrintHeader</span><span class="o">&gt;</span><span class="p">(</span><span class="n">DebugPrintHeader</span><span class="p">{});</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span><span class="p">(</span><span class="n">header</span><span class="p">.</span><span class="n">NumBytes</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">printReader</span><span class="p">.</span><span class="n">HasMoreData</span><span class="p">(</span><span class="n">header</span><span class="p">.</span><span class="n">NumBytes</span><span class="p">)</span> <span class="o">==</span> <span class="nb">false</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">formatStr</span> <span class="o">=</span> <span class="n">printReader</span><span class="p">.</span><span class="n">ConsumeString</span><span class="p">(</span><span class="n">header</span><span class="p">.</span><span class="n">StringSize</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span><span class="p">(</span><span class="n">formatStr</span><span class="p">.</span><span class="n">length</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span><span class="p">(</span><span class="n">header</span><span class="p">.</span><span class="n">NumArgs</span> <span class="o">&gt;</span> <span class="n">MaxDebugPrintArgs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">argStrings</span><span class="p">.</span><span class="n">Reserve</span><span class="p">(</span><span class="n">header</span><span class="p">.</span><span class="n">NumArgs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span><span class="p">(</span><span class="n">uint32</span> <span class="n">argIdx</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">argIdx</span> <span class="o">&lt;</span> <span class="n">header</span><span class="p">.</span><span class="n">NumArgs</span><span class="p">;</span> <span class="o">++</span><span class="n">argIdx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">const</span> <span class="n">ArgCode</span> <span class="n">argCode</span> <span class="o">=</span> <span class="p">(</span><span class="n">ArgCode</span><span class="p">)</span><span class="n">printReader</span><span class="p">.</span><span class="n">Consume</span><span class="o">&lt;</span><span class="n">uint8</span><span class="o">&gt;</span><span class="p">(</span><span class="mh">0xFF</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span><span class="p">(</span><span class="n">argCode</span> <span class="o">&gt;=</span> <span class="n">NumDebugPrintArgCodes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">const</span> <span class="n">uint32</span> <span class="n">argSize</span> <span class="o">=</span> <span class="n">ArgCodeSizes</span><span class="p">[</span><span class="n">argCode</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span><span class="p">(</span><span class="n">printReader</span><span class="p">.</span><span class="n">HasMoreData</span><span class="p">(</span><span class="n">argSize</span><span class="p">)</span> <span class="o">==</span> <span class="nb">false</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">argStr</span> <span class="o">=</span> <span class="n">MakeArgString</span><span class="p">(</span><span class="n">printReader</span><span class="p">,</span> <span class="n">argCode</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">ReplaceStringInPlace</span><span class="p">(</span><span class="n">formatStr</span><span class="p">,</span> <span class="n">ArgPlaceHolders</span><span class="p">[</span><span class="n">argIdx</span><span class="p">],</span> <span class="n">argStr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">GlobalApp</span><span class="o">-&gt;</span><span class="n">AddToLog</span><span class="p">(</span><span class="n">formatStr</span><span class="p">.</span><span class="n">c_str</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">readbackBuffer</span><span class="p">.</span><span class="n">Unmap</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The processing involves a loop that iterates until all of the print data in the buffer has been processed. For each iteration of the loop, we pull out a <code>DebugPrintHeader</code> that marks the beginning of a single <code>DebugPrint</code> call from a single thread of a shader. That header is then used to gather the actual format string, as well as the number of argument. Each argument is extracted using the code we embedded on the shader side, and a stringified version of the argument is then inserted into the original print string to replace the argument index. The final expanded string with arguments is then passed on to my simple log system, but at that point you could do whatever you&rsquo;d like with it. In my case the log outputs to a simple ImGui text window, but you can get as fancy as you want.</p>
<h2 id="going-beyond-printf">Going Beyond Printf</h2>
<p>Having a shader printf is already hugely valuable and worthwhile, but once you have this sort of infrastructure in place there&rsquo;s all kinds of things you can do with it. For example you could build a higher-level assert mechanism on top of <code>DebugPrint</code> that logs information when a condition isn&rsquo;t met. The <code>__FILE__</code> and <code>__LINE__</code> predefined macros work in DXC, so you could include that along with the condition itself in the log message. Unfortunately there&rsquo;s no way I know of to get any kind of &ldquo;stack strace&rdquo;, so you&rsquo;re on your own for that. There also isn&rsquo;t a standard <code>__debugbreak()</code> intrinsic or anything like that for shaders. The best you can do is enter an infinite loop or <a href="https://github.com/sawickiap/ShaderCrashingAssert">force a deliberate page fault</a>, and hope that your GPU crash reporting systems catch it appropriately.</p>
<p>Another common trick is to implement shader-driven debug rendering. If you have a CPU-based debug renderer then it&rsquo;s pretty straightforward to add special &ldquo;print&rdquo; code that can be detected on the CPU, which can then extract and forward the draw arguments to the debug renderer. The main downside of doing it this way is that you&rsquo;ll need to accept a few frames of latency between when the shader draws and when it shows up. For a lot of cases this is totally fine, but it can be a dealbreaker for certain debugging scenarios. A zero-latency alternative is to have a separate buffer for debug draws, and run a compute shader to process that and <a href="https://www.gijskaerts.com/wordpress/?p=190">convert it into indirect draw commands</a>.</p>
<p>Once you take it to the extreme, your print system can basically be a deferred message passing interface between your shaders and the engine. This could potentially be used for all kinds of powerful or wacky things if you&rsquo;re sufficiently motivated. For example, you could replicate a subset of the Dear ImGui interface in your shader, <a href="https://twitter.com/dieter_debaets/status/1742201258398609786">and give your shaders the ability to draw their own debug UI</a>. The sky is the limit!</p>
<h2 id="cr-lf">CR LF</h2>
<p>That&rsquo;s it for the article! Hopefully what I&rsquo;ve described is helpful for people implementing similar systems, or provides some ideas for how to improve existing ones. Good luck, and happy printing!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>HLSL has actually had a <a href="https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/printf">printf intrinsic</a> since the D3D11 days. Unfortunately no driver or tool has ever supported it outside of the old software REF device, which was really only used for validation.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
		</item>
		
		<item>
			<title>GPU Memory Pools in D3D12</title>
			<link>https://therealmjp.github.io/posts/gpu-memory-pool/</link>
			<pubDate>Sun, 31 Jul 2022 00:00:00 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/gpu-memory-pool/</guid>
			<description>Basics of GPU Memory Integrated/UMA GPUs Dedicated/NUMA GPUs How It Works In D3D12 Common Patterns in D3D12 Textures And The Two-Step Upload Should We Upload Buffers? Working With The COPY Queue Two COPY Queues Are Better Than One? Allocating Staging Memory What About DirectStorage? Results From My Testing App CPU Write Performance CPU Read Performance GPU Read Performance, Normal Access GPU Read Performance, Non-Coalesced Access GPU Read Performance, Various Buffer Sizes Conclusion When the monkey&amp;rsquo;s paw granted our wish for lower-level/explicit graphics APIs, one of the consequences was that we were much more directly exposed to the fact that GPUs can have their own separate set of physical memory.</description>
			<content:encoded><![CDATA[<div>
    <nav id="TableOfContents">
  <ul>
    <li><a href="#basics-of-gpu-memory">Basics of GPU Memory</a>
      <ul>
        <li><a href="#integrateduma-gpus">Integrated/UMA GPUs</a></li>
        <li><a href="#dedicatednuma-gpus">Dedicated/NUMA GPUs</a></li>
      </ul>
    </li>
    <li><a href="#how-it-works-in-d3d12">How It Works In D3D12</a></li>
    <li><a href="#common-patterns-in-d3d12">Common Patterns in D3D12</a>
      <ul>
        <li><a href="#textures-and-the-two-step-upload">Textures And The Two-Step Upload</a></li>
        <li><a href="#should-we-upload-buffers">Should We Upload Buffers?</a></li>
        <li><a href="#working-with-the-copy-queue">Working With The COPY Queue</a></li>
        <li><a href="#two-copy-queues-are-better-than-one">Two COPY Queues Are Better Than One?</a></li>
        <li><a href="#allocating-staging-memory">Allocating Staging Memory</a></li>
        <li><a href="#what-about-directstorage">What About DirectStorage?</a></li>
      </ul>
    </li>
    <li><a href="#results-from-my-testing-app">Results From My Testing App</a>
      <ul>
        <li><a href="#cpu-write-performance">CPU Write Performance</a></li>
        <li><a href="#cpu-read-performance">CPU Read Performance</a></li>
        <li><a href="#gpu-read-performance-normal-access">GPU Read Performance, Normal Access</a></li>
        <li><a href="#gpu-read-performance-non-coalesced-access">GPU Read Performance, Non-Coalesced Access</a></li>
        <li><a href="#gpu-read-performance-various-buffer-sizes">GPU Read Performance, Various Buffer Sizes</a></li>
      </ul>
    </li>
    <li><a href="#conclusion">Conclusion</a></li>
  </ul>
</nav>
</div>
<p>When the <a href="https://en.wikipedia.org/wiki/The_Monkey%27s_Paw">monkey&rsquo;s paw</a> granted our wish for lower-level/explicit graphics APIs, one of the consequences was that we were much more directly exposed to the fact that GPUs can have their own separate set of physical memory. Anybody that&rsquo;s ever had to purchase a dedicated video card for their PC can probably tell you that such a video card has its own set of physical RAM chips on the board that are reserved for the GPU. Meanwhile, the integrated GPUs often used in laptops and mobile devices will typically share one set of RAM with the CPU. This reality can make things complicated for a graphics programmer, since you potentially have two different physical pools of &ldquo;GPU memory&rdquo; that will have different characteristics depending on how you&rsquo;re using it. Earlier APIs such as D3D11 and OpenGL <em>sort of</em> exposed these pools by abstracting away GPU memory altogether behind different resource types, but in D3D12 and Vulkan you are now more-or-less directly exposed to lower-level allocation and management of GPU memory. Altogether it&rsquo;s complicated enough to cause confusion and headaches for beginners or even experienced graphics programmers, and it tends to come up rather early since you usually need to grapple with it before you can create your first texture.</p>
<p>In this article we&rsquo;re going to dive in on this topic, and in particular cover the following things:</p>
<ul>
<li>The basics of GPU memory</li>
<li>How GPU memory works in D3D12</li>
<li>Common patterns in D3D12</li>
<li>Some timing results gathered from a D3D12 test app</li>
</ul>
<p>Ultimately I&rsquo;m going to cover a lot of things that were already covered in some form by <a href="https://asawicki.info/">Adam Sawicki</a>&rsquo;s <a href="https://www.youtube.com/watch?v=ML0YC77bSOc">excellent talk</a> from Digital Dragons 2021 about optimizing for GPU memory pools. I would recommend watching that talk either way, but I&rsquo;m hoping that this article can complement that presentation by adding some extra details as well as some real-world benchmark results.</p>
<p>To keep this article somewhat focused, I&rsquo;m also only going to talk about D3D12 here and ignore other APIs. I would suggest reading <a href="https://asawicki.info/news_1740_vulkan_memory_types_on_pc_and_how_to_use_them">Adam&rsquo;s post about Vulkan memory types</a> if you&rsquo;re looking for information specific to Vulkan. You can also check out his <a href="https://asawicki.info/news_1755_untangling_direct3d_12_memory_heap_types_and_pools">later post</a> about D3D12 memory types and pools for a great overview of how memory pools are exposed through the D3D12 API.</p>
<h2 id="basics-of-gpu-memory">Basics of GPU Memory</h2>
<p>As I briefly mentioned in the intro, a particular GPU may or may not have its own physically separate pool of memory to use for storing whatever data it needs to access. In general this sort of &ldquo;split&rdquo; memory pool setup is something you see on the discrete GPUs found on video cards, whereas the integrated GPUs that are built onto the same die as the CPU will typically use the same physical memory as the CPU. To start, let&rsquo;s look at the latter case which is arguably simpler.</p>
<h3 id="integrateduma-gpus">Integrated/UMA GPUs</h3>
<p>So-called &ldquo;integrated&rdquo; GPUs are what you&rsquo;ll find built into many of the CPU offerings from Intel as well as the APU offerings from AMD, and are called that because the GPU is literally integrated into the same die as the CPU:</p>
<p><img src="/images/gpu-memory-pools/intel_integrated_die.jpg" alt="Intel Skylake die shot with integrated GPU"></p>
<p>These types of GPU are not a monolith, especially if you look at what you might see in phones, consoles, and Apple/Mac devices. However if you&rsquo;re looking at the Windows/PC world you can usually assume that the following is true:</p>
<ul>
<li>The CPU and the GPU share the same physical memory, and often the same memory controller</li>
<li>The RAM being used is of the DDR variety typically used for CPUs, as opposed to the more exotic GDDR chips found in video cards</li>
<li>The amount of raw bandwidth available to the GPU is perhaps an order of magnitude lower than what you might find on high-end dedicated graphics cards. As an example, the <a href="https://en.wikipedia.org/wiki/List_of_Intel_graphics_processing_units#Gen12">Gen12 GPUs</a> found in recent Tiger Lake and Alder Lake CPUs from Intel will range from about ~50 GB/s to ~76 GB/s. Meanwhile an <a href="https://en.wikipedia.org/wiki/Radeon_RX_6000_series#Desktop">AMD RX 6800</a> can achieve 512 GB/s to its dedicated Video RAM (VRAM), while an <a href="https://en.wikipedia.org/wiki/GeForce_30_series">Nvidia RTX 3090</a> peaks at a whopping 936 GB/s!</li>
</ul>
<p>Since the CPU and GPU share the same pool of physical memory, this setup is often referred to as a &ldquo;Unified Memory Architecture&rdquo;, or UMA for short. While it might seem at first that this is purely a disadvantage relative to a faster dedicated memory pool, this isn&rsquo;t entirely true. Sharing the memory pool and mechanisms for accessing that memory can make it easier for the CPU and GPU to work together: both processors can directly read and write to the same physical memory addresses, and perhaps even have a shared level of cache hierarchy. This can improve performance for CPU/GPU communication, and also remove certain cases of bugs. The other main advantage is that the system and its applications are free to partition the single pool of memory however they see fit, without having to instead manage two separate pools of memory that can only be used for a single processor.</p>
<h3 id="dedicatednuma-gpus">Dedicated/NUMA GPUs</h3>
<p>For a PC that has its own dedicated video card, the situation is of course very different: these video cards have their own bank of physical RAM chips right on the board. Since this RAM pool is physically separate from the CPU RAM, this setup is usually referred to as a &ldquo;Non-Unifed Memory Architecture&rdquo; or &ldquo;NUMA&rdquo; for short. Often this separate pool is accessed through its own memory controller that&rsquo;s present on the GPU, and can even have its own separate page tables and virtual address space.</p>
<p>As we mentioned before, dedicated VRAM is capable of achieiving significantly higher bandwidth than what you&rsquo;ll see for the DDR modules used by PC CPUs. The secret weapon here for VRAM is that they typically use exotic GDDR RAM chips that are specifically designed for high-bandwidth use cases, and these chips are then wired onto very wide 256-bit or even 384-bit buses. The chips, bus, and the GPU itself are all explicitly designed to handle the sort of wide bursts of coalesced memory accesses that you can expect from the 32-wide SIMD arrays that shader programs execute on.</p>
<p><img src="/images/gpu-memory-pools/numa.png" alt=""></p>
<p>Since the memory pools are physically split on a dedicated GPU setup, sharing data between the CPU and GPU is naturally much more complicated. For sharing to happen, everything needs to happen on the PCI-express bus that the video card is connected to. The GPU is capable of reading from CPU memory (SysRAM) over this bus, which it can do at around 16 GB/s for <a href="https://en.wikipedia.org/wiki/PCI_Express#PCI_Express_3.0">PCI Express 3.0</a> and around 32 GB/s with the newer PCI Express 4.0. 16 GB/s really isn&rsquo;t very much compared with the GPU&rsquo;s bandwidth to VRAM or even the CPU&rsquo;s bandwidth to SysRAM, which makes it a resource that has to be utilized rather carefully. A video card will usually have a dedicated DMA unit on board that is optimized for asynchronously &ldquo;uploading&rdquo; large chunks of texture or buffer data from SysRAM to VRAM. PCIe can also service general texture and buffer reads issued by the shader core, which can work out well for small buffers that are updated frequently and fit comfortably in caches. The CPU also has the ability to directly write to VRAM over the PCIe bus, through a mechanism known as &ldquo;<a href="https://en.wikipedia.org/wiki/PCI_configuration_space#Bus_enumeration">Base Address Register</a>&rdquo;, or BAR for short. Until recently the amount of VRAM accessible through BAR was limited to around 256 MB, limiting its usefulness to special case data that was carefully managed by drivers. More modern systems however can support a feature known as &ldquo;<a href="https://www.nvidia.com/en-us/geforce/news/geforce-rtx-30-series-resizable-bar-support/">Resizable BAR</a>&rdquo;, or ReBAR, which can make the entire pool of VRAM accessible from the CPU.</p>
<p>The fact that the CPU and GPU have their own memory controllers and cache hierarchies can cause further pain and complexity for CPU/GPU communication. PC video cards can typically <a href="https://en.wikipedia.org/wiki/Bus_snooping">snoop</a> the caches of the CPU when the CPU writes to GPU-visible SysRAM, however CPU and GPU cache hierarchies are still effectively seperate. This leads to something of a dilemma for the CPU when writing to memory that only the GPU will read from: does it make sense to leave caching enabled and &ldquo;pollute&rdquo; the cache with data from GPU, or would it make more sense to bypass the CPU cache entirely? The latter has historically been the default on PC, while making use of a hardware feature known as <a href="https://en.wikipedia.org/wiki/Write_combining">write combining</a>. This feature effectively buffers nearby writes together so that each individual write doesn&rsquo;t need its own separate uncached bus transaction, which can work decently as long as you&rsquo;re doing fairly boring memcpy-style sequential writes. However this does nothing at all for you if you are unlucky enough to try to read from GPU-visible memory: the reads are completely uncached, and they will be <a href="https://fgiesen.wordpress.com/2013/01/29/write-combining-is-not-your-friend/">quite slow</a>. Things are even worse if you somehow read from VRAM over PCIe, which can be catostrophically slow. Enabling caching can avoid the performance penalty from accidental reads, however it may cause adverse performance for subsequent code by pushing important CPU data out of cache.</p>
<h2 id="how-it-works-in-d3d12">How It Works In D3D12</h2>
<p>D3D12 applications have to allocate their own memory, so how is all of this exposed to us? Adam Sawicki already gave a great overview of how everything works on <a href="https://asawicki.info/news_1755_untangling_direct3d_12_memory_heap_types_and_pools">his blog</a>, and there is also information in the <a href="https://docs.microsoft.com/en-us/windows/win32/direct3d12/memory-management">official programming guide for D3D12</a>. For completeness I&rsquo;ll provide a brief overview here as well, along with some additional information based on my own experiences.</p>
<p>In D3D12 you have two different ways to allocate GPU-accessible memory for your device: by <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createcommittedresource">creating a committed resource</a>, or by <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createheap">creating a heap</a>. Heaps are the more direct way of allocating and utilizing memory, where you must create a heap first with various properties and then &ldquo;place&rdquo; resources within that heap via <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createplacedresource">CreatePlacedResource</a>. Committed resources allocate memory indirectly: it&rsquo;s roughly the same as if the runtime/driver created a single resource-sized heap for you behind the scenes, and then placed the resource in that heap for you. For that reason, we will focus on heaps here instead of committed resources.</p>
<p>Once you&rsquo;ve created your device, you can query some feature bits in order to get some information in advance about the architecture and behavior of the GPU that you will be allocating memory for. In particular there are two fields of interest on <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_feature_data_architecture">D3D12_FEATURE_DATA_ARCHITECTURE</a>, which you obtain by calling <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-checkfeaturesupport">CheckFeatureSupport</a> with <code>D3D12_FEATURE_ARCHITECTURE</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">D3D12_FEATURE_DATA_ARCHITECTURE</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">UINT</span> <span class="n">NodeIndex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">BOOL</span> <span class="n">TileBasedRenderer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">BOOL</span> <span class="n">UMA</span><span class="p">;</span>                 <span class="c1">// &lt; -- 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">BOOL</span> <span class="n">CacheCoherentUMA</span><span class="p">;</span>    <span class="c1">// &lt; -- 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="n">D3D12_FEATURE_DATA_ARCHITECTURE</span><span class="p">;</span>
</span></span></code></pre></div><ol>
<li>The <code>UMA</code> flag tells you if the GPU uses a Unified Memory Architecture where a single physical pool of RAM is shared between the CPU and GPU, like what you&rsquo;d normally find on an integrated GPU</li>
<li>If <code>UMA</code> is true and <code>CacheCoherentUMA</code> is also true, then the CPU and GPU caches are fully coherent with each other. In this situation using uncached writes for filling GPU-visible SysRAM does not make sense, since the GPU can benefit from data being resident in cache.</li>
</ol>
<p>In a bit we&rsquo;ll talk about how these flags affect the behavior of a device and ways an app can respond to them, but first let&rsquo;s look at the lowest-level mechanism for creating a heap: creating a &ldquo;custom&rdquo; heap. Custom heaps allow the app to fully specify all of the supported attributes of a heap, which is done through these two structs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">D3D12_HEAP_DESC</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">UINT64</span> <span class="n">SizeInBytes</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3D12_HEAP_PROPERTIES</span> <span class="n">Properties</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">UINT64</span> <span class="n">Alignment</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3D12_HEAP_FLAGS</span> <span class="n">Flags</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">D3D12_HEAP_DESC</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">D3D12_HEAP_PROPERTIES</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3D12_HEAP_TYPE</span> <span class="n">Type</span><span class="p">;</span>   <span class="c1">// Must be D3D12_HEAP_TYPE_CUSTOM
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">D3D12_CPU_PAGE_PROPERTY</span> <span class="n">CPUPageProperty</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3D12_MEMORY_POOL</span> <span class="n">MemoryPoolPreference</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">UINT</span> <span class="n">CreationNodeMask</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">UINT</span> <span class="n">VisibleNodeMask</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">D3D12_HEAP_PROPERTIES</span><span class="p">;</span>
</span></span></code></pre></div><p>First, there is <code>SizeInBytes</code> which is exactly what it sounds like. There is also an <code>Alignment</code> needed for satisfying alignment requirements for specific kinds of resources (in particular, MSAA textures). There is also a set of <code>D3D12_HEAP_FLAGS</code> that you can configure, but they are not generally relevant to our focus on memory pools and so we will ignore this. Instead, let&rsquo;s look at two of the interesting enums that you can specify in <code>D3D12_HEAP_PROPERTIES</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">enum</span> <span class="n">D3D12_MEMORY_POOL</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3D12_MEMORY_POOL_UNKNOWN</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>  <span class="c1">// Not relevant for custom heaps
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">D3D12_MEMORY_POOL_L0</span>  <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3D12_MEMORY_POOL_L1</span>  <span class="o">=</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">D3D12_MEMORY_POOL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">enum</span> <span class="n">D3D12_CPU_PAGE_PROPERTY</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3D12_CPU_PAGE_PROPERTY_UNKNOWN</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>    <span class="c1">// Not relevant for custom heaps
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3D12_CPU_PAGE_PROPERTY_WRITE_BACK</span>  <span class="o">=</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">D3D12_CPU_PAGE_PROPERTY</span><span class="p">;</span>
</span></span></code></pre></div><p>Now things are getting interesting! <code>D3D12_MEMORY_POOL</code> is exactly what it sounds like: it allows us to choose the physical pool of RAM that the memory will live in. On NUMA devices (dedicated video cards) L0 maps to SysRAM AKA CPU memory, while L1 maps to VRAM located on the video card. However on UMA devices there is only physical memory pool available, which is LO. L1 is not available on such devices, and attempting to use it will cause the CreateHeap call to fail. This choice of memory pool must also be carefully paired with an appropriate <code>D3D12_CPU_PAGE_PROPERTY</code> in order to play nice with the CPU&rsquo;s ability (or lack thereof) to access the memory pool. In practice it ends up working something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">UMA</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">MemoryPoolPreference</span> <span class="o">==</span> <span class="n">D3D12_MEMORY_POOL_L0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Memory will live SysRAM pool shared between CPU and GPU
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span><span class="p">(</span><span class="n">CPUPageProperty</span> <span class="o">==</span> <span class="n">D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// Not accessible to the CPU at all
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span> <span class="nf">if</span><span class="p">(</span><span class="n">CPUPageProperty</span> <span class="o">==</span> <span class="n">D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// Uncached for the CPU. Write combined for CPU writes, very very slow CPU reads.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// Ok for passing data from CPU -&gt; GPU.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span> <span class="nf">if</span><span class="p">(</span><span class="n">CPUPageProperty</span> <span class="o">==</span> <span class="n">D3D12_CPU_PAGE_PROPERTY_WRITE_BACK</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span><span class="p">(</span><span class="n">CacheCoherentUMA</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// CPU pages are cached, fast for writes and reads.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="c1">// GPU potentially benefits from data being in cache as well.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="c1">// Good for all purposes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">else</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// Cached for CPU. May not have ideal CPU performance when
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="c1">// compared with uncached/write-combined. Good for
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="c1">// reading back data from GPU -&gt; CPU
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="nf">if</span><span class="p">(</span><span class="n">MemoryPoolPreference</span> <span class="o">==</span> <span class="n">D3D12_MEMORY_POOL_L1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Error, can&#39;t use L1 on UMA!
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// NUMA device
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span><span class="p">(</span><span class="n">MemoryPoolPreference</span> <span class="o">==</span> <span class="n">D3D12_MEMORY_POOL_L0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Memory will live in the SysRAM pool
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span><span class="p">(</span><span class="n">CPUPageProperty</span> <span class="o">==</span> <span class="n">D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// Lives in SysRAM but the CPU can&#39;t access it, don&#39;t do this!
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span> <span class="nf">if</span><span class="p">(</span><span class="n">CPUPageProperty</span> <span class="o">==</span> <span class="n">D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// Uncached for the CPU. Write combined for CPU writes, very very slow CPU reads.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// GPU will read this data over PCIe at low speeds relative to VRAM.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// Good for passing data from CPU -&gt; GPU
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span> <span class="nf">if</span><span class="p">(</span><span class="n">CPUPageProperty</span> <span class="o">==</span> <span class="n">D3D12_CPU_PAGE_PROPERTY_WRITE_BACK</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// Cached for the CPU with full speed reads and writes.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// GPU will access this data over PCIe at low speeds relative to VRAM.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// Good for reading back data from GPU -&gt; CPU.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// Can be used for CPU -&gt; GPU traffic as well, but may pollute
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// the CPU cache with data that will only be read by the GPU.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="nf">if</span><span class="p">(</span><span class="n">MemoryPoolPreference</span> <span class="o">==</span> <span class="n">D3D12_MEMORY_POOL_L1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Memory will live in the high-bandwidth VRAM pool
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span><span class="p">(</span><span class="n">CPUPageProperty</span> <span class="o">==</span> <span class="n">D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// The CPU can&#39;t access this memory at all, only the GPU can.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// Good for data that only the GPU will ever read or write, such as render targets.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// Good for persistent, read-only resources but filling the data requires
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// a two-step upload process.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">CPUPageProperty</span> <span class="o">==</span> <span class="n">D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// Error, not valid for a NUMA device
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">CPUPageProperty</span> <span class="o">==</span> <span class="n">D3D12_CPU_PAGE_PROPERTY_WRITE_BACK</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// Error, not valid for a NUMA device
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This is naturally a bit complicated to manage all of this yourself, so to make things a bit easier D3D12 provides &ldquo;built-in&rdquo; heap types that are optimized for common use cases. These types are accessed by using the <code>D3D12_HEAP_TYPE_DEFAULT</code>, <code>D3D12_HEAP_TYPE_UPLOAD</code>, or <code>D3D12_HEAP_TYPE_READBACK</code> values of <code>D3D12_HEAP_TYPE</code> when creating the heap, and setting the <code>MemoryPoolPreference</code> to <code>D3D12_MEMORY_POOL_UNKNOWN</code> and <code>CPUPageProperty</code> to <code>D3D12_CPU_PAGE_PROPERTY_UNKNOWN</code>. Here&rsquo;s how they work:</p>
<ul>
<li><code>DEFAULT</code> is intended for resources that the GPU primarily accesses at full bandwidth. On NUMA this maps to L1 with no CPU access, while on UMA it maps to L0 with no CPU access.</li>
<li><code>UPLOAD</code> is intended for CPU -&gt; GPU communication where the CPU writes the data and the GPU reads it. On NUMA this maps to L0 with WRITE_COMBINE CPU access, while on UMA it maps to L0 with WRITE_COMBINE if CacheCoherentUMA is false, and WRITE_BACK if CacheCoherentUMA is true.</li>
<li><code>READBACK</code> is intended for GPU -&gt; CPU communication where the GPU writes the data and the CPU reads it. On both UMA and NUMA devices this maps to L0 with WRITE_BACK CPU access.</li>
</ul>
<p>One thing you may have noticed about this setup is that CPU-accessible VRAM is not currently exposed through any of these options. While the driver can use the small amount of BAR VRAM if it decides it&rsquo;s worthwhile, applications have no direct way of allocating from that special bit of memory. This is true even on newer machines with ReBAR support that makes the entire pool of VRAM accessible to the CPU. However, here is an NVAPI extension that allows you to <a href="https://docs.nvidia.com/gameworks/content/gameworkslibrary/coresdk/nvapi/group__dx.html#gac0e1d0851b25d2d956b13eaa64752bf9">query</a> and <a href="https://docs.nvidia.com/gameworks/content/gameworkslibrary/coresdk/nvapi/group__dx.html#ga1c24c3f5a5c6bf9dd3a35b660b4b95db">allocate</a> from CPU-accessible VRAM if the device was created on a supported Nvidia GPU. Meanwhile, AMD&rsquo;s driver can <a href="https://gpuopen.com/learn/get-the-most-out-of-smart-access-memory/">automatically promote allocations</a> to CPU-accessible VRAM based on heuristics and ReBAR support. Hopefully a future version of D3D12 adds support for this functionality, since it is certainly a useful option.</p>
<p>It&rsquo;s not necessarily obvious from reading the official documentation, but these various built-in and custom heap types come with limitations on resource usage that are either implicit or explicit depending on the exact use case. In general buffer resources are not limited: you can place them in any of the built-in heap types as well as any possible configuration of a custom heap, provided that the heap wasn&rsquo;t created with <code>D3D12_HEAP_FLAG_DENY_BUFFERS</code>. Buffers have an implied linear layout of the data that is similar to an array in C/C++, which makes it trivial for both CPU and GPU to access these resources. Textures however are special: these typically do <em>not</em> use a linear layout<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, but instead use a hardware-specific swizzle pattern that&rsquo;s designed for maximum spatial locality and cache efficiency. This isn&rsquo;t an issue if the GPU accesses the texture since it knows how to read/write to these special patterns, but it&rsquo;s a major issue if you wanted to access such a texture from the CPU: your app doesn&rsquo;t know the swizzle pattern, which means only the driver knows how to encode or decode it. Even if you could access the texture you likely wouldn&rsquo;t want to read it from SysRAM on a NUMA device, since the slow PCIe bandwidth could degrade performance. The built-in heaps use a heavy-handed means of guarding you from these issues by flat-out forbidding you from creating texture resources in an UPLOAD heap. Instead you must use a &ldquo;two-step&rdquo; upload scheme where the CPU writes un-swizzled data to a buffer in UPLOAD memory, and then the GPU itself <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12graphicscommandlist-copybufferregion">copies and swizzles</a> the data to a texture resource in DEFAULT memory.</p>
<p>With custom heaps, you can potentially bypass the texture/heap limitations but it must be done carefully. D3D12 will let you place a ROW_MAJOR texture resource in a CPU-writable L0 heap even on a NUMA device, which you can then fill from the CPU. But you should be prepared for excruciatingly poor performance if you try to do this, due poor spatial locality coupled with slow PCIe bandiwdth. A somewhat better option is to use the special <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12resource-writetosubresource">WriteToSubresource</a> function, which allows the driver to fill an UNKNOWN-layout texture using a hardware-specific swizzle pattern. This would likely still be too slow on NUMA hardware that reads from L0, however it can be compelling option for UMA devices that share a memory pool anyway. WriteToSubresource allows you to forego a GPU-side copy to initialize your resources, which is appealing since integrated GPUs often lack an asynchronous DMA for performing said copies. WriteToSubresource might one day be relevant for dedicated video cards if ReBAR becomes the norm an D3D12 exposes CPU-accessible VRAM, however it ultimately may not be desirable for apps to burn a CPU core on swizzling texture data.</p>
<p>If you want to keep an eye on how much memory your app is using from the two pools, <a href="https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_4/nf-dxgi1_4-idxgiadapter3-queryvideomemoryinfo">QueryVideoMemoryInfo</a> can be called from a <code>IDXGIAdapter3</code> to get the current usage of L0 and L1. To do this you just need to know that <code>DXGI_MEMORY_SEGMENT_GROUP_LOCAL</code> maps to L1, while <code>DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL</code> maps to L0. The OS-assigned budgets returned by this function are also useful to keep an eye on. In particular if you start to execeed the L1 budget, the OS can start to automatically &ldquo;demote&rdquo; some of your heaps from L1 to L0 in order to leave enough free VRAM for other processes on the system. If this demotion happens to the wrong resource it can destroy your performance, and so you really want to avoid that situation if you can help it. <a href="https://devblogs.microsoft.com/pix/system-monitor/">PIX for Windows</a> can also show you this information when attached to your app, which is handy. If demotion does occur you can mitigate the damage by <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device1-setresidencypriority">assigning a residency priority</a> on your heaps/resources. This can let you keep your more critical resources in VRAM by allowing the OS to demote less-important things.</p>
<h2 id="common-patterns-in-d3d12">Common Patterns in D3D12</h2>
<p>After discussing some of the specifics of the API, let&rsquo;s now talk about how D3D12 apps and samples typically work with memory pools.</p>
<h3 id="textures-and-the-two-step-upload">Textures And The Two-Step Upload</h3>
<p>To start, let&rsquo;s discuss textures since they are somewhat special. As we covered earlier, textures tend to be swizzled in an opaque hardware-specific layout while also being quite large and bandwidth-hungry. Therefore the common choice is to throw these guys in a DEFAULT heap, which maps to the high-bandwidth L1/VRAM pool on NUMA devices (and as we mentioned earlier, the runtime won&rsquo;t even let you create a texture in an UPLOAD heap). This puts us in our desired physical memory pool on both UMA and NUMA devices, but introduces a separate problem: the CPU cannot directly initialize the contents of a texture living in DEFAULT. Instead we must rely on what I call the <a href="https://docs.microsoft.com/en-us/windows/win32/direct3d12/upload-and-readback-of-texture-data">two-step upload process</a>, which goes something like this:</p>
<ol>
<li>Some memory from a buffer resource living in CPU-accessible memory (typically a UPLOAD heap) needs to be reserved, enough to fit the texture data being uploaded. This is sometimes called a &ldquo;staging&rdquo; buffer.</li>
<li>The CPU loads or copies un-swizzled texture data into the CPU-accessible buffer</li>
<li><a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12graphicscommandlist-copytextureregion">CopyTextureRegion</a> is called on a command list, one for each subresource in the texture (mip level or array slice)</li>
<li>The command list is submitted on a queue where it executes on the GPU, and proper synchronization is used to ensure tha the copy completes before another command attempts to read from the texture</li>
</ol>
<p>This process has the GPU effectively accomplish two goals: swizzling the texture data from a linear layout into a hardware-specific texel order, and getting the texture data into fast VRAM. Both are critical for performance, since textures are bandwidth heavy and rely on caching and 2D locality to keep things from slowing down too much. Neither of these are stricly recquired, since it&rsquo;s possible to allocate textures in SysRAM via custom heaps and you can also specify that a texture resource should use <code>D3D12_TEXTURE_LAYOUT_ROW_MAJOR</code> when creating it. However both of these can come with severe peformance penalties, so it is highly recommended to place textures in VRAM with <code>D3D12_TEXTURE_LAYOUT_UNKNOWN</code> (or <code>D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE</code> for tiled resources).</p>
<p>The two-step upload process might sound rather simple at first, but make no mistake: there are many potential paths to take and a lot of subtleties to consider when it comes to allocating staging memory and scheduling the actual copy/upload. We&rsquo;ll dig into that <a href="../gpu-memory-pool/#working-with-the-copy-queue">in a little bit</a>, but first let&rsquo;s discuss buffers.</p>
<h3 id="should-we-upload-buffers">Should We Upload Buffers?</h3>
<p>The two-step upload is a de-facto requirement for textures, but what about buffers? Unlike textures, buffers can be created in UPLOAD heaps and can be read directly from SysRAM by shaders and copy operations. But wouldn&rsquo;t they also benefit from being uploaded to VRAM? The answer to that is &ldquo;yes, sometimes&rdquo;. For large, read-only or rarely-updated buffers allocating them in VRAM and uploading their contents makes a ton of sense, and you can likely use the same mechanisms that you would use for initializing textures. The situation is slightly easier since you don&rsquo;t need to worry about CopyTextureRegion and RowPitch alignment requirements, but is otherwise mostly the same.</p>
<p>Things are different though for smaller, frequently-updated buffers. For example, a 64-byte constant buffer containing a per-draw world transform that you update every frame. In these cases the data can fit nicely in cache, so you&rsquo;re probably not losing very much performance (if any) by letting the GPU read it over PCIe. Also if you&rsquo;re only reading the constant buffer once, you&rsquo;re liable to actually make performance <em>worse</em> by copying from SysRAM to VRAM first. Therefore you can usually save yourself the headache for these kinds of small dynamic buffers, and just let them live in SysRAM. If the data is truly fire-and-forget and doesn&rsquo;t need to persist longer than the current frame being recorded, you can use really fast linear allocator patterns to <a href="https://github.com/TheRealMJP/DXRPathTracer/blob/sm_6_6/SampleFramework12/v1.02/Graphics/DX12_Upload.cpp#L532">quickly grab a bit of temporary UPLOAD memory</a> that&rsquo;s tied to frame lifetime.</p>
<p>Where things get trickier is buffers that are not small, are updated fairly frequently, and are accessed either more than once or with addresses that are unique for every thread of a draw or dispatch. For these cases you probably do want the full bandwidth of VRAM, but uploading their contents every frame can add additional costs and headaches that need to be considered. The simplest option of uploading the data syncronously via a CopyResource or CopyBufferRegion call on the main DIRECT/graphics queue gets the job done, but that introduces additional GPU time on that queue that has to be offset with the improved bandwidth afforded by VRAM. In the <a href="../gpu-memory-pool/#working-with-the-copy-queue">next section</a> we&rsquo;ll talk about more advanced scenarios for uploading data that can potentially result in better overall performance.</p>
<!-- Leftover from previous revision

For these cases you probably do want the full bandwidth of VRAM, but uploading them every single frame is perhaps not appealing if your uploader is tuned more for background resource loading/initialization. In these cases I've found you may want a sort-of hybrid approach: you want to quickly grab a bit of staging/UPLOAD memory that only needs to persist for the current frame, and then you want feed that into a batched COPY queue upload that's submitted once a frame. This gives you a fast path for getting the buffer data into VRAM that won't hold up critical rendering threads.

 -->
<p>Once CPU-accessible VRAM is ubiquitous through ReBAR, it seems plausible that nearly all CPU-updated buffers could live in VRAM and be directly updated from the CPU. This would simplify the situation considerably compared to what&rsquo;s required now. For integrated UMA GPUs that situation already exists: there&rsquo;s really no reason to do a two-step upload for buffer resources on these systems, and it would benefit engines to detect this case if they want to optimize for these GPUs.</p>
<h3 id="working-with-the-copy-queue">Working With The COPY Queue</h3>
<p>When discussing the two-step upload we mentioned how there are actually many choices and trade-offs to consider when setting up upload paths in your engine. In particular you need to choose how exactly you allocate/aquire the UPLOAD buffer used as the source of the copy, and you also need to decide how and when you submit your command list on a command queue. The official D3D12 samples use a <a href="https://github.com/microsoft/DirectX-Graphics-Samples/blob/master/Samples/Desktop/D3D12DynamicIndexing/src/D3D12DynamicIndexing.cpp#L272">very simple scheme</a> where an UPLOAD buffer is created as a committed resource and the copy is immediately submitted on a DIRECT command queue (via <a href="https://github.com/microsoft/DirectX-Headers/blob/main/include/directx/d3dx12.h#L2210">UpdateSubresources</a> from the d3dx12.h helper header) followed by an immediate CPU wait for the submission to finish. That&rsquo;s totally fine for simple use cases, but what if you have many uploads to do and you don&rsquo;t want to spend a ton of time creating and destroying committed resources? What if you need to stream in these textures in background while most of your CPU cores are busy and your GPU is already executing giant batches of rendering work on the DIRECT queue? What if there&rsquo;s a texture or buffer in VRAM that needs to be updated every single frame, and you don&rsquo;t want updating it to stall out one of your rendering threads? For these situations you probably want a more complex approach that better fits the use case.</p>
<p>In many cases it&rsquo;s ideal to use the dedicated COPY queue type to perform this kind of uploading. On NUMA devices this queue typically feeds to a dedicated DMA unit on the video card, which is specifically optimized for performing asynchronous copies of data over the PCIe bus. This not only means it may be more efficient than a copy performed on a DIRECT queue, but it also means that the actual copy operation can potentially run in parallel to other graphics and compute work running on the GPU! For first-time engine initialization this is not a big deal, but for background streaming/loading it&rsquo;s definitely a better option than forcing your critical per-frame drawing to wait for a bunch of uploads to finish executing (which is what will happen if you submit your copies to your main DIRECT queue). Async copies can still slow down your graphics work a bit by consuming some bandwidth, but this is generally still a better proposition than forcing your graphics work to wait for a bunch of slow PCIe copies to finish. To illustrate the difference, the images below are from two different GPU Trace captures taken with Nvidia Nsight graphics:</p>
<p><a href="/images/gpu-memory-pools/buffer_upload_direct_queue.png"><img src="/images/gpu-memory-pools/buffer_upload_direct_queue.png" alt=""></a></p>
<p><a href="/images/gpu-memory-pools/buffer_upload_copy_queue.png"><img src="/images/gpu-memory-pools/buffer_upload_copy_queue.png" alt=""></a></p>
<p>The top image is capture where a 16 MB buffer is uploaded every frame from SysRAM to VRAM using CopyBufferRegion on the main DIRECT queue, which is followed by a compute dispatch that immediately reads from the uploaded buffer. The bottom image shows what things look like if the CopyBufferRegion is instead submitted to a COPY queue, allowing it to overlap with the previous frame&rsquo;s work on the DIRECT queue. Notice how the frame time is 1.2ms shorter when using the COPY queue, despite the dispatch taking slightly longer when overlapped with the upload.</p>
<p>The trade-off here is that you will take on some significant complexity by using a separate COPY queue instead of re-using your primary queue. Submitting on an async queue means it is now up to you to properly synchronize your submissions across multiple queues: you will need to signal a fence on your COPY queue after submitting a command list and then have another queue wait on that fence before commands on the latter queue can read from the resources being uploaded. This is exactly like &ldquo;async compute&rdquo; setups that are common for overlapping compute work with graphics work, and has some of the same headaches. Like async compute, you need to carefully manage the submissions and synchronization between your queues if you&rsquo;re hoping for them to overlap. You also need to take care not to delete or otherwise re-use your staging buffers until the CPU has fully waited for the COPY fence to be signaled, otherwise you will get incorrect results and/or a GPU crash.</p>
<p>One extra bonus of using a COPY queue is that under the existing (and <a href="https://microsoft.github.io/DirectX-Specs/d3d/D3D12EnhancedBarriers.html">soon-to-be-legacy</a>) barrier setup there are <a href="https://docs.microsoft.com/en-us/windows/win32/direct3d12/using-resource-barriers-to-synchronize-resource-states-in-direct3d-12#state-decay-to-common">special rules</a> carved out for resource decay and promotion when a COPY queue is involved. When a resource is copied to or from on a COPY queue, that resource automatically decays back to a COMMON state after the COPY command list finished executing. Since COMMON resources get auto-promoted on their first use, this means that you can typically completely skip issuing any barriers for read-only resources that you initialize this way! This is extremely convenient, and can even save some performance as well.</p>
<p>Before moving on, I should point out that all of the above really only applies for NUMA/dedicated devices. A UMA/integrated device often doesn&rsquo;t have a dedicated DMA engine, and so any submissions to COPY queues will end up getting <a href="../breaking-down-barriers-part-5-back-to-the-real-world/#the-present-windows-10-d3d12-and-wddm-20">serialized/flattened</a> onto a single hardware queue. The typical DEFAULT/UPLOAD setup will still work correctly on ths type of hardware, but if you want to optimize for integrated devices then you may want to consider adding a path that uses custom heaps to allow the CPU to directly initialize your textures.</p>
<h3 id="two-copy-queues-are-better-than-one">Two COPY Queues Are Better Than One?</h3>
<p>Let&rsquo;s imagine you&rsquo;ve setup your engine to be able to submit SysRAM -&gt; VRAM uploads on a COPY queue so that they run asynchronously to your main graphics and compute work that&rsquo;s performed every frame. You hook this up to the buffer/texture initialization uploads that occur when new resources are created, and it seems to work great. In fact it works so well that you decide to re-use the same path for when the CPU needs to perform per-frame updates of large buffers that live in VRAM. Whenever you peek at a timing capture you see the uploads overlapping with your graphics work, so you consider your work done and move on. But then one day you get reports that the game&rsquo;s frame time is spiking erratically in areas where there is heavy background streaming, and the spikes appear to be coming from the GPU. You peek at the engine&rsquo;s timestamp queries surrounding your main graphics queue submission, and confusingly everything looks fine. It&rsquo;s not until you take a full multi-frame timing capture that you notice the problem:</p>
<p><a href="/images/gpu-memory-pools/background_streaming_upload_queue.png"><img src="/images/gpu-memory-pools/background_streaming_upload_queue.png" alt=""></a></p>
<p>It looks like your uploads from the background streaming are effectively hogging the COPY queue, preventing your more-critical buffer update upload from running. Since your graphics queue waits for the buffer upload to complete, it remains idle for a while which makes the overall frame time longer. This is pretty unfortunate and clearly needs to be fixed, but what should you do? One option is to move your critical upload onto the main DIRECT queue, effectively giving up on getting any overlap for that copy and letting your background uploads have the COPY queue all to themselves:</p>
<p><a href="/images/gpu-memory-pools/background_streaming_direct_queue.png"><img src="/images/gpu-memory-pools/background_streaming_direct_queue.png" alt=""></a></p>
<p>Things are much improved with this setup. In the fabricated scenario I setup for these captures we&rsquo;ve gone from a worst-case of 19.39ms down to about 12ms per frame. You&rsquo;re tempted to submit this and mark the JIRA task as complete, but as a performance-oriented programmer you can&rsquo;t shake the nagging feeling that we can still do even better than this. It&rsquo;s just not ideal to require the graphics queue to have to eat the cost of that critical buffer upload, and it&rsquo;s <em>really</em> not ideal that the critical upload is actually running slower than usual since it&rsquo;s sharing the PCIe bandwidth with the background uploads (in my test case it&rsquo;s taking about 2.43ms instead of 1.2ms for a 16 MB buffer, roughly double the time). Seeing how the two submissions end up sharing bandwidth, you think to yourself &ldquo;wouldn&rsquo;t it be great if the graphics work, background uploads, and critical uploads could all run asynchronously to each other and the uploads could split the bandwidth?&rdquo;. Figuring you have nothing to lose, you decide to add a second COPY queue to the engine and redirect all of your critical uploads to that queue instead. Much to your surprise and delight, this works!</p>
<p><a href="/images/gpu-memory-pools/background_streaming_fast_queue.png"><img src="/images/gpu-memory-pools/background_streaming_fast_queue.png" alt=""></a></p>
<p>It turns out that dedicated video cards from Nvidia and AMD support this kind of thing, which is advertised by the fact that they expose two transfer queues in Vulkan. What ends up happening is the background uploads and critical upload still share bandwidth which slows down the critical upload, but since the critical upload is able to execute much earlier it doesn&rsquo;t hold up the main graphics work. This won&rsquo;t scale up forever: at some point if there are too many critical uploads (or the main graphics work completes too quickly) then the critical upload COPY queue will hold up the main DIRECT/graphics queue. But it&rsquo;s still absolutely a compelling option for these kinds of advanced scenarios, and likely justifies the additional complexity.</p>
<h3 id="allocating-staging-memory">Allocating Staging Memory</h3>
<p>As I alluded to earlier, another complicating aspect of the two-step upload is that you need to reserve a sufficient amount of staging memory from <em>somewhere</em>. This memory then needs to remain allocated and untouched until the copying queue completely finishes executing the submission that pulls from the staging buffer. The simple approach seen in some samples is to create a committed buffer resource every time some data needs to be uploaded, and then destroy it after the submission is finished. While this certainly works, creating those committed resoures is definitely not free (especially if you omit the <code>D3D12_HEAP_FLAG_CREATE_NOT_ZEROED </code> flag!), and you could slow down your resource loading and creation by constantly creating and freeing like that. Another tricky issue is memory usage: if you keep allocating more staging buffers, you won&rsquo;t be able to free them until the GPU consumes them. This can result in unbounded memory usage if many uploads are being batched into a single queue submission during engine initialization. A single big submission with tons of uploads can also be bad on integrated GPUs that don&rsquo;t have an async DMA unit that you can use, in which case you&rsquo;re just going to stall the main graphics queue until all of the uploads finish.</p>
<p>As an alternative, one might consider sub-allocating from a single large staging buffer instead of constantly creating new ones. Since the allocation is tied to the GPU progress of copy operations, a ring buffer can be a pretty natural fit. The overall flow could go something like this:</p>
<ol>
<li>An upload is requested for N bytes</li>
<li>Check if there is enough space in the ring buffer, if not stall and wait for previous submissions to finish</li>
<li>Allocate space in the ring buffer by moving the head back N bytes</li>
<li>Copy resource data into the ring buffer at the right offset</li>
<li>Record a copy command on a command list</li>
<li>Submit the command list on a queue</li>
<li>Tell the queue to signal a fence once the command list finishes executing</li>
<li>Periodically check the fence value of previous submissions, if they&rsquo;ve completed move up the ring buffer tail to free up more space</li>
</ol>
<p>This is but one possibility for an allocation scheme, and even within such a setup there are more choices to be made. For instance, should it be thread-safe? If multiple threads can use it, which operations can happen in parallel? Is the calling code responsible for copying into the staging buffer and issuing copy commands, or is the uploader mechanism responsible? When should you submit to the GPU, and when should you poll for completed submissions? Should you batch multiple uploads into a single submission? How does this interact with the &ldquo;main&rdquo; per-frame submissions on DIRECT and COMPUTE queues? How big should your ring buffer be, and what should happen if an upload exceeds the size of that ring buffer? These questions don&rsquo;t necessarily have one-size-fits all answer, so it really depends on your engine and use case.</p>
<p>One example of the ring buffer setup can be found in <a href="https://github.com/TheRealMJP/DXRPathTracer/blob/sm_6_6/SampleFramework12/v1.02/Graphics/DX12_Upload.cpp">my sample framework</a>. It uses locks to ensure thread-safety by serializing allocations from the ring buffer as well as submissions to the COPY queue, but it allows multiple calling threads to copy into staging memory and record copy commands in parallel once they&rsquo;ve successfully allocated. It also allows for uploads to happen on background streaming threads that are decoupled from frame rendering. This is achieved by having a 1:1 relationship between uploads and submissions, which is definitely a non-optimal choice for uploading many small resources in succession. In that case the uploads can become bottlenecked by the max number of submissions rather than the ring buffer itself, and overhead from queue submission could start to dominate. The async COPY queue submissions are also waited on by the next frame&rsquo;s DIRECT/COMPUTE queue submissions, which is always safe (the calling code does not need to wait before using the resource to record commands) but usually too conservative. My framework also pairs this with a <a href="https://github.com/TheRealMJP/DXRPathTracer/blob/sm_6_6/SampleFramework12/v1.02/Graphics/DX12_Upload.cpp#L391">FastUploader</a> path that&rsquo;s intended for &ldquo;critical&rdquo; per-frame buffer updates. This path uses its own dedicated COPY queue, and batches all copies from a frame into a single command list and COPY queue submission that&rsquo;s performed once a frame. It also does not use the staging ring buffer, and instead allows calling code to either aquire some temporary per-frame staging memory or provide its own staging buffer.</p>
<p>As an example of another intriguing approach that makes different trade-offs, consider the uploader that <a href="https://alextardif.com/index.html">Alex Tardiff</a> wrote for his <a href="https://alextardif.com/DX12Tutorial.html">D3D12Lite framework</a>. Rather than using a ring buffer, the uploader allocates linearly from a staging buffer and fits as many uploads as it can until the staging buffer is exhausted. Every frame the uploads that were staged are submitted in a single command list, and the remaining uploads that do not fit are queued to be uploaded in a subsequent frame. This provides great batching behavior, and effectively enforces a fixed budget in terms of upload bandwidth that will consumed every frame. In comparison to my framework, the main tradeoff is that the uploader handles copying into staging memory and recording D3D12 commands (as opposed to the calling code handling it), which requires the memory containing the source data to be persistent until staging memory frees up. Calling code must also potentially wait several frames until the upload completes, although this kind of synchronization is nicely simplified by tying it to the main frame submission fence. Overall I would say it&rsquo;s very compelling, and could work well for a lot of scenarios!</p>
<p>For another possible example, check out <a href="https://github.com/microsoft/DirectXTK12/wiki/ResourceUploadBatch">ResourceUploadBatch</a> from DirectXTK. This helper class still allocates a committed resource for use as staging memory, but allows (manually) batching multiple uploads together into a single submission. It also makes use of <code>std::future</code> as a way of tracking when an upload actually completes, which is a nice touch.</p>
<h3 id="what-about-directstorage">What About DirectStorage?</h3>
<p>Some of you may have been asking yourself &ldquo;hey, how does DirectStorage fit into this?&rdquo; as you were reading through this section. DirectStorage is capable of fully managing the full journey from disk to staging memory to VRAM all on its own, which is pretty awesome. Unfortunately I&rsquo;m not personally familar with the inner details of how DirectStorage manages these things, nor do I have much experience with using it. I would assume they are biasing for high throughput of upload operations, since the main goal of that API is to improve loading times. Based on <a href="https://github.com/microsoft/DirectStorage/blob/main/Docs/DeveloperGuidance.md#staging-buffers-and-copying">the docs</a>, it sounds like they always suballocate staging memory from a single upload buffer and the operation will fail if the request is larger than the configured staging buffer size. The buffer is then flushed periodically after it fills up to trigger the actual upload to VRAM.</p>
<h2 id="results-from-my-testing-app">Results From My Testing App</h2>
<p>While it&rsquo;s useful to look at things in terms of theoretical max bandwidth, it&rsquo;s helpful to gather some actual performance numbers from real working hardware. To that end, I created <a href="https://github.com/TheRealMJP/MemPoolTest">simple D3D12 application</a> that can and gather timing data from a synthetic performance test. The test itself is nothing fancy: a buffer has its contents updated from the CPU, and then a compute shader is dispatched to read from that buffer. There are options to specify what kind of heap the buffer is allocated from, the size of the buffer, the number of compute shader threads, and the access pattern used by those threads. By timing the CPU update, we can gather some data indicating how quickly or slowly the CPU can fill the buffer with different memory pools and caching protocols. Optionally, we can also time how long it takes to <em>read</em> from the buffer in order to get an idea of how badly this will destroy performance. When the app is run, it presents some real-time results along with a simple interface that looks like this:</p>
<p><img src="/images/gpu-memory-pools/testing-app.png" alt=""></p>
<p>In the compute shader itself, a starting address is computed for each thread. The shader then runs an N-iteration loop, where each iteration reads and sums a single 16-byte element of the source buffer. The settings for controlling how the addresses are calculated are a bit obtuse, but together they allow setting up some commonly-seen access patterns to <a href="https://github.com/sebbbi/perftest">see how they fare with different memory and buffer types</a>. The shader itself is quite short so I&rsquo;ll post it here for reference:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">const</span> <span class="n">uint</span> <span class="n">ThreadGroupSize</span> <span class="o">=</span> <span class="mi">256</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="n">numthreads</span><span class="p">(</span><span class="n">ThreadGroupSize</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ComputeJob</span><span class="p">(</span><span class="n">in</span> <span class="n">uint3</span> <span class="nl">dispatchThreadID</span> <span class="p">:</span> <span class="n">SV_DispatchThreadID</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">in</span> <span class="n">uint3</span> <span class="nl">groupThreadID</span> <span class="p">:</span> <span class="n">SV_GroupThreadID</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">in</span> <span class="n">uint3</span> <span class="nl">groupID</span> <span class="p">:</span> <span class="n">SV_GroupID</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">uint</span> <span class="n">localThreadIndex</span> <span class="o">=</span> <span class="n">groupThreadID</span><span class="p">.</span><span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">uint</span> <span class="n">globalThreadIndex</span> <span class="o">=</span> <span class="n">dispatchThreadID</span><span class="p">.</span><span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">uint</span> <span class="n">groupIndex</span> <span class="o">=</span> <span class="n">groupID</span><span class="p">.</span><span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cp">#if RawBuffer_
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">ByteAddressBuffer</span> <span class="n">inputBuffer</span> <span class="o">=</span> <span class="n">RawBufferTable</span><span class="p">[</span><span class="n">AppSettings</span><span class="p">.</span><span class="n">InputBufferIdx</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#elif FormattedBuffer_
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">Buffer</span><span class="o">&lt;</span><span class="n">float4</span><span class="o">&gt;</span> <span class="n">inputBuffer</span> <span class="o">=</span> <span class="n">InputBuffers</span><span class="p">[</span><span class="n">AppSettings</span><span class="p">.</span><span class="n">InputBufferIdx</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#elif StructuredBuffer_
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">StructuredBuffer</span><span class="o">&lt;</span><span class="n">float4</span><span class="o">&gt;</span> <span class="n">inputBuffer</span> <span class="o">=</span> <span class="n">InputBuffers</span><span class="p">[</span><span class="n">AppSettings</span><span class="p">.</span><span class="n">InputBufferIdx</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">sum</span> <span class="o">=</span> <span class="mf">0.0f</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Values with a trailing underscore are compile-time constants, allowing full
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// loop unrolling and dispatch-uniform/wave-uniform buffer access where applicable
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">uint</span> <span class="n">elemIdx</span> <span class="o">=</span> <span class="p">(</span><span class="n">ElemsPerThread_</span> <span class="o">*</span> <span class="n">ThreadGroupSize</span> <span class="o">*</span> <span class="n">GroupElemOffset_</span> <span class="o">*</span> <span class="n">groupIndex</span><span class="p">)</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="n">ElemsPerThread_</span> <span class="o">*</span> <span class="n">ThreadElemOffset_</span> <span class="o">*</span> <span class="n">localThreadIndex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span><span class="p">(</span><span class="n">uint</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">ElemsPerThread_</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">const</span> <span class="n">uint</span> <span class="n">loadIdx</span> <span class="o">=</span> <span class="n">elemIdx</span> <span class="o">%</span> <span class="n">NumInputBufferElems_</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cp">#if RawBuffer_
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>            <span class="k">const</span> <span class="n">float4</span> <span class="n">data</span> <span class="o">=</span> <span class="n">asfloat</span><span class="p">(</span><span class="n">inputBuffer</span><span class="p">.</span><span class="n">Load4</span><span class="p">(</span><span class="n">loadIdx</span> <span class="o">*</span> <span class="mi">16</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#elif StructuredBuffer_ || FormattedBuffer_
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>            <span class="k">const</span> <span class="n">float4</span> <span class="n">data</span> <span class="o">=</span> <span class="n">inputBuffer</span><span class="p">[</span><span class="n">loadIdx</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#elif ConstantBuffer_
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>            <span class="k">const</span> <span class="n">float4</span> <span class="n">data</span> <span class="o">=</span> <span class="n">CBuffer</span><span class="p">.</span><span class="n">Elems</span><span class="p">[</span><span class="n">loadIdx</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">        <span class="n">sum</span> <span class="o">+=</span> <span class="n">dot</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="mf">1.0f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">elemIdx</span> <span class="o">+=</span> <span class="n">ThreadElemStride_</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Should not actually execute, this is just here to keep the
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// compiler from optimizing away the entire shader
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span><span class="p">(</span><span class="n">sum</span> <span class="o">&lt;</span> <span class="mf">0.0f</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">OutputBuffer</span><span class="p">.</span><span class="n">Store</span><span class="p">(</span><span class="n">globalThreadIndex</span> <span class="o">*</span> <span class="mi">4</span><span class="p">,</span> <span class="n">sum</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>I&rsquo;ve used my desktop PC for gathering the data below, which has a Nvidia RTX 2080 with an AMD Ryzen 9 5900X CPU. The GPU has a peak bandwidth of 448 GB/s to VRAM, and as a PCIe 3.0 part it has a max bandwidth of about 16 GB/s for reading memory over that bus. Meanwhile the CPU is connected to 32 GB of DDR4 3600, which has a peak bandwidth of around 28 GB/s per channel, resulting in a total of 56 GB/s for my dual-channel configuration.</p>
<h3 id="cpu-write-performance">CPU Write Performance</h3>
<div>
    <canvas id="cpu-write-chart" width="" height=""></canvas>
    <script src="/js/gpu-memory-pools/cpu-write-chart.js"></script>
</div>
<p>Here we&rsquo;re measuring the time it took for a single 5900X core to fill 16 MB buffer. For this test I only used CPU-accessible memory, since non-CPU-accessible VRAM requires additional work to schedule a GPU copy for the upload. What we see here is that the uncached and cached paths are basically identical, meaning that write combining is doing its job for our simple access pattern we&rsquo;re using (which is just a memcpy). For the special CPU-accessible VRAM path available through NVAPI, the time grows by about 40% reflecting the fact that we are writing over a slow PCI express bus. For SysRAM this works out to about 15.5 GB/s, while for VRAM it&rsquo;s about 11.6 GB/s.</p>
<h3 id="cpu-read-performance">CPU Read Performance</h3>
<div>
    <canvas id="cpu-read-chart" width="" height=""></canvas>
    <script src="/js/gpu-memory-pools/cpu-read-chart.js"></script>
</div>
<p>For this test case, we want to see how quickly (or slowly) the CPU is able to read from CPU-visible GPU memory. For uncached SysRAM it takes about 4ms to read the entire 16 MB buffer, which is about 4x more time than it took to fill the buffer with write combining. Meanwhile for cached SysRAM it&rsquo;s about 60x faster, no doubt because the buffer is still present in the cache hierarchy after being filled just prior to the reading step. In real-world scenarios we might expect read performance to be worse for this case since the data may not be present in cache. As for CPU-visible VRAM, we have officially entered &ldquo;oh no, what I have done?&rdquo; territory at a whopping 26.66ms. Remember kids: friends don&rsquo;t let friends read from VRAM on the CPU!</p>
<h3 id="gpu-read-performance-normal-access">GPU Read Performance, Normal Access</h3>
<div>
    <canvas id="gpu-read-chart" width="" height=""></canvas>
    <script src="/js/gpu-memory-pools/gpu-read-chart.js"></script>
</div>
<p>For this test, we are now looking at GPU performance by measuring how long it takes for the GPU to complete the single big compute shader dispatch that reads from the buffer. While this doesn&rsquo;t serve as a comprehensive bandwidth or cache performance test, it tells us roughly the overall effect that the GPU memory pool will have on GPU performance which is primarily what we are interested in. To gather the data, the test app was configured to use a 64 MB buffer and launch 16 * 1024 * 256 = 4M threads that each access a unique contiguous 16-byte element from the input buffer. This effectively reads from the entire buffer, giving us a rough idea of low long it takes to read the whole thing.</p>
<p>As you would expect for a dedicated GPU, there&rsquo;s a significant disparity between reading from SysRAM (L0) and VRAM (L1). Reading from SysRAM causes the compute shader to take about ~32x longer to execute relative to VRAM, reflecting the significantly lower theoretical bandwidth for reading over PCIe vs VRAM. This is very close to what we would expect given that my RTX 2080 is rated at 448 GB/s for bandwidth to VRAM, and PCIe 3.0 tops out at 16 GB/s.</p>
<p>To help confirm things, let&rsquo;s invert these numbers to estimate what the minimum required bandwidth would be in order for the dispatch to complete in the reported time:</p>
<div>
    <canvas id="gpu-read-bandwidth-chart" width="" height=""></canvas>
    <script src="/js/gpu-memory-pools/gpu-read-bandwidth-chart.js"></script>
</div>
<p>While our calculated numbers fall a bit short of the expected theoretical bandwidth, this is probably expected given that our shader may not be hitting the exact conditions required for achieving peak bandwidth on the hardware. However they still clearly reflect the extreme bandwidth deficit that&rsquo;s experienced when reading from SysRAM.</p>
<h3 id="gpu-read-performance-non-coalesced-access">GPU Read Performance, Non-Coalesced Access</h3>
<div>
    <canvas id="gpu-read-non-coalesced-chart" width="" height=""></canvas>
    <script src="/js/gpu-memory-pools/gpu-read-non-coalesced-chart.js"></script>
</div>
<p>For the previous test, each thread accessed a buffer element that was exactly contiguous with the element being accessed by neighboring threads. GPUs can typically accelerate this case by issuing <a href="https://gpuopen.com/learn/gcn-memory-coalescing/">coalesced memory requests</a>. This pattern is also good in general for cache efficiency: all data from a cache line ends up getting utilized, as opposed to unused neighboring data getting inadvertently pulled into cache. In this test we use a less efficient access pattern where each thread reads a 16 byte element that is 64 bytes away from what their neighboring threads read. If the cache line is 64 bytes, this means that 48 of those 64 bytes are wasted which will slow things down considerably. This is roughly reflected in the test numbers, where the dispatch is about 3-4x slower for all memory types when compared with the previous test case.</p>
<h3 id="gpu-read-performance-various-buffer-sizes">GPU Read Performance, Various Buffer Sizes</h3>
<p>Earlier in the article we discussed how the <em>size</em> of the buffer being read is something that should factor into our decision of whether to place the buffer in VRAM or SysRAM. The general rule of thumb is &ldquo;small&rdquo; buffers are ok being in SysRAM, particularly if they are only read once and/or many threads end up reading the same data from that buffer (constant buffers typically fit this pattern). Let&rsquo;s try to verify that assumption by running a test where we run a smaller dispatch of 1M threads that access various sizes of smaller buffers. The setup is such that each thread will mod their address with the buffer size, effectively causing the reads to &ldquo;wrap around&rdquo;. Therefore if 1M threads each reads a 16-byte element from the input buffer and that buffer is 512 KB, we would expect each byte of that buffer to be read (16 * 1024 * 1024) / (512 * 1024) = 32 times. Let&rsquo;s now have a look at the results:</p>
<div>
    <canvas id="gpu-read-sizes-chart" width="" height=""></canvas>
    <script src="/js/gpu-memory-pools/gpu-read-sizes-chart.js"></script>
</div>
<p>At the leftmost end of the chart we see that the gap between SysRAM and VRAM is almost 0 for a tiny 64 KB buffer. This suggests that at typical constant buffer sizes there is little benefit to VRAM, and the speed of the GPU&rsquo;s cache hierarchies will dominate. As we increase in size though see the SysRAM times pull away from the VRAM times, the latter essentially appearing to have a flat slope at these scales. This suggests that the limited bandwidth of PCIe is bottlenecking the SysRAM dispatch more and more as the buffer size increases, but for VRAM we are likely bottlenecked elsewhere (perhaps on the output writes). Let&rsquo;s zoom out a bit more and see what we get for 1 to 16 MB buffer sizes:</p>
<div>
    <canvas id="gpu-read-sizes-chart2" width="" height=""></canvas>
    <script src="/js/gpu-memory-pools/gpu-read-sizes-2-chart.js"></script>
</div>
<p>Up to 4 MB we continue our trend of SysRAM pulling away from the VRAM results, with SysRAM being about 10x slower at that point. But then at 5 MB things jump way up to 1.285 milliseconds, and it stays up there! What gives? What&rsquo;s so special about 4 MB that things take a nosedive after that point? Well it turns out that 4 MB is the size of the L2 cache on my RTX 2080! Essentially at &lt;= 4 MB the entire buffer was able to fit in L2 cache, which meant that the dispatch time roughly reflected the amount of time it would require to fill that &lt;= 4 MB from memory. If you compute the minimum required bandwidth (buffer size divided by execution time) for these &lt; 4 MB timings, they come out to about 10 GB/s which is fairly close to the 12.5 GB/s I measured from SysRAM in other tests. Meanwhile for the &gt;= 5 MB data, that 1.285 millisecond number that repeats for each test is exactly what you get if you calculate how long it takes to read a 16 MB buffer with 12.5 GB/s bandwidth. 16 MB is what you get if you sum the size of all reads from every thread regardless of the buffer size (1M threads, each reading 16 bytes), which means we&rsquo;re basically pegged at 12.5 GB/s &ldquo;effective&rdquo; bandwidth for these measurements. Since our data set no longer fits comfortably in L2 cache, we can&rsquo;t count on the data to still be in cache the Nth time an element is accessed by a later thread.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Having to manage separate physical memory pools for GPU programming can be rather complicated, and there are definitely performance pitfalls that we need to watch out for. Integrated GPUs that share a memory pool with the CPU can be simpler, but we still need to interact with them using APIs that accomodate dedicated video cards. In most cases we also need to run the same code on both dedicated and integrated GPUs, which means we need to try to handle both cases gracefully. To minimize the pain, I would recommend the following high-level practices:</p>
<ul>
<li>For dedicated video cards:
<ul>
<li>Make sure you&rsquo;re using VRAM for all textures as well as buffers that do not need to be updated from the CPU. The bandwidth difference between SysRAM and VRAM can be nearly 100x on high-end video cards.</li>
<li>Try to use a COPY/Transfer queue to initialize VRAM resources asynchronously to your graphics work.</li>
<li>For larger buffers that need to be updated from the CPU, consider allocating them in VRAM and using a staging buffer + COPY/Transfer queue to update them. Consider using two queues if you have both background uploads and critical per-frame uploads.</li>
<li>On APIs and systems that support it (Vulkan and NVAPI), consider allocating large CPU-updated buffers in CPU-writable VRAM so that you can update them directly. Watch out for potentially slower CPU write performance. <em><strong>Do not read ever from this memory on the CPU!</strong></em></li>
</ul>
</li>
<li>For integrated/mobile GPUs:
<ul>
<li>Skip using the COPY/transfer queue to initialize or update buffers, you can just make them CPU-writable with no GPU performance penalty.</li>
<li>With D3D12 you can still use DEFAULT heaps if you like, but it&rsquo;s still going to be located in SysRAM.</li>
</ul>
</li>
<li>In general:
<ul>
<li>Be careful not to read from memory that is uncached for the CPU, it can be quite slow!</li>
<li>Try to stick to a simple memcpy when updating GPU memory from the CPU. This will ensure that you play nicely with write combining by writing to the buffer sequentially, and it will also avoid implicit reads that can sometimes occur (for example by summing a value located in GPU memory).</li>
</ul>
</li>
</ul>
<p>Finally, I&rsquo;d like to thank the following people for reviewing this post early and providing great feedback:</p>
<ul>
<li><a href="https://asawicki.info/">Adam Sawicki</a> (extra-special thanks for the suggestion to try using two COPY queues)</li>
<li><a href="https://alextardif.com/">Alex Tardiff</a></li>
<li><a href="https://www.jeremyong.com/">Jeremy Ong</a></li>
<li><a href="https://twitter.com/pbrubaker">Pete Brubaker</a></li>
<li><a href="https://twitter.com/Jiayin_Cao">Jiayin Cao</a></li>
<li>Brian Collins</li>
</ul>
<p>That&rsquo;s all for now, good luck with wrangling those GPUs and their memory pools!</p>
<p><em>Update 01/04/2024:</em> I&rsquo;ve uploaded the memory bandwidth testing app to a <a href="https://github.com/TheRealMJP/MemPoolTest">public GitHub repo</a> in case anybody else would like to try it out. I also updated it to use <a href="https://microsoft.github.io/DirectX-Specs/d3d/D3D12GPUUploadHeaps.html">GPU Upload Heaps</a> from the preview agility SDK, which should be available if you&rsquo;re running on a system with ReBAR support.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>D3D12 actually provides two different ways for you to create a texture with a &ldquo;known&rdquo; layout that you can encode/decode from the CPU. The first is <code>D3D12_TEXTURE_LAYOUT_ROW_MAJOR</code>, which uses a standard linear layout that is very inefficient for the GPU to access due to lack of 2D locality. The second is through <code>D3D12_TEXTURE_LAYOUT_64KB_STANDARD_SWIZZLE</code>, which uses a known standardized swizzle pattern that&rsquo;s common across hardware. While the standard swizzle sounds like a nice option, in practice Intel/AMD/Nvidia do not actually support it on their hardware. This is likely because the standardized pattern is less efficient than the hardware-specific patterns currently in use.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
		</item>
		
		<item>
			<title>The Shader Permutation Problem - Part 1: How Did We Get Here?</title>
			<link>https://therealmjp.github.io/posts/shader-permutations-part1/</link>
			<pubDate>Mon, 11 Oct 2021 00:00:00 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/shader-permutations-part1/</guid>
			<description>It is no secret that we tend to have a bit of a problem with shader permutations in real-time graphics. It&amp;rsquo;s such a bad problem that it not only affects graphics programmers, but also trickles down to all of the other content creators that use an engine. If you don&amp;rsquo;t believe me, just go ahead and search for &amp;ldquo;unreal compiling shaders meme&amp;rdquo; on Google images and see what comes up. There are many great ones to choose from, but personally I am a bit partial to this one:</description>
			<content:encoded><![CDATA[<p>It is no secret that we tend to have a bit of a problem with shader permutations in real-time graphics. It&rsquo;s such a bad problem that it not only affects graphics programmers, but also trickles down to all of the other content creators that use an engine. If you don&rsquo;t believe me, just go ahead and search for &ldquo;unreal compiling shaders meme&rdquo; on Google images and see what comes up. There are many great ones to choose from, but personally I am a bit partial to this one:</p>
<p><img src="/images/shaders/compiling_shaders.png" alt="compiling shaders narcos meme"></p>
<p>If people are making memes about an issue then you know it&rsquo;s pretty bad, and the shader permutation situation can indeed be pretty bad. It&rsquo;s frustrating because collectively we <em>have</em> put a lot of effort into fixing or mitigating the problem, and yet it still hasn&rsquo;t really gone away. It&rsquo;s also frustrating because I&rsquo;ve seen people trivialize it with suggestions like &ldquo;just don&rsquo;t make so many shaders!&rdquo; or &ldquo;just use branching!&rdquo; which I think misses a lot of the nuance and context of the problem. To help with those latter two, I thought I would spend some time talking about why permutations continue to be a problem and also give a little bit of history on how we got to this point. In <a href="../shader-permutations-part2">part 2</a> I&rsquo;ll then talk a bit about the various options we have available that can help us to get out of this mess, and expand on what&rsquo;s good and bad about them.</p>
<h3 id="whats-a-few-shaders-among-friends">What&rsquo;s A Few Shaders Among Friends?</h3>
<p>To start, let&rsquo;s go over a bit of background on the problem. When I talk about &ldquo;shader permutations&rdquo; (also sometimes called &ldquo;shader variants&rdquo;), what I&rsquo;m referring to is taking a pile of shader code and compiling it N times with different options. In most cases these permutations are tied directly to features that are supported by the shader, often by writing the code in an &ldquo;uber-shader&rdquo; style with many different features that can be turned on and off independently. As a super-simple example let&rsquo;s look at a little HLSL pixel shader that uses preprocessor macros to turn different features on and off:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// ENABLE_NORMAL_MAP, ENABLE_EMISSIVE_MAP, ENABLE_AO_MAPPING are
</span></span></span><span class="line"><span class="cl"><span class="c1">// macros whose values are defined via compiler command line options
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="k">const</span> <span class="kt">bool</span> <span class="n">EnableNormalMap</span> <span class="o">=</span> <span class="n">ENABLE_NORMAL_MAP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="kt">bool</span> <span class="n">EnableEmissiveMap</span> <span class="o">=</span> <span class="n">ENABLE_EMISSIVE_MAP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="kt">bool</span> <span class="n">EnableAOMap</span> <span class="o">=</span> <span class="n">ENABLE_AO_MAPPING</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">float4</span> <span class="nf">PSMain</span><span class="p">(</span><span class="n">in</span> <span class="n">PSInput</span> <span class="n">input</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target0</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">normal</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">input</span><span class="p">.</span><span class="n">VtxNormal</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">EnableNormalMap</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">normal</span> <span class="o">=</span> <span class="n">ApplyNormalMap</span><span class="p">(</span><span class="n">normal</span><span class="p">,</span> <span class="n">input</span><span class="p">.</span><span class="n">TangentFrame</span><span class="p">,</span> <span class="n">input</span><span class="p">.</span><span class="n">UV</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">albedo</span> <span class="o">=</span> <span class="n">input</span><span class="p">.</span><span class="n">Albedo</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">ambientAlbedo</span> <span class="o">=</span> <span class="n">albedo</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">EnableAOMap</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">ambientAlbedo</span> <span class="o">*=</span> <span class="n">SampleAOMap</span><span class="p">(</span><span class="n">input</span><span class="p">.</span><span class="n">UV</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">lighting</span> <span class="o">=</span> <span class="n">CalcLighting</span><span class="p">(</span><span class="n">albedo</span><span class="p">,</span> <span class="n">ambientAlbedo</span><span class="p">,</span> <span class="n">normal</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">EnableEmissiveMap</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">lighting</span> <span class="o">+=</span> <span class="n">SampleEmissiveMap</span><span class="p">(</span><span class="n">input</span><span class="p">.</span><span class="n">UV</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">float4</span><span class="p">(</span><span class="n">lighting</span><span class="p">,</span> <span class="mf">1.0f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>We have 3 features that can be enabled or disabled here, and if all of those features are independent we need to compile 2^3 = 8 permutations. You can hopefully see the problem already: each new feature we add means we <em>double</em> the permutation count! We can also generalize this a bit to non-binary features that have more than 2 different states, in which case the formular for computing the total number of permtuations looks something like this:</p>
<p>$$ NumPermutations = NumStates_{featureA} * NumStates_{featureB} * NumStates_{featureC} &hellip; $$</p>
<p>With that kind of exponential growth it&rsquo;s not hard to imagine how the total permutation count can get up into the thousands or <a href="https://aras-p.info/blog/2017/02/05/Every-Possible-Scalability-Limit-Will-Be-Reached/">millions</a> as you add more and more features. That&rsquo;s pretty obviously not great when you start thinking about the impact this could have on developer workflow. As you add more and more features and more and more shader code to implement those features, you get this kind of cascading effect where you&rsquo;re asking the compiler to churn through this big monolith shader program and over and over again. That&rsquo;s a lot of wasted work of parsing and optimizing the same code over and over again, and unless you&rsquo;ve got threadrippers and/or a local network of machines to distribute compilation across it can turn into hours of time for each change to the shader code. This is really not where you want to be if you&rsquo;re trying to rapidly iterate on optimizations or new features, and you <em>especially</em> don&rsquo;t want to pass that on to content creators that need to cook all of the necessary data for the game to run.</p>
<p>Speaking of content creators, yet another source of unbounded shader growth comes from engines that allow users to create their own custom shaders that are bound to materials. Some expose this by allowing users to hand-author shaders in a shader language, while others allow you to create &ldquo;<a href="https://docs.unrealengine.com/4.27/en-US/RenderingAndGraphics/Materials/Editor/">shader graphs</a>&rdquo; using a visual node-based interface. Depending on how the renderer is setup, these can potentially serve as an additional multiplier on top of the permutation count that comes from the engine itself. For example a shader graph might only allow you to generate shading parameters (such as color, normals, roughness, etc.) while the engine combines that with hand-written shader permutations that implement the actual lighting calculations. If you have 100 permutations for the lighting phase and 100 shader graphs for a project&hellip;well guess what, you now have 10k shaders.</p>
<p>The thing is that the time spent compiling isn&rsquo;t even the entire problem, it&rsquo;s just one part of it. Compiling all of those shaders can start to generate a lot of binary data, especially if you generate debug info. You may start running into issues with storing all of that data, or loading it and keeping it in memory at runtime. But wait, it gets worse! Recent PC and mobile graphics APIs like D3D and Vulkan actually implement a two-step compilation pipeline. Some people are surprised to learn this when they start with graphics, but offline shader compilers like dxc, fxc, and glslc actually generate some kind of intermediate bytecode rather than machine code that can be run on the GPU shader cores. GPUs actually have wildly different ISAs under the hood, with the instructions often changing from one hardware generation to the next even within the same vendor. To handle this setup, the driver will employ some kind of JIT compiler that takes your DXBC, DXIL or SPIR-V and converts it into the final ISA that the shader core can execute. The full pipeline typically goes something like this:</p>
<ul>
<li>The shader source code is compiled offline by a shader compiler, such as fxc, <a href="https://github.com/microsoft/DirectXShaderCompiler">dxc</a>, or <a href="https://github.com/google/shaderc/tree/main/glslc">glslc</a>
<ul>
<li>The output of this step is a hardware-agnostic intermediate bytecode format, either DXBC (fxc), DXIL (dxc), or SPIR-V (dxc or glslc)</li>
</ul>
</li>
<li>The compiled bytecode is loaded into memory by the engine</li>
<li>The engine creates a <a href="https://blog.mecheye.net/2021/06/the-missing-guide-to-modern-graphics-apis-2-psos/">Pipeline State Object</a> (PSO) by passing the required state as well as the compiled bytecode for any required shader stages
<ul>
<li>During this step, the driver runs its own JIT compiler that converts the bytecode into its own hardware-specific ISA that runs on the physical shader cores</li>
</ul>
</li>
<li>The engine binds a PSO to a command buffer, and issues draw/dispatch commands that used the shaders referenced by the PSO</li>
<li>The command buffer is submitted to the GPU, where the shader program actually gets executed</li>
</ul>
<p>It&rsquo;s easy to focus on that first step when thinking about the downsides of permutation explosion, but in reality those other remaining steps can all be affected as well. Having more permutations often means invoking that JIT compile step more times, which can really add up. Keep in mind this is usually not a simple translation that&rsquo;s happening here: most drivers will boot up a full-fledged optimizing compiler (often LLVM) and then run your bytecode through that in order to produce the final ISA. This can really catch you off guard if you&rsquo;re just expecting to quickly create all your PSOs in the background as you stream in a new chunk of the world: you have to anticipate that creating those PSOs is going to consume considerable time and CPU resources which in turn affects your your effective load times. Even worse, the amount of time can vary depending on the vendor, exact GPU architecture, the exact driver version, the other states and shader stages you&rsquo;ve provided in the PSO description, and even whether or not this is the first time creating that exact PSO on the system!<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> It&rsquo;s true that <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12pipelinestate-getcachedblob">D3D12</a> and <a href="https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPipelineCache.html">Vulkan</a> both provide mechanisms for manual caching of PSOs by the application, however these APIs tend to be <a href="https://zeux.io/2019/07/17/serializing-pipeline-cache/">complex</a> and still don&rsquo;t help your &ldquo;first boot&rdquo; loading times. Valve has even gone so far as to integrate a <a href="https://www.guru3d.com/news-story/steam-enables-shader-pre-caching.html">distributed caching system</a> into Steam that works for OpenGL and Vulkan, which actually says a lot about how bad of a problem this can be!</p>
<p>Even once you&rsquo;ve got your PSOs created, the downsides of unchecked permutation explosion continue. To use those PSOs, you need to bind them prior to a draw or dispatch on a command buffer. This binding step consumes CPU time: issuing many draws with a single PSO can be significantly cheaper than switching PSO between each draw. The end result is that the CPU time required for generating command buffers goes up as you increase your shader/PSO count. Having many PSOs can also prevent you from using techiniques such as instancing or batching to combine things into a single draw or dispatch, which also increases CPU time. But that&rsquo;s not all: unless you&rsquo;re using <a href="https://developer.nvidia.com/blog/new-vulkan-device-generated-commands/">Nvidia-specific extensions</a>, both D3D12 and Vulkan are incapable of changing PSOs from the GPU in the middle of a ExecuteIndirect or DrawIndirect/DispatchIndirect call.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> This means that if you&rsquo;re looking to use <a href="http://advances.realtimerendering.com/s2015/aaltonenhaar_siggraph2015_combined_final_footer_220dpi.pdf">fancy GPU-driven techniques</a> for culling and computing LOD on the GPU, you will need at <em>minimum</em> a number of ExecuteIndirect/DrawIndirect calls that is equal to the number of PSOs used by those draws. Too many PSOs/shaders can also pose issues for ray tracing, since large shader tables can lead to divergence and inefficient hardware utilization.</p>
<p>Finally, there&rsquo;s the execution of the shader program on the GPU itself. Modern GPUs have a lot of tricks in them to keep performance acceptable even in the presence of many draw calls, and many shader switches. However, in the general case they are still going to achieve higher performance when you can feed them larger batches of work to do that don&rsquo;t require switching states and shaders. As a consequence, your GPU performance can be reduced as a result of switching shaders too often. The same goes for your CPU performance: switching shaders/PSOs often involves more expensive operations in the driver that are required to reconfigure the necessary state bits on the GPU, and also handle downstream impacts of switching shaders (such as the low-level representation of the resource bindings used by the shaders). Therefore if you really want to burn through lots of draw calls in a few milliseconds of CPU time, you&rsquo;ll want to aim for as few PSOs and PSO switches as you can.</p>
<h3 id="the-monolithic-shader-program">The Monolithic Shader Program</h3>
<p>In my earlier example where I shared a simple pixel shader with permutations, you may have noticed how each permutation required compiling the entire shader program all the way through. This idea is probably a bit weird if you&rsquo;re not used to shader programming: after all, that&rsquo;s not really how we do things for our CPU code. For a language like C or C++ we will typically compile much more modular sections of code into separate compilation units, and then link everything together through function calls and global variables. Or we might go even further and compile our code into completely separate executables (DLLs or SOs) that can be dynamically loaded and called into at runtime, giving us further flexibility in how we structure things. Why can&rsquo;t we do things like that with shaders? Why do shaders have to be so <em>monolithic</em> instead of being modular? To answer that, let&rsquo;s step back in time a bit.</p>
<p>If you look back to the start of shader programs, they started out small. Not in the metaphorical sense of &ldquo;small ideas and limited use cases&rdquo; (which is true, but not what I&rsquo;m talking about here) but in the very literal sense of &ldquo;they were very very short&rdquo;. The original <a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-sm1">Shader Model 1.0</a> introduced with D3D8 had a hard limit of 128 instructions for vertex shaders, and the <a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx9-graphics-reference-asm-ps-1-x">pixel shaders</a> were basically just directly exposing the (very limited) register combiner and texture functionality of the hardware of that time. At that point you would have been crazy to think of a shader as anything other than a tiny little self-contained program or routine: they were only a handful of instructions! It wasn&rsquo;t even until D3D9 and later that people authored them in an actual high-level shader language instead of hand-writing &ldquo;assembly&rdquo;.</p>
<p>The thing about shaders is that we never really shook that kind of &ldquo;small self-contained program&rdquo; mentality. For years we basically just extended it further and further: first with higher instruction counts, then with more features, then with fancier compilers. But all along the way we kept the model where a shader program is monolithic and completely self-contained. This is not entirely just legacy decisions either: along the way we benefited from some of the halo effects that come from this really simple model. Shaders never needed a linker since there was nothing to link, and there was never a need to have a standard ABI for calling between separately-compiled binaries. In fact there&rsquo;s historically been no function calls at all within a compiled shader program! Why do that when you can instead just ruthlessly rip through the shader code and inline <em>everything</em> until you&rsquo;re left with nothing but a linear stream of instructions. If you&rsquo;re coming from a C++ background you&rsquo;re probably used to seeing the optimizer pull some heroics in certain places, but shaders can be on another level due to the simplicity of the language: after all there&rsquo;s no constructors, no side effects, and general memory writes weren&rsquo;t even added until Shader Model 5.0! Let&rsquo;s look at another simple HLSL pixel shader to show you what I mean by this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">ComputeLighting</span><span class="p">(</span><span class="n">in</span> <span class="n">float3</span> <span class="n">albedo</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">normal</span><span class="p">,</span> <span class="n">in</span> <span class="n">Light</span> <span class="n">light</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">saturate</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">normal</span><span class="p">,</span> <span class="n">light</span><span class="p">.</span><span class="n">Dir</span><span class="p">))</span> <span class="o">*</span> <span class="n">albedo</span> <span class="o">*</span> <span class="n">light</span><span class="p">.</span><span class="n">Color</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">float</span> <span class="nf">PSMain</span><span class="p">(</span><span class="n">in</span> <span class="n">float3</span> <span class="nl">albedo</span> <span class="p">:</span> <span class="n">ALBEDO</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="nl">normal</span> <span class="p">:</span> <span class="n">NORMAL</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target0</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ComputeLighting</span><span class="p">(</span><span class="n">albedo</span><span class="p">,</span> <span class="n">normalize</span><span class="p">(</span><span class="n">normal</span><span class="p">),</span> <span class="n">CBuffer</span><span class="p">.</span><span class="n">Light</span><span class="p">).</span><span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>If you were expecting the compiler to emit instructions for <code>ComputeLighting</code> independently, you would probably expect it to perform the chain of multiplication using float3 vectors, resulting in 6 total multiplications after the saturated dot product is computed. You would also expect <code>PSMain</code> to receive that float3 result, ignore the Y/Z components, and then return the X component. In reality shader compilers have never worked this way: instead they would fully inline/flatten the function call and dead-strip like their life depended on it, resulting in generated assembly that was more equivalent to this code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">float</span> <span class="nf">PSMain</span><span class="p">(</span><span class="n">in</span> <span class="n">float3</span> <span class="nl">albedo</span> <span class="p">:</span> <span class="n">ALBEDO</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="nl">normal</span> <span class="p">:</span> <span class="n">NORMAL</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target0</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">saturate</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">normal</span><span class="p">,</span> <span class="n">light</span><span class="p">.</span><span class="n">Dir</span><span class="p">))</span> <span class="o">*</span> <span class="n">albedo</span><span class="p">.</span><span class="n">x</span> <span class="o">*</span> <span class="n">light</span><span class="p">.</span><span class="n">Color</span><span class="p">.</span><span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This is not really magic or anything, and you can see a C or C++ compiler do the same thing in similar places. But with shaders it&rsquo;s different because you&rsquo;re almost guaranteed to get that optimized and inlined result due to the simplicity of the programming model. This is really really great if you once again go back to the earlier SM 2.0 era of shaders: you really needed to penny pinch your instructions to avoid hitting instruction limits, and when you were running these programs across thousands of pixels each little add or multiply could really add up quickly. The &ldquo;flatten everything!&rdquo; style of code-gen also suited earlier programmable GPUs particularly well, since they either couldn&rsquo;t do dynamic flow control or they were very very bad at it. Thus it was to your advantage to be able to take shader code written in a branchy or loopy style and convert that into something completely flattened and unrolled whereever possible.</p>
<p>Modern GPUs can still benefit from these kinds of agressive micro-optimizations. Sure a recent GPU can do an awful lot more MADDs per second than a GPU from 2005, but extra math can still add up when you&rsquo;re doing it for millions of pixels every frame. They&rsquo;re also significantly <a href="https://bartwronski.com/2021/01/18/is-this-a-branch/">better at actual branching and loops</a> then they used to be (at least as long as the flow control is &ldquo;uniform&rdquo;, which means that all threads within a wave/subgroup take the same path), but flow control is never 100% free. Once again a few extra instructions for iterating a loop control variable and checking the condition can add up if it&rsquo;s done many times, and you might be better off letting the compiler unroll that for you. Flow control can also inhibit certain kinds of optimizations that compilers like to do where they overlap memory or math instructions from unrelated code in order to extract additional <a href="https://en.wikipedia.org/wiki/Instruction-level_parallelism">instruction level parallelism</a> (or <a href="https://anteru.net/blog/2018/even-more-compute-shaders/">ILP</a> for short), which can potentially decrease the latency of a wave in isolation.</p>
<p>Even if you ignore the optimization aspect, there&rsquo;s still the fact that GPU shader cores are traditionally not really setup to handle true function calls or dynamic dispatch. For various reasons, these shader cores tend to use a simple <a href="https://anteru.net/blog/2018/intro-to-compute-shaders/">register-only</a> <a href="https://courses.cs.washington.edu/courses/cse558/11wi/lectures/05_gpuArchShaderCores_BPS_2011.pdf">SIMD execution model</a> with no true stack. This naturally makes it trickier to have the concept of a standard ABI, and things get especially tricky if you want to support diverging function calls within a wave. Shader cores also typically use a model where waves must statically allocate all of the registers they will ever need for the entire duration of the program, as opposed to a more dynamic model that might involve spilling to the stack. Keeping that register count low is good for <a href="https://interplayoflight.wordpress.com/2020/11/11/what-is-shader-occupancy-and-why-do-we-care-about-it/">occupancy</a> (which is yet another reason why aggressive micro-optimization can help), and so it&rsquo;s important you only allocate what you truly need. This means you have a problem to solve if you want to let a program jump out to arbitrary code: the wave has to statically allocate sufficient registers <em>before</em> it can call into some function. Even if you know all of the possible places that the shader might jump to, everybody will be limited to the occupancy of the worst-case target. <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> These are not necessarily unsolveable problems, but they help explain why it&rsquo;s been easier not to rock the boat and just stick with a monolithic shader program.</p>
<h3 id="are-we-too-focused-on-micro-optimization">Are We Too Focused On Micro-Optimization?</h3>
<p>If we pull back the camera a bit, one of the broad themes we see in the rush to over-permute our shaders is a focus on micro-optimization. If your goals include &ldquo;I want to let the compiler generate its idea of the best possible shader program for a given feature set&rdquo;, then you&rsquo;re pretty much guaranteed to end up with permutation hell once you have a non-trivial feature set. In a way this is pretty natural: gating a new feature behind a permutation lets you look at things more in isolation, and give you peace of mind that whatever crazy thing you do in that shader is going to have zero impact on the 99% of materials that don&rsquo;t use it. In my experience that freedom can significantly lower the barrier of entry for new features: I don&rsquo;t mind saying &ldquo;yes&rdquo; to an artist&rsquo;s feature request if I know it&rsquo;s wrapped up in a preprocessor macro. This kind of thinking can also naturally come out of situations where you&rsquo;re optimizing some existing problematic material that your artists came up with, where you end up wanting to add a permutation that unrolls some loop or strips out a pointless texture fetch.</p>
<p>Broadly speaking, I think that breaking out of permutation jail requires a shift in mindset. Instead of looking at things one material at a time and focusing on lost cycles here and there, you need to pull back and look at GPU performance holistically. No wave of a shader program runs in isolation: we need to keep occupancy high and I$ pressure low while also reducing stalls from having too many shader switches or draw batches. We can&rsquo;t overlook techniques that require spilling to memory or splitting things up into multiple passes if in the aggregate those techniques end up boosting utilization and throughput. Perhaps it&rsquo;s ok if our simplest material is a little bit slower if we can ensure that all of our shaders end up faster overall. We also need to think more globally about the effects of our shaders on the full development pipeline: what&rsquo;s the cost to productivity if we add another permutation and double the shader count?</p>
<p>It&rsquo;s funny too because the micro-heavy approach is in some ways very counter-productive to the goals of getting &ldquo;better&rdquo; generated programs. Having so many permutations can actually make it much more difficult for a programmer to do actual hand-optimization of the shader source code, both because of the added iteration times but also because there&rsquo;s just too many possible permutations to focus on! Some of the heavy optimizations performed by compilers can also be surprisingly bad for overall performance. In particular aggressive unrolling and overlapping of instructions tends to eat up registers very quickly, which can result in reduced occupancy. In other words these optimizations might have a small reduction to the latency of a single wave but can be actively harmful to the GPU&rsquo;s efforts to boost overall throughput by cycling through different active waves. This can be quite frustrating to deal with, since sometimes it feels like you have to trick the compiler into generating code that uses fewer registers. You can&rsquo;t even totally blame the compiler really since it doesn&rsquo;t really have the full picture: it doesn&rsquo;t know what else will be happening on the GPU at the time the shader executes, and it also doesn&rsquo;t know the expected latency of a memory operation (maybe it was in cache, or maybe it wasn&rsquo;t!).</p>
<p>With all that said, it&rsquo;s a lot easier to just say &ldquo;take a global approach to GPU performance&rdquo; than it is to pull it off in practice. You likely need excellent tooling and test cases to make sure you&rsquo;re getting data that isn&rsquo;t misleading, and you also need to analyze quite a few stats to make informed decisions. Think for a moment about what you might need to consider in terms of your global GPU performance when deciding whether or not to split off another permutation of a shader, or perhaps unroll a loop in that shader. Off the top of my head, I can come up with a non-comprehensive list of considerations that&rsquo;s pretty long:</p>
<ul>
<li>Time Spent Compiling From Source
<ul>
<li>How fast or slow is the compiler itself?
<ul>
<li>Do various compile options affect compilation time?</li>
<li>Does the speed change over time with newer versions?</li>
</ul>
</li>
<li>Can we compile on multiple threads?
<ul>
<li>If so, how many cores are available on the local machine?</li>
</ul>
</li>
<li>Can we distribute across multiple machines?
<ul>
<li>If so, how many remote machines are typically available?</li>
<li>What is the latency of sending over code and spinning up compilation on a remote machine?</li>
<li>Are those remote machines available to people who aren&rsquo;t on in a central office on the same LAN?</li>
</ul>
</li>
<li>How does compilation and optimization time scale with shader code length and number of emitted instructions?</li>
<li>How much does shader compilation negatively effect the productivity of graphics programmers?
<ul>
<li>Can programmers compile only a subset of permutations when testing a change, or do they need to compile all of them?</li>
<li>How quick is a change + compile + measure loop for optimizing shaders?</li>
</ul>
</li>
<li>How much does shader compilation affect the productivity of non-programmers?
<ul>
<li>Do material artists need to recompile many shaders when creating or editing materials?</li>
<li>Are compilation results cached across the team, or does everybody have to recompile the same shaders?</li>
</ul>
</li>
</ul>
</li>
<li>Time Spent Generating Hardware ISA
<ul>
<li>Can this happen ahead-of-time (consoles), or do we need to JIT compile at runtime (PC)?</li>
<li>How fast is the JIT compile?
<ul>
<li>Does that speed change over time or with different drivers?</li>
<li>What kind of caching is available?</li>
</ul>
</li>
</ul>
</li>
<li>Number of shaders and PSOs loaded at runtime
<ul>
<li>How much memory do they consume? Can you even measure it?</li>
<li>How quickly can your code, the runtime, and the driver switch PSOs in terms of CPU time?
<ul>
<li>Does that time vary based on the bindings used for the shader?</li>
</ul>
</li>
<li>How does switching shaders affect GPU performance?</li>
</ul>
</li>
<li>Length and size of individual shaders
<ul>
<li>How does this affect runtime memory consumption?</li>
<li>How does it affect PSO creation time?</li>
<li>Does it increase pressure on the shader instruction cache, degrading performance?</li>
<li>Does having long shaders that sample many textures increase L1/L2 cache thrashing, reducing performance?</li>
<li>Does having a longer shader give the compiler more opportunities to dead-strip unnecessary instructions, and interleave memory and math operations?</li>
</ul>
</li>
<li>Number of individual passes required to render a frame
<ul>
<li>How much memory bandwidth do we consume by writing out intermediate results vs. keeping them resident in registers?</li>
</ul>
</li>
<li>Register pressure and occupancy
<ul>
<li>How many registers do our shaders consume, and what is the resulting occupancy?</li>
<li>Is lower occupancy reducing performance by not giving the hardware the ability to hide long latency memory access?</li>
<li>Is high occupancy reducing performance by causing L1 cache thrashing?</li>
<li>Can register usage and occupancy be calculated with offline tools, and are there any tools available for analyzing and optimizing register usage?</li>
</ul>
</li>
<li>ILP of the shader programs
<ul>
<li>Can the shader program hide latency of memory access by executing ALU-heavy instructions while waiting for memory operations to complete?</li>
<li>Can multiple ALU instructions execute in a pipelined manner, increasing IPC?</li>
<li>How many registers are utilized for latency hiding and pipelining?</li>
<li>Does hiding latency require unrolling loops, increasing the size and register usage of the shader program?</li>
</ul>
</li>
<li>Number of features required for creating content
<ul>
<li>What functionality do artists need to create content quickly and at a satisfying quality level?</li>
<li>What features are needed for gameplay functionality? For example, changing colors based on team or building certain kinds of UI into the material.</li>
<li>Do content creators have the ability to write their own shaders for game-specific purposes, either by hand-writing HLSL or using a shader graph?</li>
</ul>
</li>
</ul>
<p>It&rsquo;s&hellip;a lot. This list doesn&rsquo;t even consider the non-technical aspects of working with and art team to explain to them the trade-offs in terms of their workflow and what kind of features they can use. Don&rsquo;t let anybody ever tell you that this stuff is easy!</p>
<h3 id="coming-up-in-part-2">Coming Up in Part 2</h3>
<p>In the <a href="../shader-permutations-part2">final part</a> of this series, I&rsquo;m going to look at a list of techniques we can employ that potentially help us to dig ourselves at out of the hole that we created.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The PC GPU vendors all implement driver-side caches for the native ISA produced as part of creating a PSO, which can cause cold boots to take significantly longer than subsequent runs.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Metal can actually change the PSO within an <a href="https://developer.apple.com/documentation/metal/indirect_command_buffers">indirect command buffer</a>, which is very cool!&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Dynamic branching can have a similiar performance issue, where a block of code with high register usage can lower occupancy for the entire program even if that branch is never taken! This is another contributor to (over-)using permutations instead of runtime branches.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
		</item>
		
		<item>
			<title>The Shader Permutation Problem - Part 2: How Do We Fix It?</title>
			<link>https://therealmjp.github.io/posts/shader-permutations-part2/</link>
			<pubDate>Mon, 11 Oct 2021 00:00:00 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/shader-permutations-part2/</guid>
			<description>Pictured above: Paul Atreides visualizing his full tree of shader permutations and regretting his decisions
If you&amp;rsquo;ve read the previous article then you hopefully have a decent understanding of how shader permutations ended up being such a common issue. The good news is that there is some hope for the future: if we look across recent game releases as well as the features available in the latest GPUs and APIs, we do see some promising avenues for digging ourselves out of our self-imposed avalanche of compiled bytecode.</description>
			<content:encoded><![CDATA[<p><img src="/images/shaders/dune_trees.png" alt="dune screenshot showing Paul&amp;rsquo;s prescience abilitoes"></p>
<p><em><center>Pictured above: Paul Atreides visualizing his full tree of shader permutations and regretting his decisions</center></em></p>
<p>If you&rsquo;ve read <a href="../shader-permutations-part1">the previous article</a> then you hopefully have a decent understanding of how shader permutations ended up being such a common issue. The good news is that there is some hope for the future: if we look across recent game releases as well as the features available in the latest GPUs and APIs, we do see some promising avenues for digging ourselves out of our self-imposed avalanche of compiled bytecode. In my opinion nothing I&rsquo;m going to mention here is a silver bullet on its own: each technique comes with a set of trade-offs to be carefully evaluated in the context of an engine and the games that run on it. Regardless, it&rsquo;s inspiring to see smart and resourceful people come up with clever approaches that help to sidestep some of the issues that I&rsquo;ve brought up the previous article.</p>
<h3 id="only-compile-what-you-need">Only Compile What You Need</h3>
<!---
TODO: Get LE2 shader/PSO info
-->
<p>This is the simplest, oldest, and perhaps least-effective way to reduce permutation counts. The general idea is that out of your 2^N possible permutations, some subset of them are either redundant, invalid, or will never actually get used. Therefore you will reduce the set of shaders that you need to compile and load if you can strip out the unnecessary permutations. In many cases the reduction in shader permutation count can be substantial, and can be the difference between &ldquo;completely untenable&rdquo; and &ldquo;we can ship this&rdquo;. Ideally this process is something you would do offline, perhaps as part of a content processing pipeline that has knowledge of what meshes and materials are going to be used in each scene. But there have also been games/engines that have done it at runtime, essentially deferring compilation of a permutation until the scene is loaded. Either way there are some pretty obvious downsides:</p>
<ul>
<li>Determining your set of shaders offline requires your offline process to have a pretty complete understanding of both the content and how it interacts with your shader pipeline. Making changes to how that works may also require you to recompile and invoke the content processing pipeline again, as opposed to recompiling the runtime code and running the app again</li>
<li>Offline approaches may make editors and other tooling more complicated, since you now have to deal with an on-the-fly combination of mesh + material</li>
<li>On a related note, some engines are setup where they treat their shaders more like code and less like content. For instance, they might want to compile shaders using the same build system used for C++ code since it already handles dependencies and includes correctly. Moving to a system where the shaders are compiled as part of the content pipeline can potentially be a large shift.</li>
<li>If you wait until runtime to compile your shaders, you now have to either make the user wait for compilation to complete or do something else to hide the compilation time. This might involve having QA generate a cache of shaders to ship with the game, or it might even involve <a href="https://dolphin-emu.org/blog/2017/07/30/ubershaders/">using a slow and generic shader until your specialized permutation finishes compiling in the background</a>. Platforms that don&rsquo;t allow you to invoke the shader compiler at runtime can also make this approach a non-starter, at least without some kind of two-step process that discovers the shaders and then compiles an offline cache to ship with the app.</li>
<li>The amount of shaders that you generate is content-dependent, and the count could vary wildly depending on the scene/game/material setup/etc.</li>
<li>In the worst case this degenerates to compiling your full set of permutations, except that you may end up with a more complicated pipeline for generating those permutations.</li>
<li>It doesn&rsquo;t do anything to reduce your loaded shader/PSO count at runtime. If you need to rely on a small number of PSOs to implement some technique (for example, GPU-driven rendering) then this approach won&rsquo;t help you.</li>
</ul>
<p>For our engine at Ready At Dawn, we have used the offline variant of this approach with some success. When our content processing pipeline encounters a mesh, it looks at the assigned material as well as properties of the mesh itself to figure out the final shader permutation that will be required. A request to compile that shader is then kicked off, and the final bytecode is ready to load in the game. In our case the reduction is quite substantial: just our &ldquo;generic&rdquo; base material + shader set supports an unfathomable number of possible permutations, and the vast majority go unused. For the entirety Lone Echo 2 we end up compiling around 5000 pixel shaders out of a possible set of millions, and typically have around 900-1000 PSOs loaded at runtime (with about 50% of them being used in any particular frame). Unfortunately the full set of shaders to compile is still quite large, so a global change can take quite some time to churn through before the full game is rebuilt. To mitigate this we make use of distributed shader compilation that is run on idle machines. One silver lining is that it&rsquo;s quite simple to build only the set of shaders required for a particular area of the game, since the shaders are compiled as part of processing a particular chunk of the game world. This can make iteration times lower for global shader changes, but still quite far from instant. To handle tooling issues where the mesh + material combination is not known at runtime, we use &ldquo;generic&rdquo; un-permuted versions of our shaders that branch on constant buffer values for all features. As a bonus these shaders also support full real-time parameter editing, and have additional dev-only features for debugging. These shaders are <em>huge</em> though since they support every possible feature, and can be quite slow!</p>
<h3 id="run-time-specialization">Run-Time Specialization</h3>
<p><a href="https://www.khronos.org/registry/vulkan/specs/1.1-khr-extensions/html/chap10.html#pipelines-specialization-constants">Vulkan</a> and <a href="https://developer.apple.com/documentation/metal/using_function_specialization_to_build_pipeline_variants">Metal</a> both support an interesting feature called <em>specialization constants</em>, or &ldquo;spec constants&rdquo; for short (Metal calls the feature &ldquo;function specialization&rdquo;). The basic idea goes like this:</p>
<ul>
<li>You compile your shader with a global uniform value (basically like a value in a constant buffer) that&rsquo;s used in your shader code</li>
<li>When creating the PSO, you pass a value for that uniform that will be constant for all draws and dispatches using the PSO</li>
<li>The driver somehow ensures that the value you passed is used in the shader program. This might include:
<ul>
<li>Treating the value as a &ldquo;push constant&rdquo;, basically a small uniform/constant buffer that gets set from the command buffer</li>
<li>Patching a value into the compiled intermediate bytecode or vendor-specific ISA</li>
<li>Treating the value as a compile-time constant when the driver does its JIT compile and performing full optimizations (including constant folding and dead code elimination) based on that value</li>
</ul>
</li>
</ul>
<p>It&rsquo;s a pretty neat concept, since it potentially lets you avoid having to do a lot of your own compiling and instead rely on the driver&rsquo;s backend compiler to do a lot of the work for you. If you have a lot of specializations it won&rsquo;t necessarily allow you to reduce your PSO count, but it can be pretty nice in a similar way to the &ldquo;Compile What You Need&rdquo; strategy if you don&rsquo;t use your full set of possible permutations at runtime. A good example is using them to implement quality levels based on user settings: you&rsquo;re not going to need both the low-quality and high-quality PSOs at once, so you can use a spec constant to choose when you create the PSO. It should be noted though that spec constants also share one of the main issues with &ldquo;Compile What You Need&rdquo;: it won&rsquo;t reduce your runtime PSO count. You can potentially ship and load fewer shader binaries which is an improvement, but you can still run into the same problems that PSOs can cause with batching and ray tracing.</p>
<p>The other main issue with this approach (aside from lack of support in D3D12 and consoles) is that you don&rsquo;t really know which approach the driver is going to take when it handles your spec constant. Sure it makes sense that it will dead-strip as much as it can since it&rsquo;s doing that as part of its JIT compile anyway, but drivers have complicated trade-offs to deal with when it comes to doing their back-end compilation. If they take too long when initially creating your PSO then games can have slow loading times, and both devs and customers will complain. Doing further optimization on a background thread is possible, but drivers are limited in how much of the CPU they can use for this (particularly in D3D12) and it surely makes things more complicated for both the driver and developer. The worst part is that you could end up having wildly varying results (and thefore performance) based on the vendor and driver version. Sure in some ways you just have to be zen about hoping that PC drivers will do whatever they need to in the background to make things fast, but do you really want to set up your entire shader pipeline on the assumption that you&rsquo;ll get as good results as if you compiled the shader permutations beforehand? Do you really want to do that on mobile where drivers are even <em>more</em> constrained in their CPU usage?</p>
<p>One interesting variant on this approach could be to have the app manually invoke the shader compiler (or some slimmed-down version of it) to patch in the spec constant and optimize based on its value. I&rsquo;m not sure if this would be significantly faster than invoking the full compiler toolchain again, but perhaps it could be speedier since you wouldn&rsquo;t need to parse anything. This approach would work on any API as long as the compiler can run on-target, and you would have the peace of mind of knowing that the optimization and dead-stripping is for sure happening. It would also put any trade-offs regarding background compilation in the hands of the app developers, which would certainly be more consistent with the overall spirit of the &ldquo;explicit&rdquo; APIs.</p>
<h3 id="cached-material-evaluation">Cached Material Evaluation</h3>
<p>A lot of what goes into our shader permutations is typically related to combining and building shading parameters in various ways. It&rsquo;s quite common to build the &ldquo;look&rdquo; of one material by sampling multiple textures with different UV values, either to add varying levels of detail or to create meta-materials from simpler &ldquo;building blocks&rdquo;. A broad feature set for doing these operations can naturally result in a lot of shader code, which can in turn lead to shader permutations. Or alternatively an engine might allow technical artists to completely define their own logic by authoring shader graphs, in which case there are potentially unlimited numbers of compiled shaders that could be generated. If you can somehow pull this process out of your pixel shader, then you can perhaps reduce the number of shader variants that are needed for your draws. There are a few ways you might do this:</p>
<ul>
<li>Use Substance Designer or a <a href="https://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf">custom material pipeline</a> to do offline compositing and generating of your textures
<ul>
<li>Even with this you generally still want to combine tiling maps together at runtime since &ldquo;flattening&rdquo; them would consume considerably more memory</li>
</ul>
</li>
<li>Use an offline system combined with runtime virtual texturing to full generate a unique set of composited textures for all surfaces in the game (basically the Megatexture approach)
<ul>
<li>There are plenty of well-documented issues with this approach, such as disk space, streaming/transcoding cost, lack of fine texture detail, etc.</li>
</ul>
</li>
<li>Use a runtime virtual texturing system that composites/generates pages on-the-fly
<ul>
<li>More complex, you may still need permutations for the process that does the VT page generation</li>
</ul>
</li>
</ul>
<p>The end goal of these techniques is to end up with simpler pixel shaders that can just sample one set of textures without needing to do anything fancy with them. This won&rsquo;t help you for the portions of the shader that <em>don&rsquo;t</em> deal with generating shading parameters, but it can potentially cut things down quite a bit.</p>
<h3 id="replace-permutations-with-branching-and-looping">Replace Permutations with Branching and Looping</h3>
<p>On modern GPUs flow control is <a href="https://bartwronski.com/2021/01/18/is-this-a-branch/">no longer the problem that it used to be</a>. Having divergent branching and looping within a wave is still a problem due to the SIMD execution model used on GPUs, but if all threads go down the same path it can be quite efficient. Therefore if you&rsquo;ve got a material flag that enables a feature for an entire draw call, it could make a lot of sense to stick that value in a uniform/constant buffer and branch on it in the shader at runtime. This kind of branching is usually referred to as &ldquo;uniform branching&rdquo; or &ldquo;static branching&rdquo; since it guarantees that you&rsquo;re not going to have any divergence. The idea here is that we can rethink a lot of the permutation decisions that we made 10 years ago and (hopefully) reduce our total shader count after sprinkling in a lot of these static branches. If that branch is totally uniform then the shader process will legitimately skip over the instructions in the branch when the flag is disabled, and it may not even cost very many cycles to do this if the code has a dedicated scalar unit for performing those operations.</p>
<p>The poster child for this approach is the past two Doom games from id: <a href="http://advances.realtimerendering.com/s2016/Siggraph2016_idTech6.pdf">Doom 2016</a> and <a href="https://advances.realtimerendering.com/s2020/RenderingDoomEternal.pdf">Doom: Eternal</a>. In their presentations they mention that they use a forward-rendering ubershader that only results in ~100 shaders and ~350 PSOs. This is of course more than a handful but a distributed compilation system can probably tear through that many shaders in no time, making it very attractive from an iteration point of view. Personally I get quite jealous when thinking of how much simpler my life would be with only that many shaders to deal with, and I really doubt I&rsquo;m the only one! Their games run really well and look great so it&rsquo;s a pretty strong proof-of-concept that this approach can work.</p>
<p>With that said, I personally still think it can be quite difficult to achieve a low permutation count in the general case. In <a href="../shader-permutations-part1">part 1</a> we discussed why this is tricky to get right, so I won&rsquo;t go into detail again. But the main gist of it is that it can be difficult to know where you should leave a branch or loop and where you should permute/unroll. On consoles you can make pretty informed decisions by looking at a single GPU in isolation (and make use of the excellent tooling), but on PC and mobile you are in a much much tougher spot. But on the positive side having fewer permutations means fewer total programs to optimize by hand, which can potentially pay off in big ways. In particular it&rsquo;s a lot more reasonable to try to make sure that all of your permutations have a reasonable register count and occupancy&hellip;at least assuming that you have the tools available to determine these things. There are also some things that really do require permutations since they are static properties of the shader itself, and thus can&rsquo;t be replaced with branching. In particular this includes the set of inputs or outputs of a shader (interpolants, render targets), usage of discard, per-sample execution, and forced early-z.</p>
<p>I think what would truly help in using this approach is to have a lot of discipline and focus in your material/shader feature set. The less features you need to support, the easier it&rsquo;s going to be to make sensible decisions about where you should permute. I would imagine you would have to get used to saying &ldquo;no&rdquo; to a lot of feature requests, since every feature added can potentially affect the performance of your ordinary baseline materials unless it&rsquo;s gated behind a permutation. For those of us with very broad material feature sets it seems difficult to get to that point without being very disruptive to existing workflows, and/or without giving up things that make a significant contribution to the look of a game.</p>
<p>Another thing that could really help is good automated processes for helping to figure out which permutations are truly worth it vs. just leaving in a branch. Unfortunately the testing space for this is quite large: you can have N different materials and M different video cards with a huge amount of permutation options, so it&rsquo;s not feasible to test exhaustively. But perhaps reasonable subsets can be derived that give a decent level of confidence.</p>
<p>One last thing I would like to note is that you have to watch out for drivers taking your handful of shaders and permuting them behind your back. Drivers have been known to peek at values from constant/uniform buffers and use them to silently flatten branches and unroll loops. This can lead you to think that your shaders are well-optimized&hellip;until you run them on a driver that <em>doesn&rsquo;t</em> do this and the performance is worse. It&rsquo;s tougher for drivers to do this in D3D12 and Vulkan since there are more avenues to load data into shaders, but it&rsquo;s still feasible if the driver is sufficiently motivated.</p>
<h3 id="deferred-rendering-and-other-techniques-for-splitting-up-shading">Deferred Rendering, and Other Techniques For Splitting Up Shading</h3>
<p>When people first learn about deferred rendering, there&rsquo;s usually a lot of focus on how it helps to achieve greater dynamic lighting counts by decoupling the lighting process from your geometry. While this was true at the time it became popular, IMO the real secret weapon of deferred is that it&rsquo;s a way of chopping up a mesh&rsquo;s rendering process into at least two different distinct phases. In effect this is really a way of <em>modularizing</em> your shader code, despite the fact that individual shader programs are still monolithic. You can conceptually think of most deferred techniques as splitting up a single gigantic pixel shader function into two separate functions: one that calculates the parameters that feed into the lighting phase (surface normals, albedo, roughness, etc.) and another that takes those parameters and computes how much light reflects off the surface given a collection of lighting sources. With this mental model you can even think of the G-Buffer as being a sort of &ldquo;ABI&rdquo; that allows your first phase to pass its arguments to the second, except in this case it does so by writing out many megabytes of per-pixel data to a collection of render target textures.</p>
<p>The effect on permutation count can be significant, since it effectively causes us to split our pixel shader into two feature groups instead of one. Let&rsquo;s see what our <a href="../shader-permutations-part1/#whats-a-few-shaders-among-friends">permutated pixel shader from part 1</a> looks like before and after converting to deferred:</p>
<p>$$ PShader(NormalMap[Off, On] * LightType[Point, Spot, Area] * Shadows[Off, On]) $$</p>
<p>$$ GBuffer(NormalMap[Off, On]) + Lighting(LightType[Point, Spot, Area] * Shadows[Off, On]) $$</p>
<p>Splitting up our shader turned one of those multiplies into an add, causing us to drop from 12 pixel shader permuations down to 8. Saving 4 shaders might not sound like a lot, but keep in mind the delta can be <em>significantly</em> larger as the number of features grow. In practice this can be a huge win for permutation counts, and the modularization can have all kinds of other halo effects. For example you can potentially optimize the code for your G-Buffer and Lighting phases separately without having to consider interactions, and the peak register usage will often be lower as a result of splitting things into smaller shaders with fewer steps. Shader compilers may also be able to chew through your shaders more quickly since they are smaller, since optimization time can grow non-linearly with code length.</p>
<p>In practice there <a href="https://software.intel.com/content/www/us/en/develop/articles/deferred-rendering-for-current-and-future-rendering-pipelines.html">all</a> <a href="http://jcgt.org/published/0002/02/04/">kinds</a> of <a href="https://therealmjp.github.io/posts/bindless-texturing-for-deferred-rendering-and-decals/">flavors</a> of <a href="http://filmicworlds.com/blog/visibility-buffer-rendering-with-material-graphs/">deferred</a>, and even more ways of splitting things up the final rendering pipeline into separate phases with distinct shader programs. For instance, it&rsquo;s completely possible to only pull out shadow map evaluation into its own deferred passes that only requires a depth buffer, while keeping everything else in a combined forward pixel shader. Or going in the other direction, with deferred you have the option to either do all lighting in a single combined draw or dispatch, vs. further splitting the lighting passes into multiple sub-steps. Doing 1 pass per light source can allow for trivial shader specialization based on the properties of the light source while avoiding combinatorial issues, while doing separate passes for things like sampling ambient diffuse lighting can also provide opportunities for using async compute.</p>
<p>But of course, everything is a trade-off. One thing that most deferred renderers need to do at some point in the frame is write out results to memory and then read them back into registers<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. In the more classic deferred setups this happens when the G-Buffer is written out to render target textures, and then gets read back in during a lighting pass. If you were to compare that to a modern forward renderer you would likely see that the &ldquo;G-Buffer&rdquo; still exists in the forward pipeline, it just ends up sitting in registers that store the values passed between two phases of the same shader program. When you think about things in those terms deferred might not sound so appealing! After all, isn&rsquo;t it better to keep data in ultra-fast registers rather than spilling them out to memory and then reading them back in again? But of course things are much more complicated than that when it comes to performance and renderer design, as we discussed in part 1 when we <a href="../shader-permutations-part1/#are-we-too-focused-on-microoptimization">talked about taking a global approach to GPU performance</a>. Perhaps using more bandwidth and memory ends up being a net negative, or perhaps the shorter/higher-occupancy shader programs enabled by modularization win out in the end. Perhaps letting the compiler optimize across a giant forward pixel shader enables some serious wins, or perhaps the I$ misses add up and limit your performance. Or perhaps you end up designing your deferred renderer in a way such that you can analyze the properties of your G-Buffer on per-tile basis, and use that analysis to manually propogate optimizations into the deferred shader, thus gaining back many of the benefits that the compiler&rsquo;s optimizer might have afforded you. Or maybe your forward renderer completely dies due to pixel shader quad overdraw caused by tiny triangles, whereas the deferred renderer handles it more gracefully. It&rsquo;s clearly not a straighforward proposition, and the continued success of deferred certainly suggests that it can win out in many cases.</p>
<h3 id="offline-linking">Offline Linking</h3>
<p>In part 1 we discussed how the <a href="../shader-permutations-part1/#the-monolithic-shader-program">monolithic shader compilation model</a> contributed to permutation explosions. If you can&rsquo;t compile and optimize portions of a shader program individually, then permututations that only affect that portion end up requiring the entire program to be recompiled. What if there was some hot new technology from the 1970&rsquo;s that would let us compile separate binaries and then &ldquo;link&rdquo; them together into one combined shader program? That would be be pretty neat, right? 😉</p>
<p>The good news is that DXIL and SPIR-V support this sort of thing! For D3D12/DXIL all of the functionality to compile and link libraries is present in <a href="https://github.com/microsoft/DirectXShaderCompiler">dxc</a>, either via the command line or through its API. See my <a href="../dxil-linking">recent post about it</a> for more details on how it works in practice. The story for SPIR-V though is a little more complicated. <a href="https://github.com/KhronosGroup/SPIRV-Tools">SPIR-V Tools</a> has a utility for doing the linking, provided you have some compiled SPIR-V that&rsquo;s been generated with the appropriate <a href="https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#Linkage_Type">linkage type</a>. At the time of writing however, neither dxc nor glslang are capable of generating SPIR-V with the appropriate declarations. Therefore if you want to write HLSL or GLSL and link the results together, there doesn&rsquo;t seem to be a working option for doing that. However I&rsquo;ve been told that other projects that generate SPIR-V through other means (such as <a href="https://github.com/EmbarkStudios/rust-gpu">rust-gpu</a>) have been able to successfully link using the SPIR-V tools. Either way it&rsquo;s cool that in both cases the linking happens in open-source toolchains using documented IL representations. <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>There&rsquo;s a big question mark when it comes to linking: for compiling a zillion permutations, would it actually reduce the compile times? I don&rsquo;t have any data to draw even some basic conclusions from, so I think we&rsquo;ll have to wait for someone to do some more comprehensive testing. Ultimately the situation might not be straightforward depending on the inputs to compiling and linking. There&rsquo;s also the potential for a linking step to make the compilation pipeline more complicated, just like it does for C/C++. In particular it might convert what was once a straightforward parallel operation into a tree with dependencies and sync points.</p>
<p>The other thing that&rsquo;s both good and bad is that linking doesn&rsquo;t really change the runtime situation at all. Since the linking can happen fully offline and outside of the API, the API and driver don&rsquo;t really have to know or care that things were linked together (although there&rsquo;s always some possibility that linking generates new bytecode patterns that can cause drivers to choke). This means there&rsquo;s no need to wait for new API or driver support, which is a good thing. It also means there&rsquo;s no reason to expect a significant delta in performance between a linked and fully-compiled program, at least assuming there&rsquo;s a post-link optimization step (not doing one would introduce some interesting trade-offs). But it also means that it won&rsquo;t do anything to reduce the number of shaders and PSOs that have to loaded at runtime, which is not-so-great. Therefore it won&rsquo;t be a cure if your engine has a case of PSO-itis.</p>
<h3 id="true-function-calls-and-dynamic-dispatch">True Function Calls and Dynamic Dispatch</h3>
<p>Having a runtime function call combined with dynamic dispatch is potentially more interesting than a linking step, but it&rsquo;s also much more of a radical change. While linking can happen offline with no driver input, dynamic dispatch for sure requires the driver and the hardware to be on board. The &ldquo;stuff everything in a statically-allocated chunk of registers&rdquo; model used by most GPUs certainly doesn&rsquo;t lend itself to true dynamic dispatch, and it&rsquo;s easy to imagine that various constraints on the programming might be necessary to keep performance from falling off a cliff.</p>
<p>The good news is that on PC we sort-of have this right now! The bad news is that it&rsquo;s <em>very</em> constrained, and only works for ray tracing. I&rsquo;m less familar with the specifics for Vulkan, but in <a href="https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html">D3D12/DXR</a> it works by essentially having a sort of &ldquo;run-time linker&rdquo; step. Basically you compile a bunch of functions into a DXIL binary using a &ldquo;lib&rdquo; target (just like you would for offline linking), and then at runtime you assemble all of your pieces together into a combined state object. Later on when you call DispatchRays the driver is able to dynamically execute the right hit/miss/anyhit/etc. shader since it was linked into the state object. There <em>is</em> a <a href="https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#callable-shaders">callable shaders feature</a> that can be used without any actual ray tracing, however it still needs to be used from within a ray generation shader that was kicked off from DispatchRays. In other words: it&rsquo;s usable right now for compute-like shaders, but currently can&rsquo;t be used within the graphics pipeline. With any luck this could change in the future! <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>Currently I&rsquo;m not aware of anyone that has tried using DXR callable shaders to replace permutations, or at least that has published their findings. Personally I have not experimented with them, so I&rsquo;m not yet sure how they might work out in practice. With that said, we can still probably make some reasonable assumptions based on what we know about how they work and what kind of implications they might have on usage and performance:</p>
<ul>
<li>They currently require a runtime &ldquo;link&rdquo; step where all possible callables are assembled into a single state object and associated shader table, either by creating a new one or by calling AddToStateObject to append additional shader binaries. This has implications on streaming, since loading in new callable shaders may require modifying a global state object being used for rendering. Either way you need to be careful where you interact with state objects since it can involve a JIT compile, just like creating a normal PSO.</li>
<li>We probably should not expect any magic workarounds for static register allocation: if a callable shader requires many registers, we can likely expect for the occupancy of the entire batch to suffer. It&rsquo;s possible that GPUs could diverge from this model in the future, but that could come with all kinds of potential pitfalls (it&rsquo;s not like they&rsquo;re going to start spilling to a stack when executing thousands of pixel shader waves).</li>
<li>The way the state object spec is setup gives drivers some room to do transformations if necessary. Since the full set of call targets is known when creating or modifing a state object, it&rsquo;s feasible that the driver might make a decision to &ldquo;flatten&rdquo; a CallShader into a branch with inlined function calls.</li>
<li>There are some subtle implications depending on whether this kind of functionality is implemented as a function call from the same wave, or whether it implies that a wave is launched that runs the function being called. In particular the DXR model implies the latter, since hit shaders can set their own resource context and bindings via a local root signature. There can also be implications around how divergence is handled (different threads in the same wave calling different functions), and whether or not threads are allowed to be &ldquo;re-packed&rdquo; into a full wave. For replacing existing permutations I would expect the function calls to be completely uniform, and thus a simpler &ldquo;run the function in the same thread&rdquo; approach is sufficient and probably more desirable. Having a resource context is also not necessary as long as bindless is being used.</li>
</ul>
<p>We&rsquo;ll have to watch this area closely in the future. With any luck this kind of functionality can be extended to graphics pipelines and standard compute shaders, which could be a very compelling option for those of us that are juggling too many permutations. Metal is also currently ahead of the curve on this front by offering <a href="https://developer.apple.com/videos/play/wwdc2021/10229/">function pointers</a> that can be called from any shader stage. Perhaps this can serve as inspiration for PC and Android!</p>
<h3 id="summary">Summary</h3>
<table>
<thead>
<tr>
<th>Technique</th>
<th>Pros</th>
<th>Cons</th>
<th>Platforms</th>
</tr>
</thead>
<tbody>
<tr>
<td>Compile What You Need</td>
<td>Less offline compiling, no changes needed to renderer</td>
<td>No reduction in PSOs, might not reduce shader count enough, may need offline analysis</td>
<td>Any</td>
</tr>
<tr>
<td>Run-Time Specialization</td>
<td>Significantly less offline compiling, no changes needed to renderer</td>
<td>May add to PSO creation time, driver might not optimize as much as you want</td>
<td>Vulkan, Metal</td>
</tr>
<tr>
<td>Cached Material Evaluation</td>
<td>Less offline compiling and lower PSO counts, could speed up GPU performance</td>
<td>Complex and potentially invasive to the renderer, only affects shading parameter generation</td>
<td>Any</td>
</tr>
<tr>
<td>Runtime Branching And Looping</td>
<td>Significantly less offline compiling and lower PSO counts</td>
<td>Potential performance implications, may put serious contraints on material features, no shader graphs</td>
<td>Any</td>
</tr>
<tr>
<td>Deferred Rendering</td>
<td>Less offline compiling and PSOs, more modular code, could be better for GPU performance</td>
<td>Very invasive to the renderer, may not be a good fit for certain hardware and use cases</td>
<td>Any</td>
</tr>
<tr>
<td>Offline Linking</td>
<td>Potentially quicker offline compiling, no changes needed to renderer</td>
<td>May not be faster to compile, doesn&rsquo;t reduce PSOs, may reduce GPU performance if not optimized post-link</td>
<td>D3D12, Vulkan, Metal</td>
</tr>
<tr>
<td>Dynamic Function Calls</td>
<td>Significantly less offline compiling and PSO counts, opens the door to other new techniques</td>
<td>Likely worse for GPU performance, requires changes to how your engine handles shaders and PSOs</td>
<td>D3D12 + Vulkan (Compute-Only), Metal (Full Support)</td>
</tr>
</tbody>
</table>
<h3 id="final-thoughts">Final Thoughts</h3>
<p>That&rsquo;s it for the series! I hope it was an enjoyable read, and that it helped to give some additional context regarding the ongoing battle against shader combinatorial explosion. Please leave a comment or reach out on Twitter if you have any additional thoughts or insights, or if you take issue with anything I&rsquo;ve written here.</p>
<p>I&rsquo;d also like to quickly think everyone that read these posts early and provided valuable feedback and insight:</p>
<ul>
<li><a href="https://blog.mecheye.net/">Jasper St. Pierre</a></li>
<li><a href="https://www.reedbeta.com/">Nathan Reed</a></li>
<li><a href="https://twitter.com/jhaberstro">Jedd Haberstro</a></li>
<li><a href="https://alextardif.com/">Alex Tardiff</a></li>
<li><a href="https://twitter.com/rcalocao">Rolando Caloca</a></li>
<li><a href="https://www.jeremyong.com/">Jeremy Ong</a></li>
<li><a href="https://www.sebastiansylvan.com/">Sebastian Sylvan</a></li>
<li><a href="https://twitter.com/gavkar">Gokhan Avkarogullari</a></li>
<li>Joel de Vahl</li>
<li><a href="http://www.palgorithm.co.uk/">Sam Martin</a></li>
<li><a href="https://twitter.com/frogblast">Richard Schreyer</a></li>
<li><a href="https://www.selfshadow.com/">Stephen Hill</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Many mobile GPUs have fast on-chip framebuffer memory that can be accessed through special mechanisms. This can allow deferred renderers to keep their G-Buffer entirely in on-chip memory rather than slower off-chip memory, potentially changing the trade-offs that are mentioned here.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>D3D11 and fxc actually added support for <a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/using-shader-linking">offline linking</a> in the Win 8 era. Unfortunately it was rather complicated, and I&rsquo;m not sure if it ever got heavy usage.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>D3D11 actually launched with a pseudo-dynamic-linking feature that was called <a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/overviews-direct3d-11-hlsl-dynamic-linking">Dynamic Shader Linkage</a> in D3D11. It was a <a href="https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.19.1%20Overview">noble effort</a>, and had some really interesting and forward-looking ideas. In particular it tried to save the developer from having to somehow know the right magic conditions for when to permute and when not to, and it did this by essentially kicking that decision up the driver. In order to do this though it had some pretty wacky syntax. Basically you would define interfaces with methods, and then declare N classes that implemented those interfaces and their methods. This let the offline compiler (fxc) figure out all the possibilities for what implementation might be invoked at each call site into an interface, and then it could present the driver&rsquo;s backend compiler with a precompiled set of possibilities for the callee with full optimization applied across the function boundary. At runtime you would then use some rather complicated APIs to choose which class implementation would ultimately get used when you bound a shader. In hindsight it&rsquo;s not too hard to see why this failed. The class/interface HLSL additions was quite a departure from previous shader code, and rendered it incompatible when trying to use it across platforms. It also doesn&rsquo;t really solve the issue of needing to compile N shaders, since that permutation loop was really just getting moved inside of the shader compiler (it wasn&rsquo;t actually compiling the class implementation methods separately). On top of that, it doesn&rsquo;t really guarantee that the driver won&rsquo;t just end up creating N full permutations of your shader program at runtime. This certainly would have been the easiest path forward for them, and may have been the best option for various reasons.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
		</item>
		
		<item>
			<title>Linking DXIL Binaries Using DXC</title>
			<link>https://therealmjp.github.io/posts/dxil-linking/</link>
			<pubDate>Tue, 05 Oct 2021 00:00:00 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/dxil-linking/</guid>
			<description>While doing some research for a different blog post, I happened to discover that DXC is capable of linking together multiple compiled DXIL binaries into single complete shader program of any stage (vertex, pixel, compute, etc.). I had known that this was a thing you could do for D3D11 with the old fxc-based compiler stack, but I wasn&amp;rsquo;t aware that DXC could now do it as well. It turns out that the new lib_6_x targets aren&amp;rsquo;t just usable for creating DXR state objects: you can also compile whatever you want into libraries and link them together into a full binary for a non-raytracing shader.</description>
			<content:encoded><![CDATA[<p>While doing some research for a different blog post, I happened to discover that <a href="https://github.com/microsoft/DirectXShaderCompiler/">DXC</a> is capable of linking together multiple compiled DXIL binaries into single complete shader program of any stage (vertex, pixel, compute, etc.). I had known that this was a <a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/using-shader-linking">thing you could do</a> for D3D11 with the old fxc-based compiler stack, but I wasn&rsquo;t aware that DXC could now do it as well. It turns out that the new <code>lib_6_x</code> targets aren&rsquo;t just usable for creating DXR state objects: you can also compile whatever you want into libraries and link them together into a full binary for a non-raytracing shader. Unfortunately there doesn&rsquo;t seem to be any documentation that I can find for this feature, my only reference for getting this to work was <a href="https://github.com/microsoft/DirectXShaderCompiler/blob/master/tools/clang/unittests/HLSL/LinkerTest.cpp">the unit tests</a> from the DXC repo. To help out with that, I&rsquo;ll briefly explain the basics of how it works.</p>
<p>As I mentioned above, if you&rsquo;re going to link things then you&rsquo;re going to want to compile to a library target rather than one of the shader stage targets (<code>ps_6_x</code>, <code>vs_6_x</code>, etc.). Let&rsquo;s say we want to create a full pixel shader where we can call into an external function that we can link in later. You would first want to setup an entry point like this in a file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Entry.hlsl
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// This is the function we&#39;re going to link to
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">float4</span> <span class="nf">LibFunc</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// This is the entry point for our pixel shader
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">[</span><span class="n">shader</span><span class="p">(</span><span class="s">&#34;pixel&#34;</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"><span class="n">float4</span> <span class="n">PSMain</span><span class="p">(</span><span class="n">in</span> <span class="n">PSInput</span> <span class="n">input</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target0</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">LibFunc</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>You would then want to compile this with the <code>lib_6_6</code> target (or the equivalent of whatever shader model you&rsquo;re targetting) in order to produce a binary DXIL blob containing the compiled bytecode. For the implementation of <code>LibFunc()</code> we&rsquo;ll now want to setup another file with this code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Func.hlsl
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">export</span> <span class="n">float4</span> <span class="nf">LibFunc</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">float4</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Note the <code>export</code> keyword. By default the compiler won&rsquo;t give your functions external linkage when compiling to a lib, so you&rsquo;ll want to add that keyword for any functions that you want to link to (you can also pass <code>-default-linkage external</code> when compiling but that&rsquo;s quite a hammer). You would once again compile this to the <code>lib_6_6</code> target to get another DXIL binary.</p>
<p>To link these two binaries together, you&rsquo;ll want to create an instance of <code>IDxcLinker</code>, register your libraries with it, and then instruct it to create a new linked binary containing a complete pixel shader:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">IDxcBlob</span><span class="o">*</span> <span class="n">entryLib</span> <span class="o">=</span> <span class="n">CompileLib</span><span class="p">(</span><span class="s">&#34;Entry.hlsl&#34;</span><span class="p">,</span> <span class="s">&#34;lib_6_6&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">IDxcBlob</span><span class="o">*</span> <span class="n">funcLib</span> <span class="o">=</span> <span class="n">CompileLib</span><span class="p">(</span><span class="s">&#34;Func.hlsl&#34;</span><span class="p">,</span> <span class="s">&#34;lib_6_6&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">IDxcLinker</span><span class="o">*</span> <span class="n">linker</span> <span class="o">=</span> <span class="n">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">CheckHResult</span><span class="p">(</span><span class="n">DxcCreateInstance</span><span class="p">(</span><span class="n">CLSID_DxcLinker</span><span class="p">,</span> <span class="n">IID_PPV_ARGS</span><span class="p">(</span><span class="o">&amp;</span><span class="n">linker</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">CheckHResult</span><span class="p">(</span><span class="n">linker</span><span class="o">-&gt;</span><span class="n">RegisterLibrary</span><span class="p">(</span><span class="sa">L</span><span class="s">&#34;EntryLib&#34;</span><span class="p">,</span> <span class="n">entryLib</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">CheckHResult</span><span class="p">(</span><span class="n">linker</span><span class="o">-&gt;</span><span class="n">RegisterLibrary</span><span class="p">(</span><span class="sa">L</span><span class="s">&#34;FuncLib&#34;</span><span class="p">,</span> <span class="n">funcLib</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="n">wchar</span><span class="o">*</span> <span class="n">names</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="sa">L</span><span class="s">&#34;EntryLib&#34;</span><span class="p">,</span> <span class="sa">L</span><span class="s">&#34;FuncLib&#34;</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">IDxcOperationResult</span><span class="o">*</span> <span class="n">result</span> <span class="o">=</span> <span class="n">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">CheckHResult</span><span class="p">(</span><span class="n">linker</span><span class="o">-&gt;</span><span class="n">Link</span><span class="p">(</span><span class="sa">L</span><span class="s">&#34;PSMain&#34;</span><span class="p">,</span> <span class="sa">L</span><span class="s">&#34;ps_6_6&#34;</span><span class="p">,</span> <span class="n">names</span><span class="p">,</span> <span class="n">ArraySize_</span><span class="p">(</span><span class="n">names</span><span class="p">),</span> <span class="n">nullptr</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">result</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">IDxcBlob</span><span class="o">*</span> <span class="n">fullShader</span> <span class="o">=</span> <span class="n">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">HRESULT</span> <span class="n">status</span> <span class="o">=</span> <span class="n">S_OK</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">CheckHResult</span><span class="p">(</span><span class="n">result</span><span class="o">-&gt;</span><span class="n">GetStatus</span><span class="p">(</span><span class="o">&amp;</span><span class="n">status</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">SUCCEEDED</span><span class="p">(</span><span class="n">status</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">result</span><span class="o">-&gt;</span><span class="n">GetResult</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fullShader</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">IDxcBlobEncoding</span><span class="o">*</span> <span class="n">linkerErrorMsg</span> <span class="o">=</span> <span class="n">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">result</span><span class="o">-&gt;</span><span class="n">GetErrorBuffer</span><span class="p">(</span><span class="o">&amp;</span><span class="n">linkerErrorMsg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">OutputErrorMessage</span><span class="p">(</span><span class="n">linkerErrorMsg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>That&rsquo;s really it! If the operation succeeds, then <code>fullShader</code> will contain a complete DXIL binary that you can use to create a PSO the usual way. I haven&rsquo;t done any testing yet to see if linking is actually any faster than doing a full compile, but if it is faster then it could potentially be an avenue for reducing the compile times for excessive shader permutations.</p>
<p>One major limitation to be aware of is that resources can <em>not</em> be a parameter or return value for exported functions. This is a real bummer, since it will require workarounds for real code. The only way to coordinate on resources right now is to either use the fancy new <a href="https://microsoft.github.io/DirectX-Specs/d3d/HLSL_ShaderModel6_6.html#dynamic-resource">dynamic resources functionality</a> from Shader Model 6.6, or have the lib that uses the resource declare it globally. You can also declare the same resource in both libs, in which case things will merge correctly as long as they use the exact same register assignment:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Entry.hlsl
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">Texture2D</span> <span class="nl">MyTex</span> <span class="p">:</span> <span class="k">register</span><span class="p">(</span><span class="n">t0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">float4</span> <span class="nf">LibFunc</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="n">shader</span><span class="p">(</span><span class="s">&#34;pixel&#34;</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"><span class="n">float4</span> <span class="n">PSMain</span><span class="p">(</span><span class="n">in</span> <span class="n">PSInput</span> <span class="n">input</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target0</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">LibFunc</span><span class="p">()</span> <span class="o">+</span> <span class="n">MyTex</span><span class="p">[</span><span class="n">uint2</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Func.hlsl
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">Texture2D</span> <span class="nl">MyTex</span> <span class="p">:</span> <span class="k">register</span><span class="p">(</span><span class="n">t0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">export</span> <span class="n">float4</span> <span class="nf">LibFunc</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">float4</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="n">MyTex</span><span class="p">[</span><span class="n">uint2</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">)];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Happy linking!</p>
]]></content:encoded>
		</item>
		
		<item>
			<title>Read My Chapter in Ray Tracing Gems II!</title>
			<link>https://therealmjp.github.io/posts/rtg2-bindless/</link>
			<pubDate>Sun, 22 Aug 2021 00:00:00 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/rtg2-bindless/</guid>
			<description>Ray Tracing Gems II was released a few weeks ago, and like the first one it&amp;rsquo;s free to download as a PDF from the publisher&amp;rsquo;s website. There are 50 articles in this thing written by over 80 authors, so I think it&amp;rsquo;s safe to say that this book is absolutely jam-packed with useful knowledge and techniques related to ray tracing.
Yours truly contributed a chapter called &amp;ldquo;Using Bindless Resources With DirectX Raytracing&amp;rdquo;, which as you&amp;rsquo;d expect is all about using bindless techniques in the context of DXR.</description>
			<content:encoded><![CDATA[<p><img src="/images/rtg2-bindless/rtg2-bindless.png" alt="comparison of computing irradiance from a directional light with L1 SH and L2 SH projections"></p>
<p><a href="https://www.realtimerendering.com/raytracinggems/rtg2/index.html">Ray Tracing Gems II</a> was released a few weeks ago, and like the first one it&rsquo;s free to <a href="https://link.springer.com/content/pdf/10.1007%2F978-1-4842-7185-8.pdf">download as a PDF</a> from the publisher&rsquo;s website. There are 50 articles in this thing written by over 80 authors, so I think it&rsquo;s safe to say that this book is absolutely jam-packed with useful knowledge and techniques related to ray tracing.</p>
<p>Yours truly contributed a chapter called &ldquo;Using Bindless Resources With DirectX Raytracing&rdquo;, which as you&rsquo;d expect is all about using bindless techniques in the context of DXR. I had already written a simple <a href="https://github.com/TheRealMJP/DXRPathTracer">open source path tracer</a> in DXR which uses bindless for all SRVs, so it seemed like a natural fit for me to write full article that dives into all of the details required to make that happen. I also wasn&rsquo;t aware of a comprehensive article or tutorial explaining the ins and outs of bindless in D3D12, and I thought my article could help fill that niche. Hopefully I was successful! If you do end up reading it, feel free to let me know if you have any thoughts or have any lingering questions that I can help clear up.</p>
<p>I also wanted to use this blog post as an opportunity to emphasize a few things in an informal way, just in case they don&rsquo;t fully come through in the article:</p>
<ul>
<li>Bindless is great, it really is! There are downsides of course (mostly on the tooling and debugging side of things), but I firmly believe the pros outweigh the cons. I don&rsquo;t really see any future for the older binding models.</li>
<li>It&rsquo;s hard to oversell how bad of an experience it is dealing with multiple root signatures and descriptor tables/ranges compared to bindless.  Once you go bindless a lot of that complexity just disappears, which could end up being a good thing for people that are new to D3D12/Vulkan or graphics programming in general.</li>
<li>On a related note: just don&rsquo;t bother with local root signatures in DXR, at least for binding descriptor tables. Ray tracing is inherently global, and it makes so much sense to have global access to all of your resources/descriptors.</li>
<li>In the article I mention how the indirection offered by using descriptor indices can let you do some really cool things. Basically as long as you keep your descriptor index persistent, you&rsquo;re free to update the descriptor located at the index without having to also go and update whatever data structures have that index in it. Got a ring buffer system where you swap to a different buffer when the contents are updated by the CPU? Just update the descriptor when you do that and everything keeps working great! Same goes for updating textures due to streaming in new mips, or swapping between aliased render target textures as part of an adaptive resolution system.</li>
<li>Bindless is awesome for CPU performance and really helps unlock the original dream of low CPU overhead that was promised by D3D12 and Vulkan. Once you&rsquo;ve moved all of your &ldquo;binding&rdquo; out of API functions and into your own user-managed memory you can do things <em>much</em> more quickly, and you can really fly through your draw calls. Combine that with multithreaded command buffer recoding and you can do a ton of draws and dispatches with very low latency.</li>
</ul>
<p>Finally, I want to finish with a note addressing Shader Model 6.6 and the new &ldquo;dynamic resource binding&rdquo; feature. When I started working on the article it was March of 2020, since RTG II was originally supposed to be released that year. At the time the idea of a new HLSL feature that makes it simple and trivial to access bindless descriptors was little more than some <a href="https://github.com/microsoft/DirectXShaderCompiler/issues/1067">hopes and dreams in a GitHub issue</a>, and I thought the situation would be the same by the time the release date rolled around. I did not expect the book release to be pushed back, I <em>really</em> did not expect for Shader Model 6.6 to be <a href="https://devblogs.microsoft.com/directx/in-the-works-hlsl-shader-model-6-6/">announced</a> before the year end, and I <em>definitely</em> did not expect for the D3D team to release an awesome new <a href="https://devblogs.microsoft.com/directx/gettingstarted-dx12agility/">Agility SDK</a> that allowed for SM 6.6 to be deployed to end user machines without waiting for Windows updates. But of course all of those things happened, when meant by the time the chapter came out people were already using dynamic resources in their code. It&rsquo;s too bad, but it was of course bound to happen eventually. For those not using the latest-and-greatest the article should still help with spelling out the right HLSL and root signature incantations required for pre-SM6.6 bindless, and those that are on the bleeding edge can simply ignore that part and make use of the other content. On the plus side: dynamic resources are really really great. They fix a lot of little rough edges that show up when doing bindless the old way, and take an even further step towards simplifying and reducing the number of root signatures.</p>
<p>Thanks for reading!</p>
]]></content:encoded>
		</item>
		
		<item>
			<title>Approximating Subsurface Scattering With Spherical Gaussians</title>
			<link>https://therealmjp.github.io/posts/sss-sg/</link>
			<pubDate>Sun, 09 Feb 2020 00:00:00 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/sss-sg/</guid>
			<description>In my previous post, where I gave a quick overview of real-time subsurface scattering techniques, I mentioned that one of the potential downsides of preintegrated subsurface scattering was that it relied on sampling a precomputed lookup texture for storing the appropriate &amp;ldquo;wrapped&amp;rdquo; or &amp;ldquo;blurred&amp;rdquo; lighting response for a given lighting angle and surface curvature. Depending on the hardware you&amp;rsquo;re working with as well as the exact details of the shader program, sampling a lookup texture can be either a good or a bad thing.</description>
			<content:encoded><![CDATA[<p>In my <a href="../sss-intro/">previous post</a>, where I gave a quick overview of real-time subsurface scattering techniques, I mentioned that one of the potential downsides of preintegrated subsurface scattering was that it relied on sampling a precomputed lookup texture for storing the appropriate &ldquo;wrapped&rdquo; or &ldquo;blurred&rdquo; lighting response for a given lighting angle and surface curvature. Depending on the hardware you&rsquo;re working with as well as the exact details of the shader program, sampling a lookup texture can be either a good or a bad thing. If you have bandwidth to spare and/or your shader is mostly bottlenecked by ALU throughput, then a lookup texture could be relatively cheap. In other scenarios it could singificantly increase the average execution time of the shader program. Either way it tends to make your life as a programmer a bit more difficult, especially when it comes to providing a suitable workflow for content creators that use your engine. In particular things get complicated if you want to allow these creators to use customizable (or arbitrary) diffusion profiles, and even more complicated if they want to be able to vary the profile spatially (imagine trying to nail the look of intricate makeup without being able to vary the scattering parameters).</p>
<p>If you&rsquo;re skilled in the dark arts of approximating complex integrals with simpler functions, you could probably see yourself doing that for the lookup texture generated by a single diffusion kernel (and like I <a href="http://c0de517e.blogspot.com/2011/09/mathematica-and-skin-rendering.html">mentioned last time</a> Angelo Pesce already took a stab at this for the kernel used in the original implementation of preintegrated SSS). However you would probably be less enthusiastic about doing this for a whole set of lookup textures. Instead, wouldn&rsquo;t it would be much nicer if we could work with a more analytical approach that could approximate a wide range of scattering profiles without any precomputation? The rest of this post will talk about one possible way of achieving this using spherical gaussians.</p>
<h1 id="blurring-a-light-with-an-sg">Blurring A Light With an SG</h1>
<p>Unfortunately I haven&rsquo;t come up with any magical solutions for how to analytically compute the result of irradiance being convolved with a diffusion profile, even with the constraints used in the original preintegrated subsurface scattering technique (point lights, and making the assumption that the point being shaded is on the surface of a sphere). However, it turns out that we can achieve this somewhat indirectly by instead blurring the <em>incident radiance</em> and computing the resulting irradiance from that. It&rsquo;s not the same thing conceptually, but it does produce similar results to what we&rsquo;re after. The most obvious choice to do this kind of operation is probably spherical harmonics (SH): with SH we store spherical signals in the frequency domain, which makes it trivial to perform convolutions on the data (this is in fact how irradiance is <a href="https://cseweb.ucsd.edu/~ravir/papers/envmap/envmap.pdf">traditionally computed</a> from a set of SH coefficients representing incoming radiance). However this frequency domain representation means that we are unable to represent high-frequency components without storing and working with very large numbers of coefficients. In other words an SH approach might work well for something like an environment probe used for ambient lighting, but it can be pretty inaccurate when used for a punctual light source. To show you what I mean, here&rsquo;s a comparison showing what the irradiance from a simple directional light looks like when computed analytically versus being projected onto L1 SH with 4 coefficients, and L2 SH with 9 coeffecients:</p>
<p><img src="/images/sss/sh_dirlight_comparison.png" alt="comparison of computing irradiance from a directional light with L1 SH and L2 SH projections"></p>
<p>You can see quite clearly that we&rsquo;re already getting a filtered, lower-frequency version of the lighting response just by going through low-order SH. This means that the SH approach can be suitable for relatively large/wide diffusion kernels, but it&rsquo;s not a great fit in terms of being able to seamlessly scale from small scattering amounts to larger mean scattering distances.</p>
<p>As an alternative to SH, we can instead consider using our old friend <a href="../sg-series-part-2-spherical-gaussians-101/">the Spherical Gaussian</a> (SG). SG&rsquo;s have five properties that are of interest to us when it comes to trying to approximate the response from a filtered light source:</p>
<ol>
<li>They can be oriented in any direction, which means they can be lined up perfectly with a punctual light source</li>
<li>They have an aribitrary sharpness parameter ( \( \lambda \) ) that lets them represent a range of filtering parameters, including very narrow kernels</li>
<li>An SG is trivial to normalize (when making some reasonable approximations), which ensures that we will not add or remove any energy</li>
<li>An SG convolved with a delta gives you another SG. This means we can use an SG to represent a filtering kernel, apply it to a punctional light source, and get another SG</li>
<li>We have a good approximation for <a href="../sg-series-part-3-diffuse-lighting-from-an-sg-light-source/">computing the resulting irradiance from an SG light source</a></li>
</ol>
<p>With this in mind, the approach looks fairly straightforward: first, we come up with an SG that represents the filtering kernel we will use to approximate our scattering profile. Wider kernels will have a lower sharpness parameter, while more narrow kernels will have a higher sharpness. We can then normalize the SG to ensure that it integrates to 1 over the entire sphere, which is critical for ensuring energy conservation in the filtering process. Once we have a normalized SG representing the filtering kernel, we combine it with our light source by setting the axis ( \( \mu \) ) parameter of the SG equal to the direction to the light source, and multiplying the SG&rsquo;s amplitude ( \( a \) ) by the attenuated intensity of the light source. This effectively gives us an SG light source, for which we can compute the diffuse response in the usual way by using Stephen Hill&rsquo;s fitted irradiance approximation and multiplying the resulting irradiance by \( \frac{albedo}{\pi} \). For scattering profiles that vary by wavelength (which is typically the case) we extend this process by coming up with a separate SG for the red, green, and blue channels and evaluating each separately. When doing this the light intensity can actually be factored out if we like, which means we can leave the SG with its normalized amplitude and multiply with the attenuated light intensity afterwards. Here&rsquo;s what this all might look like in code form:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">float3</span> <span class="n">diffuse</span> <span class="o">=</span> <span class="n">nDotL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">EnableSSS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Represent the diffusion profiles as spherical gaussians
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SG</span> <span class="n">redKernel</span> <span class="o">=</span> <span class="n">MakeNormalizedSG</span><span class="p">(</span><span class="n">lightDir</span><span class="p">,</span> <span class="mf">1.0f</span> <span class="o">/</span> <span class="n">max</span><span class="p">(</span><span class="n">ScatterAmt</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="mf">0.0001f</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">SG</span> <span class="n">greenKernel</span> <span class="o">=</span> <span class="n">MakeNormalizedSG</span><span class="p">(</span><span class="n">lightDir</span><span class="p">,</span> <span class="mf">1.0f</span> <span class="o">/</span> <span class="n">max</span><span class="p">(</span><span class="n">ScatterAmt</span><span class="p">.</span><span class="n">y</span><span class="p">,</span> <span class="mf">0.0001f</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">SG</span> <span class="n">blueKernel</span> <span class="o">=</span> <span class="n">MakeNormalizedSG</span><span class="p">(</span><span class="n">lightDir</span><span class="p">,</span> <span class="mf">1.0f</span> <span class="o">/</span> <span class="n">max</span><span class="p">(</span><span class="n">ScatterAmt</span><span class="p">.</span><span class="n">z</span><span class="p">,</span> <span class="mf">0.0001f</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Compute the irradiance that would result from convolving a punctual light source
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// with the SG filtering kernels
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">diffuse</span> <span class="o">=</span> <span class="n">float3</span><span class="p">(</span><span class="n">SGIrradianceFitted</span><span class="p">(</span><span class="n">redKernel</span><span class="p">,</span> <span class="n">normal</span><span class="p">).</span><span class="n">x</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                     <span class="n">SGIrradianceFitted</span><span class="p">(</span><span class="n">greenKernel</span><span class="p">,</span> <span class="n">normal</span><span class="p">).</span><span class="n">x</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                     <span class="n">SGIrradianceFitted</span><span class="p">(</span><span class="n">blueKernel</span><span class="p">,</span> <span class="n">normal</span><span class="p">).</span><span class="n">x</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">float3</span> <span class="n">lightingResponse</span> <span class="o">=</span> <span class="n">diffuse</span> <span class="o">*</span> <span class="n">LightIntensity</span> <span class="o">*</span> <span class="n">DiffuseAlbedo</span> <span class="o">*</span> <span class="n">InvPi</span><span class="p">;</span>
</span></span></code></pre></div><p>For this snippet we used a very simple mapping from an arbitrary <code>ScatterAmt</code> parameter to an SG sharpness, where \( \lambda = \frac{1}{ScatterAmt} \). This doesn&rsquo;t directly map to a diffusion kernel that&rsquo;s specified in terms of distance from the shading point, but we&rsquo;ll come back to that latter. First, let&rsquo;s see what this looks like when we apply this algorithm for a simple directional light with various values of <code>ScatterAmt</code>:</p>
<p><img src="/images/sss/sg_scattering.png" alt="Comparison of SG directional light prefiltering using various parameters"></p>
<p>From these results, it looks like this gets us pretty close to our goal of approximating the subsurface scattering response of a point light with aribtrary scattering widths. And we&rsquo;re doing all of that with pure shader math, no lookup textures required! The first image in the top-left also shows that we can scale the filtering all the way down to the &ldquo;no scattering&rdquo; case just fine, which is convenient.</p>
<p>If you wanted to optimize this further you could probably start by pulling out the portions of <code>SGIrradianceFitted</code> that aren&rsquo;t dependent on the SG sharpness, since the normal and axis are the same for all 3 SG&rsquo;s. However in practice you will probably want to combine this with the normal map prefiltering technique described in the preintegrated subsurface scattering article, since that will simulate scattering through small-scale bumps. If you do this you&rsquo;ll end up with separate normal values for each channel, in which case you won&rsquo;t be able to make the same optimization.</p>
<h1 id="accounting-for-curvature">Accounting For Curvature</h1>
<p>If you&rsquo;re familiar with how preintegrated SSS works, you may have already realized a problem with our proposed technique: it&rsquo;s not accounting for the curvature of the surface. If we were to just use the same set of sharpness values everywhere on an arbitrary mesh the results would not look correct, because the falloff/wrap caused by the SG would be same regardless of how flat or curved the surface is. Another way of saying this is that since our SG works purely in an angular domain, we&rsquo;re effectively getting larger or smaller scattering distances across the mesh. Fortunately this problem is solvable with a bit of math. Recall that an SG has the following form:</p>
<p>$$ G(\mathbf{v};\mathbf{\mu},\lambda,a) = ae^{\lambda(\mathbf{\mu} \cdot \mathbf{v} - 1)} $$</p>
<p>or alternatively:</p>
<p>$$ G(\mathbf{v};\mathbf{\mu},\lambda,a) = ae^{\lambda(\cos(\theta) - 1)} $$</p>
<p>Since it only depends on the cosine of the angle between the sample point and the SG axis direction, the &ldquo;width&rdquo; of the SG is fixed in the angular domain but variable in terms of Euclidean distance if we were to vary the radius ( \( r \) ) of the sphere that it&rsquo;s defined on. Therefore we need to redefine our SG in terms of Euclidean distance ( \( d \) ) between the SG center on the surface of the sphere ( \( c \) ) and the shading point ( \( s \) ), where \( \theta \) is the angle between the surface normal and SG axis (which is aligned with the light direction):</p>
<p><img src="/images/sss/sg_coordinate_system.png" alt="The coordinate system for working with a spherical gaussian"></p>
<p>We can then use the following to get the distance between the points:</p>
<p>$$ d = 2r\sin(\frac{\theta}{2}) $$</p>
<p>If we flip that around to get the value of \( \theta \) in terms of distance, we get this:</p>
<p>$$ \theta = \sin^{-1}(\frac{d}{2r})2 $$</p>
<p>We can then plug that into the second form of the SG definition and get the following:</p>
<p>$$ G(d;\lambda,a,r) = ae^{\lambda(\cos(\sin^{-1}(\frac{d}{2r})2) - 1)} $$</p>
<p>This looks pretty gnarly, but fortunately we can simplify this by making the substitution \( \cos(2\sin^{-1}(x)) = 1 - 2x^{2} \), which gives us something much nicer once we clean it up a bit:</p>
<p>$$ G(d;\lambda,a,r) = ae^{\frac{-\lambda d^2}{2r^2}} $$</p>
<p>In case that&rsquo;s too hard to read, this is the exponent part on its own:</p>
<p>$$ \frac{-\lambda d^2}{2r^2} $$</p>
<p>This is now really close to a standard 1D Gaussian form, which is pretty neat! It&rsquo;s also now quite clear that the effective width of the SG will increase as the radius increases, which is not what we wanted for subsurface scattering. This is confirmed if we <a href="https://www.desmos.com/calculator/yg6mflmavo">graph this out</a> for a few values of \( r \):</p>
<p><img src="/images/sss/sg_radius_graphs.png" alt="Graphs showing an SG reparameterized in terms of euclidian distance on spheres with radius 0.25, 0.5, and 1.0"></p>
<p>The good news is that we can fix this very easily: we just need that \( r^2 \) term to drop out, which will happen if we multiply \( \lambda \) by \( r^2 \). Doing that gives up exactly a 1D Gaussian, except with a \( \lambda \) in the numerator of the exponent instead of the usual \( \sigma \) in the denominator:</p>
<p>$$ G(d;\lambda,a) = ae^{\frac{-\lambda d^2}{2}} $$</p>
<p>This final form gives us the fixed width kernel that we&rsquo;re after, as long as we can compute the effective radius from the curvature of the mesh. We can do that using pixel shader derivatives as described in the preintegrated SSS article, or we can precompute it and store it in a texture or per-vertex. This also gives another interpretation of our <code>ScatterAmt</code> parameter that we used earlier: it&rsquo;s the \( \sigma \) parameter of a Gaussian centered around the point \( c \).</p>
<h1 id="applying-to-ambient-lighting">Applying To Ambient Lighting</h1>
<p>In most real-time scenarios we&rsquo;re going to be combining lighting from punctual light sources with precomputed GI stored as either spherical or hemispherical probes in 3D space. For SH probes, I already outlined a workable approach earlier in this article. Really you just need some kind of function for generating the SH coefficients for a Guassian, and then we can multiply that with the SH lighting environment to pre-filter it. Something similar to what <a href="http://cseweb.ucsd.edu/~ravir/papers/invrend/invrend.pdf">Ravi Ramamoorthi proposed for approximating a Torrance-Sparrow NDF</a> would probably work pretty well:</p>
<p>$$ \Lambda \rho \approx e^{-(\sigma l)^{2}} $$</p>
<p>For probes encoded as a set of SG&rsquo;s, we can make use of <a href="http://web.wakayama-u.ac.jp/~iwasaki/project/biscale/sa2012.pdf">Kei Iwasaki&rsquo;s forumula for approximating the convolution of two SG&rsquo;s</a> (brought to my attention by <a href="http://graphicrants.blogspot.com/2018/05/normal-map-filtering-using-vmf-part-3.html">Brian Karis&rsquo;s blog post</a>), which looks like this once implemented in HLSL:</p>
<pre tabindex="0"><code>//-------------------------------------------------------------------------------------------------
// Approximates the convolution of two SG&#39;s as another SG
//-------------------------------------------------------------------------------------------------
SG ConvolveSGs(in SG x, in SG y)
{
    SG convolvedSG;
    convolvedSG.Axis = x.Axis;
    convolvedSG.Sharpness = (x.Sharpness * y.Sharpness) / (x.Sharpness + y.Sharpness);
    convolvedSG.Amplitude = 2 * Pi * x.Amplitude * y.Amplitude / (x.Sharpness + y.Sharpness);

    return convolvedSG;
}
</code></pre><p>The approach is then similar to what we do for punctual lights: come up with the normalized SG&rsquo;s for red/green/blue, convolve them with each SG in the lighting probe, and compute the resulting irradiance.</p>
<h1 id="limitations-and-future-work">Limitations and Future Work</h1>
<p>Compared with standard Lambertian diffuse, this approach is much more math-heavy. I have implemented and shipped this technique in two games already, so it&rsquo;s definitely usable. But obviously you&rsquo;ll want to be wary of the extra ALU and register pressure that it can add to a shader program.</p>
<p>In terms of the visual results, it mostly suffers from the same major visual drawbacks that you get with preintegrated subsurface scattering. In particular there&rsquo;s no great solution for handling shadow edges, and your results may suffer if your technique for computing curvature has errors or discontinuities. It also does nothing to account for scattering through thin surfaces like the ears, so that has to be handled separately. It also shares another limitation with preintegrated SSS: it assumes punctional light sources. For area lights you would need to do something more complicated, perhaps using the LTC framework.</p>
<p>I also have not taken the time to come up with a more principled approach for applying the normal map prefiltering based on the <code>ScatterAmt</code> parameter. Currently I use a simple ad-hoc mapping from that parameter to lerp factors between the detail normal map and the base normal map, which works well enough but could certainly be improved. If anybody has thoughts on this, please let me know in the comments or on Twitter!</p>
]]></content:encoded>
		</item>
		
		<item>
			<title>An Introduction To Real-Time Subsurface Scattering</title>
			<link>https://therealmjp.github.io/posts/sss-intro/</link>
			<pubDate>Sun, 06 Oct 2019 00:00:00 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/sss-intro/</guid>
			<description>A little while ago I was doing some research into the state-of-the-art for approximating subsurface scattering effects in real-time (mainly for skin rendering), and I had taken a bunch of loose notes to help me keep all of the details straight. I thought it might be useful to turn those notes into a full blog post, in case anyone else out there needs an overview of what&amp;rsquo;s commonly used to shade skin and other materials in recent games.</description>
			<content:encoded><![CDATA[<p>A little while ago I was doing some research into the state-of-the-art for approximating subsurface scattering effects in real-time (mainly for skin rendering), and I had taken a bunch of loose notes to help me keep all of the details straight. I thought it might be useful to turn those notes into a full blog post, in case anyone else out there needs an overview of what&rsquo;s commonly used to shade skin and other materials in recent games. If you&rsquo;re already an expert in this area then this probably isn&rsquo;t the post for you, since I&rsquo;m not going to talk about anything new or novel here. But if you&rsquo;re not an expert, feel free to read on!</p>
<h2 id="sss-basics">SSS Basics</h2>
<p>First of all, what do we mean when we say &ldquo;subsurface scattering&rdquo;? In reality subsurface scattering is happening even on our more &ldquo;standard&rdquo; materials like plastic or leather, which we typically model with a diffuse BRDF like Lambertian. This helpful image from Naty Hoffman&rsquo;s <a href="https://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf">2013 talk on the physics of shading</a> is great for understanding what&rsquo;s going on:</p>
<p><img src="/images/sss/sss_local.png" alt="subsurface scattering local to the pixel"></p>
<p>In this case you want to imagine that the green circle with the red outline is the shading area for a single pixel. The big orange arrow coming from the right is some incoming light from a particular direction, most of which reflects off the surface causing specular reflections. However some of that light also refracts <em>into</em> the surface, where it encounters particles within the medium. The light then scatters among the particles causing certain wavelengths to get absorbed, resulting in the light taking on the characteristic albedo color of the material. Eventually the light that isn&rsquo;t completely absorbed refracts its way back out of the surface in a random direction, where it may or may not make it back to eye causing you to see it. The part that makes this easy is the fact that the scattered subsurface light all comes out within the pixel footprint. This lets us simplify things by only considering the light hitting the center of the pixel, and then estimating the amount of light that comes back out. In effect we do something like this, where we assume all of the scattered light comes out right where it initially entered the surface:</p>
<p><img src="/images/sss/sss_entry.png" alt="subsurface scattering assumed to come out of the pixel center"></p>
<p>However this is not usually what we&rsquo;re talking about when we mention subsurface scattering in the context of games or graphics. Usually that term is only brought up in cases where the material is particularly translucent, allowing the scattered light to bounce around even further than in more typical materials. Like in this diagram:</p>
<p><img src="/images/sss/sss_nonlocal.png" alt="subsurface scattering assumed to come out of the pixel center"></p>
<p>In this case the scattered light is no longer coming out within the pixel footprint, it&rsquo;s coming out in areas that could possibly be covered by neighboring pixels. In other words we&rsquo;ve crossed a threshold where we can no longer consider only the incoming lighting to a pixel, and we instead need to globally consider lighting from elsewhere in order to model the visible effect of light diffusing through the surface. As you can imagine this really complicates things for real-time renderers, but we&rsquo;ll talk about that more in a bit.</p>
<p>For a moment, let&rsquo;s assume we&rsquo;re doing an offline render with access to ray tracing and other fancy features. How do we compute the contribution from subsurface scattering in that case? If we wanted to be really accurate, we would use volumetric path tracing techniques to compute how lighting scatters and refacts about within the medium. This probably sounds expensive (and it is!), but it&rsquo;s becoming an increasingly viable option for high-end <a href="https://graphics.pixar.com/library/ProductionVolumeRendering/paper.pdf">animation</a> and <a href="https://blog.selfshadow.com/publications/s2017-shading-course/walster/s2017_pbs_volumetric_notes.pdf">VFX</a> studios. Either way this approach lets you simulate the full path of light along with all of the various scattering events that occur under the surface, which can give you a relatively accurate result that accounts for complex geometry and non-uniform scattering parameters.</p>
<h2 id="diffusion-approximations">Diffusion Approximations</h2>
<p>For games and real-time we&rsquo;re obviously a lot more constrained, so instead we typically work with a more approximate diffusion-based approach. Most of the work in this area stems from <a href="http://www.graphics.stanford.edu/papers/bssrdf/">Jensen&rsquo;s paper published back in 2001</a>, which was the first to introduce the concept of a Bidirectional Surface Scattering Distribution Function (BSSDF) to the graphics world. That paper spawned all kinds of interesting follow up research over the past 18 years, spanning both the worlds of offline and real-time graphics. Covering all of that is outside of the scope of this blog post, so instead I&rsquo;m going distill things down to the basic approximations that we work with in games.</p>
<p>They key to Jensen&rsquo;s approach is the simplifying assumption that the light distribution within a highly-scattering media tends to be isotropic. If you&rsquo;re not familiar with volumetric rendering terms, what this means is that once light into the surface it&rsquo;s randomly likely to end up coming out in any direction, rather than being mostly aligned to the incoming light direction or something like that. This effectively makes the scattering sort of a &ldquo;blurring&rdquo; function, which results in a uniform distribution of reflected lighting (note that this is basically the same assumption that we make for Lambertian diffuse). Jensen&rsquo;s BSSDF was actually based on two parts: an exact method for computing the single-scattering contribtution (light that goes into the surface, hits something, then bounces right out again) and a &ldquo;dipole&rdquo; method that approximates the multiple-scattering contribution (light that goes into the surfaces, has multiple scattering events, and then finds its way out) by evaluating two virtual point light sources: one above the surface, and one below:</p>
<p><img src="/images/sss/dipole.png" alt="dipole model"></p>
<p>Even if you have analytical approaches for computing the scattering from an incoming point on the surface to an outgoing point, the problem is that your rendering integral just got even worse than it usually is. Evaluating an arbitrary BSSDF means computing the value of this integral:</p>
<p>$$ L_{o}(o, x_{o}) = \int_{A} \int_{\Omega} S(x_{i}, i; x_{o}, o) L_{i}(x_{i}, i)(i \cdot n_{i}) didA $$</p>
<p>That&rsquo;s one mean integral to evaluate: it basically means we have to go over the entire surface of the mesh, and at every point on that surface sample every direction on the upper hemisphere and apply the scattering function \( S(x_{i}, i; x_{o}, o) \) to the incoming lighting. If we want to simplify this a bit, we can make the assumption that the scattering function is radially symmetric and really only depends on the <em>distance</em> between the incoming point and the outgoing point. That brings us to a somewhat less complicated integral:</p>
<p>$$ L_{o}(o, x_{o}) = \int_{A} R(||x_{i} - x_{o}||) \int_{\Omega} L_{i}(x_{i}, i)(i \cdot n_{i}) didA $$</p>
<p>Our new integral now boils down to &ldquo;for every point on the surface, compute the irradiance and then multiply that with a scattering function that takes distance as a parameter&rdquo;. This is less complicated, but doesn&rsquo;t fix the whole &ldquo;to shade single point we need to shade every single other point on the surface&rdquo; problem. However it does give us a useful framework to build on, and also a simplified mental model that we can use. The &ldquo;scattering only depends on distance&rdquo; assumption basically turns the scattering function into blur/filter kernel, except it&rsquo;s applied to an arbitrary mesh surface instead of a 2D image. Thus you&rsquo;ll often see the \( R(r) \) part referred to as as a <strong>diffusion profile</strong> (or sometimes as a diffusion kernel), which is the terminology that I&rsquo;ll often use myself for the rest of this article. If you want to picture what the diffusion profile would look like for something like human skin, you&rsquo;ll want to imagine that you&rsquo;re in a completely dark room and could shoot an extremely narrow completely-white laser beam at your skin:</p>
<p><img src="/images/sss/skin_diffusion_profile.png" alt="skin when lit by a narrow laser beam"></p>
<p>Technically speaking, you can use whatever diffusion profile you&rsquo;d like here. For instance you can use a Gaussian kernel, smoothstep, cubic function, etc. However you&rsquo;ll really want to work with something that&rsquo;s &ldquo;normalized&rdquo; in the sense that it doesn&rsquo;t add or remove energy (in other words, the kernel integrates to 1 for all possible configurations). There&rsquo;s also no guarantee that the kernel you pick will actually be a plausible match for the scattering behavior of a real-world behavior such as skin. To do that, you have to carefully choose your diffusion profile such that it closely matches the measured (or accurately computed) response of that material. Disney did <a href="https://blog.selfshadow.com/publications/s2015-shading-course/burley/s2015_pbs_disney_bsdf_notes.pdf">exactly this for human skin</a>, and they created a kernel based on the sum of two exponentials that they called &ldquo;normalized diffusion&rdquo;. The neat thing about their model is that not only is it simple and intuitive to work with, they also showed that they could accurately fit to both the single and multi-scattering components with a single kernel:</p>
<p><img src="/images/sss/normalized_diffusion_fit.png" alt="graph showing how normalized diffusion fits the monte-carlo reference data"></p>
<h2 id="rendering-with-a-diffusion-profile">Rendering With a Diffusion Profile</h2>
<p>So how we do we actually render something with a BSSDF? Even with the simplifications that we get from using a radially-symmetric diffusion profile, we still have the problem that the shading for any particular surface on a mesh potentially depends on every other surface on the same mesh. In practice we can improve the situation by exploiting the fact that a well-behaved diffusion profile should always decrease in value as the distance gets larger. This lets us determine a &ldquo;maximum scattering distance&rdquo; where the contribution beyond that distance is below an acceptable threshold. Once we have this \( r_{max} \), we can limit our sampling to within a sphere whose radius is equal to \( r_{max} \). This definitely helps things quite a bit by letting us constrain things to local neighborhood around the shading point.</p>
<p>Most early uses of sub-scattering the offline world was built using around using <a href="https://graphics.stanford.edu/papers/fast_bssrdf/fast_bssrdf.pdf">point clouds</a> to sample the neighboring irradiance. Point clouds are a hierachical structure that allows the illuminance to be cached at arbitrary points in the scene, typically located on the surfaces of meshes (also known as surfels):</p>
<p><img src="/images/sss/point_cloud_teapot.png" alt="point cloud on a teapot"></p>
<p>They were popularized by Pixar, who <a href="https://graphics.pixar.com/library/PointBasedColorBleeding/SlidesFromAnnecy09.pdf">integrated them into RenderMan</a> in order to <a href="https://pdfs.semanticscholar.org/b4e3/81a929722ada5a63914286c325714e03cf9d.pdf">compute global illumination</a>. Later on as point clouds fell out of favor and path tracing began to take over, monte carlo methods began to appear. <a href="https://pdfs.semanticscholar.org/90da/5211ce2a6f63d50b8616736c393aaf8bf4ca.pdf">This presentation</a> from Solid Angle and Sony Imageworks sums up 4 such approaches, each based around the idea of distributing ray samples around the sample point in such a way that their density is proportional to the diffusion profile. The fourth method (shooting rays along within a sphere from orthogonal directions) has been used by Disney, and perhaps many others:</p>
<p><img src="/images/sss/ray_probing_surface.png" alt="rays probing a surface"></p>
<p>while the first method (uniformly shooting rays from a point just below the surface) has actually been <a href="https://media.contentapi.ea.com/content/dam/ea/seed/presentations/barrebrisebois2019-state-of-the-art-and-challenges-in-game-ray-tracing-release.pdf">used in real-time</a> by EA SEED&rsquo;s Halcyon rendering engine:</p>
<p><img src="/images/sss/seed_surface_probing.png" alt="rays probing a surface from a point below the surface"></p>
<p>These methods can both be very effective as long as you have the resources to perform ray intersections against your scene geometry. Despite the heavy expense involved, there are still making some assumptions being made here that can be violated depending on your particular scene. In particular, each point that is hit by a ray is assumed to be &ldquo;connected&rdquo; to the original shading point such that it would be plausible for light to scatter all of the way there. This issue is mitigated by restricting the ray queries so that they only intersect with the same mesh being shaded, which has the added bonus of reducing the amount of surfaces that need to be considered. But even with that restriction it&rsquo;s easy to imagine a scattering path that would pass right through an empty gap of air. Disney has a diagram from their course notes showing such a case:</p>
<p><img src="/images/sss/scatter_through_air.png" alt="a case where assuming diffusion is a function of distance is a bad assumption"></p>
<p>If light were to scatter all the way from the entry point to the exit point in that example, it would have to scatter all the way around the crevice which is quite a long way to go. An approach that merely consideres the distance between those points will over-estimate the scattering here, as opposed to a proper volumetric path tracer that could actually account for the gap.</p>
<h2 id="rendering-diffusion-in-real-time">Rendering Diffusion in Real-Time</h2>
<p>So it&rsquo;s great that offline renderers can just shoot a bunch of rays around in order to sample the irradiance in a local neighborhood, but until very recently it wasn&rsquo;t viable to do this in real-time (and even with the latest RTX hardware you&rsquo;re still only going to get a handful of rays per-pixel every frame). This meant that games generally had to fall back to even cheaper approximations in order render the skin on their hero characters. There have likely been all kinds of ad-hoc approaches used over the past 15 years or so, many of which were never presented or described anyhere. So instead of covering them all, I&rsquo;m going to give a quick overview of some of the more commonly-used and referenced techniques that you&rsquo;ll find out in the wild.</p>
<h3 id="texture-space-diffusion">Texture-Space Diffusion</h3>
<p><img src="/images/sss/gpu_gems_texture_space.jpg" alt="irradiance being blurred in texture-space"></p>
<p>This approach was popularized by the incredible <a href="https://www.youtube.com/watch?v=y_DEafk0cRY">Human Head demo</a> for the GTX 8800, (which you can still <a href="https://www.nvidia.com/coolstuff/demos#!/human-head">download</a> on Nvidia&rsquo;s site!) and described in detail in <a href="https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch14.html">GPU Gems 3</a> as well as in the <a href="http://www.eugenedeon.com/wp-content/uploads/2014/04/efficientskin.pdf">paper from Eurographics 2007</a>. The technique actually comes from the film world: it was first <a href="http://www.scribblethink.org/Work/Pdfs/Face-s2003.pdf">used and documented</a> as part of the production of The Matrix Reloaded way back in 2003. <a href="http://www.eugenedeon.com/">Eugene d&rsquo;Eon</a> and David Luebke were able to show that it could also work in real-time on (at the time) high-end GPUs, and in the process they were able to create some visuals that are still impressive today. It&rsquo;s called &ldquo;texture-space&rdquo; because all of the scattering/diffusion simulation happens in 2D using the UV parameterization of the head/face mesh, as opposed doing it per-pixel in screen-space. A simplified explanation of the algorithm would go something like this:</p>
<ul>
<li>Draw the mesh using a special vertex shader that sets <code>output.position = float4(uv * 2.0f - 1.0f, 0.0f, 1.0f);</code> and a pixel shader that outputs the irradiance from dynamic + baked lighting, effectively computing an irradiance map in texture-space</li>
<li>Blur the irradiance map with bunch of Gaussian kernels, with a horizontal + vertical pass for each kernel (they approximate skin&rsquo;s diffusion profile as a sum of separable gaussians)</li>
<li>Draw the mesh for real using the camera&rsquo;s projection matrix, then in the pixel shader sample the convolved irradiance maps and apply the diffuse albedo map to get the diffuse reflectance with subsurface scattering.</li>
</ul>
<p>Obviously their results speak for themselves in terms of quality, so I don&rsquo;t think that would be the main issue preventing adoption. It&rsquo;s also fairly flexible in that in can handle many different diffusion profiles, provided that they can be represented as a sum of Gaussians. However there are definitely some issues that become obvious pretty quickly if you think about it:</p>
<ul>
<li>
<p>Shading in texture-space can be difficult to tune in terms of performance and memory usage. Shading in a pixel shader means that your shader invocations naturally drop as the mesh gets further from the camera, since the mesh will occupy less are a in screen-space. However with texture-space shading you will shade however many texels are in your off-screen irradiance buffer, so you need to manually reduce the buffer size based on the distance to the character. Ideally you would want to match the mip level that will be sampled by the mesh during the primary rasterization path, but there could potentially be a wide range of mip levels used depending on the complexity of the mesh and the camera&rsquo;s current position. Budgeting enough memory for the irradiance buffer can also be difficult if you have many characters on-screen at once, especially if sudden camera cuts will cause a particular character to suddenly be in a close-up. And don&rsquo;t forget that you lose z-buffer occlusion and backface culling when shading in texture-space, which means that the naive approach could potentially result in shading a lot of surfaces that never contribute to the final image!</p>
</li>
<li>
<p>In general, texture-space diffusion makes sense because of the assumption that the local neighborhood of a surface will also be local in the UV mapping. Most of the time this is correct, but any UV seams will break this assumption and cause ugly artifacts without somekind of workaround. It also fails for thin surfaces like the ear, since both sides of the ear are probably not going to be close in UV space. The demo accounts for the forward-scattering through thin surfaces by rendering a translucent shadow map that records the UV coordinate of the closest depth for each texel, which is then used by the surface on the &ldquo;other side&rdquo; to sample the blurred irradiance map at the point where the light ray hit the mesh.</p>
</li>
<li>
<p>Typically the UV mapping of something like a face will have lots of stretching and warping, which would also result in a warping of the diffusion profile itself once the results are mapped back to world space: <img src="/images/sss/texture_space_stretch.jpg" alt="warping of the diffusion profile caused by UV stretching"> The Nvidia demo works around this by rendering out a &ldquo;stretch map&rdquo; in texture-space that contains the U/V distortion factors, which can then be used to modify the diffusion profile weights. You can pre-compute this map, but it will be incorrect in the case of extreme deformations from skinning or blend shapes. In those cases computing the stretch map on the fly would produce better results.</p>
</li>
<li>
<p>The demo used 6 large Gaussian blur kernels to approximate the skin diffusion profile, which is a lot of passes! There was a Shader X7 article where <a href="http://filmicworlds.com/">John Hable</a> described a simplified version were all 6 passes were collapsed down to a single 12-tap pass that could be done in the pixel shader when sampling the irradiance map. Unfortunately there&rsquo;s no online version of that article, but he describes it a bit in <a href="http://advances.realtimerendering.com/s2010/Hable-Uncharted2(SIGGRAPH%202010%20Advanced%20RealTime%20Rendering%20Course).pdf">this presentation</a> from SIGGRAPH 2010.</p>
</li>
<li>
<p>Specular shading is done in the pixel shader during the primary rasterization pass, which means that light sources need to be effectively sampled twice. An alternative would be to compute the specular in texture-space, and output it to a separate render target that&rsquo;s sampled during the rasterization pass.</p>
</li>
<li>
<p>The additional complexity of integrating a special-case texture-space rendering path for characters could potentially add a high maintenence cost, and may not play nice with other passes that are typically done in screen or camera space (for instance, binning lights in a frustum-aligned grid).</p>
</li>
</ul>
<p>I&rsquo;m not sure if any game actually shipped with this approach (or something) similar at runtime. Uncharted 2 used the approach described by John Hable for offline-rendered cinematics, but at runtime they fell back to something much simpler and cheaper. There was an <a href="http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/Gosselin_skin.pdf">old ATI demo</a> that did texture-space diffusion 3 years before d&rsquo;Eon and Luebke published their work at Nvidia, but what they describe sounds much less sophisticated. Overall the lack of adoption isn&rsquo;t terribly surprising given the large list of potential headaches.</p>
<p>Update 10/7/2019: <a href="https://twitter.com/vicki_ferg">Vicki Ferguson</a> was kind enough to <a href="https://twitter.com/vicki_ferg/status/1181069047053635584?s=20">point out</a> that <a href="https://en.wikipedia.org/wiki/FaceBreaker">FaceBreaker</a> shipped with texture-space diffusion way back in 2008! Angelo Pesce then <a href="https://twitter.com/kenpex/status/1181073843613339648?s=20">explained</a> how this tech made its way into the Fight Night series, where it eventually evolved into pre-integrated subsurface scattering! See <a href="https://www.gdcvault.com/browse/gdc-11/play/1014661">Vicki&rsquo;s GDC presentation</a> and Angelo&rsquo;s &ldquo;director&rsquo;s cut&rdquo; <a href="https://www.scribd.com/document/66973213/Fight-Night-Champion-GDC-Presentation">slide deck</a> for more info on the tech used for Fight Night Champion.</p>
<h3 id="screen-space-subsurface-scattering-ssss">Screen-Space Subsurface Scattering (SSSS)</h3>
<p><img src="/images/sss/sssss.jpg" alt="sampling irradiance in screen-space"></p>
<p>Once it became clear that texture-space diffusion would be both difficult and expensive to use in a real-time game scenario, a few people started looking into cheaper options that make use of lighting data stored in screen-space buffers. The idea was popularlized by the work of <a href="http://www.iryoku.com/">Jorge Jimenez</a>, thanks to a <a href="http://www.iryoku.com/sssss/">very popular article</a> that was published in the first edition of GPU Pro. Morten Mikkelsen (of MikkT fame) also did some <a href="https://www.dropbox.com/s/tn9b9tenykk650w/cbf_skin.pdf?dl=0">early work</a> in this area, and Jorge continued to publish <a href="http://www.iryoku.com/translucency/">additions</a> and <a href="https://github.com/iryoku/separable-sss">improvements</a> in the following years. The state-of-the-art is probably best represented by <a href="http://advances.realtimerendering.com/s2018/Efficient%20screen%20space%20subsurface%20scattering%20Siggraph%202018.pdf">Evgenii Golubev&rsquo;s presentation</a> from SIGGRAPH 2018, where he explained how Unity adopted Disney&rsquo;s Normalized Diffusion profile for real-time use.</p>
<p>At a high level you&rsquo;re making a similar bet that you make with texture-space diffusion: you&rsquo;re hoping that 3D locality of a sample point&rsquo;s neigboring surfaces is preserved when it&rsquo;s rasterized and projected into 2D. When this is the case (and it often is) then you merely have to do a few texture fetches from neighboring texels in order to convolve the lighting with your diffusion profile. This is quite a bit more appealing than shading a surface in unwrapped UV space (especially on 2009-era hardware and APIs), since you can borrow from typical deferred approaches in order to output per-pixel irradiance to a secondary render target. This spares you from having to figure out the appropriate shading rate to use for the surface, and also prevents you from inadvertantly shading pixels that end up being occluded by other opaque surfaces.</p>
<p>With those advantages it&rsquo;s not surprising that many engines ended up adopting SSSSS and using it in shipping games. But of course it&rsquo;s not all roses, either. Let&rsquo;s go through some of the issues that you can run into:</p>
<ul>
<li>
<p>Since you&rsquo;re working in screen-space, you can only gather irradiance from surfaces that end up getting rasterized. This means that anytime you have some complex occlusion going on (for instance when the nose occludes parts of the cheek) you&rsquo;re going to end up with missing information. It&rsquo;s really the same problem that you can get with SSAO or SSR.</p>
</li>
<li>
<p>Generally you&rsquo;re going to be limited in the number of samples that you can gather, which is going to lead to undersampling. This will manifest as banding or &ldquo;blobiness&rdquo; if re-using the same sample pattern across all pixels, or as noise if using stochastic sampling. The latter can be mitigated with denoising and/or TAA.</p>
</li>
</ul>
<p><img src="/images/sss/stochastic_sampling_comparison.png" alt="stochastic sampling comparison"></p>
<ul>
<li>
<p>Doing things in screen-space necessitates doing the rendering in multiple passes, meaning you can&rsquo;t just completely shade the surface within a single pixel shader. This is unfortunate if everything else is forward rendered, especially since it means you may need to eat the memory of a few extra render targets that are only used for a subset of materials.</p>
</li>
<li>
<p>If you only want to run the SSSSS pass on pixels that require it, then you need some kind of system for tagging those pixels and selectively running the shader. Jorge&rsquo;s original implementation used stencil, but I&rsquo;ve never met a graphics programmer that enjoyed using stencil buffers. As an alternative you can branch on a per-pixel value, or run a pass to generate a list of screen-space tiles.</p>
</li>
<li>
<p>Similar to texture-space diffusion, you can&rsquo;t really account for light that transmits through thin surfaces like the ear. There might be some cases where both sides of the surface are visible to the camera, but in general you can&rsquo;t rely on that. Instead of you have account for that separately, using shadow map depth buffers and/or <a href="https://www.slideshare.net/colinbb/colin-barrebrisebois-gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurfacescattering-look-7170855">pre-computed thickness maps</a>. I would suggest reading through the slides from <a href="http://www.iryoku.com/downloads/Next-Generation-Character-Rendering-v6.pptx">this presentation</a> by Jorge for some ideas.</p>
</li>
</ul>
<h3 id="pre-integrated-subsurface-scattering-ssss">Pre-integrated Subsurface Scattering (SSSS)</h3>
<p><img src="/images/sss/preintegrated_sss.jpg" alt="preintegrated subsurface scattering"></p>
<p>The previous two techniques were all about turning diffusion into a gathering problem: one gathered local irradiance in texture-space, the other in screen-space. There&rsquo;s some key differences that we already covered, but fundamentally they&rsquo;re trying to do the same thing, just in different domains. <a href="http://advances.realtimerendering.com/s2011/Penner%20-%20Pre-Integrated%20Skin%20Rendering%20(Siggraph%202011%20Advances%20in%20Real-Time%20Rendering%20Course).pptx">Pre-integrated subsurface scattering</a> takes a complelely different approach: it makes no attempt at all to look at the lighting in the local neighorhood. Instead, it tries pre-compute the scattered results, and use the correct result based on a few properties of the point being shaded. The key insight that Eric Penner presented was that subsurface scattering isn&rsquo;t actually visible in cases where the surface is totally flat and the incoming lighting is uniform across the surface. It&rsquo;s like opening up an image in photoshop where every pixel has the same color and then running a Gaussian blur filter on it: you won&rsquo;t see any difference, since you&rsquo;re just blurring the same color with itself. We can confirm this pretty easily by running ray-traced diffusion on a plane lit by a single direction light, and comparing it with the result you get without subsurface scattering:</p>
<p><img src="/images/sss/sss_uniform_plane_comparison.png" alt="Comparison showing a uniformly-lit plane with subsurface scattering off and on"></p>
<p>Totally worth all of the cost all of those rays, am I right? If we know that you don&rsquo;t see subsurface scattering when the the light is uniform and the surface is flat, then we can deduce that the scattering <em>will</em> be visible whenever those two things aren&rsquo;t true. A quick test with our ray-tracer can confirm our hypothesis. Let&rsquo;s first try a sphere to see what happens when the surface isn&rsquo;t flat:</p>
<p><img src="/images/sss/sss_comparison_sphere.jpg" alt="scattering sphere comparison"></p>
<p>In this case the difference is huge! Now let&rsquo;s look at an example where the lighting changes very quickly due to a shadow being cast by another mesh:</p>
<p><img src="/images/sss/sss_shadow_comparison.jpg" alt="scattering shadow comparison"></p>
<p>This time we see the lighting diffusing its way over into the shadowed pixels, which is very noticeable. Really these are both two sides of the same coin: the incident irradiance is varying across the surface, in one case due to the changing surface normal and the other due to changes in light visibility.</p>
<p>Eric decided to tackle these two things separately in his presentation, so let&rsquo;s do the same and start with the case where scattering is visible on a non-flat surface. When a surface is curved, the incident irradiance will decrease on that surface as \( N \cdot L \) decreases which is what gives you that characteristic diffuse falloff. However with subsurface scattering effectively &ldquo;blurring&rdquo; the incident irradiance, the appearance of that falloff will change. This is particulaly noticeable at the point where \( N \cdot L = 0 \), since in the example with SSS enabled the lighting actually &ldquo;wraps&rdquo; right over that line where it would normally terminate. In fact, an <a href="https://developer.download.nvidia.com/books/HTML/gpugems/gpugems_ch16.html">old trick for approximating subsurface scattering</a> is to deliberately modify the \( N \cdot L \) part of the lighting calculations in order to produce a result that resembles what you get from SSS. Eric took this idea and made it more principled by actually precomputing what the falloff would be if you used the diffusion profile predicted by the dipole model. The way you do this is to take a circle, and then compute the resulting falloff for every point on that circle by integrating the incident irradiance multiplied with the diffusion profile:</p>
<p><img src="/images/sss/preintegrate_circle.jpg" alt="integrating diffusion profiles on a circle"></p>
<p>This ends up being a kind of convolution on the surface of a circle, which fits well with our mental model of SSS being a filtering operation. Doing this for the whole circle would give you a result for every value of \( \theta \) in the range \( [ -\pi, \pi ] \), but the result is symmetrical so we can discard half of the results and just keep everything from \( [ 0, \pi ] \). We can also switch from parameterizing on \( \theta \) to parameterizing on \( cos(\theta) \) in the range \( [ -1, 1 ] \), which gives us a nice 1D lookup table that we can index into using any result of \( N \cdot L \). Visualizing that would give us something like this:</p>
<p><img src="/images/sss/preintegrated_falloff_slice.jpg" alt="preintegrated scattering falloff for a single slice"></p>
<p>This is immediatly usable for rendering, but with a major catch: it only works for a sphere with a particular radius and diffusion profile. However we can easily extend this into a 2D lookup table by computing the resulting falloff for a range of radii:</p>
<p><img src="/images/sss/preintegrated_falloff_2D.jpg" alt="preintegrated scattering falloff for a range of sphere radii"></p>
<p>At this point you might be thinking &ldquo;this is neat for spheres, but how do we use this for an arbitrary mesh?&rdquo;, which is a valid question. To use this new 2D lookup texture, we need to basically take every shaded point on the mesh and map it to a sphere of a particular radius based on the local curvature of that point. Once again this was not a new idea at the time Eric presented: there was previous work from <a href="https://dspace5.zcu.cz/bitstream/11025/11073/1/Kolchin.pdf">Konstantin Kolchin</a> and <a href="http://omilab.naist.jp/~hkubo/pubdata/Kubo2011IJVR/paper.pdf">Hiroyuki Kubo</a> that had made the obvervation that subsurface scattering appearance was tied to surface curvature. Really the idea is pretty simple: figure out the local curvature, map that to a sphere radius, and then use that to choose the appropriate row of the preintegrated falloff lookup texture. Penner proposed a way to do this on-the-fly in a pixel shader by making use of pixel quad derivatives and little bit of geometry:</p>
<p><img src="/images/sss/curvature_derivatives.jpg" alt="computing curvature from pixel quad derivatives"></p>
<p>Very cool! This works&hellip;at least for curvature that&rsquo;s broad enough to be captured accurately by the pixel quad derivatives. A normal map used for a human face will typically have many small bumps and other high-frequency details, and we need special-case handling if want them to look correct. Penner took inspiration from a paper called <a href="http://gl.ict.usc.edu/Research/FaceScanning/EGSR2007_SGI_low.pdf">Rapid Acquisition of Specular and Diffuse Normal Maps from Polarized Spherical Gradient Illumination</a>, which effectively pre-filtered subsurface scattering into the normal maps it produced by generating separate normals for red, green, and blue channels:</p>
<p><img src="/images/sss/sss_pre_filtered_normals.jpg" alt="pre-filtered normal maps"></p>
<p>You can think of it this way: by &ldquo;flattening&rdquo; the red channel more than the blue, the red light will appear to have more diffusion through the bumps. Instead of requiring three seperate normal maps, Eric instead proposed sampling from the same normal map 3 different times with 3 separate mip bias amounts (one for each color channel). This provides a similar result without requiring seperate per-channel normal maps, and also allows you to use the same normal map for specular shading.</p>
<p>With large and small-scale curvature accounted for, we now have to look at how we handle cases where the incoming lighting changes due to shadowing. Penner&rsquo;s approach to solving this was to try to determine a pixel&rsquo;s location relative to the start and end of the shadow penumbra, and us that location to index into another lookup texture containing pre-integrated scattering values for various penumbra widths:</p>
<p><img src="/images/sss/preintegrated_penumbra_scattering.jpg" alt="pre-integrated penumbra scattering"></p>
<p>One catch here is that you can&rsquo;t just apply this penumbra scattering in only the penumbra region: the scattering should actually extend past the point where the penumbra reaches zero. This is why the above diagram remaps the penumbra into a smaller area (essentially narrowing it), and then has the red &ldquo;scattering&rdquo; zone where the penumbra reaches zero.</p>
<p>With all of these cases handled Penner was able to achieve some great results! This is quite remarkable, since this can all be achieved within the local context of a single pixel shader. This makes it possible to implement as a special shader permutation within a forward renderer, without having to implement additional passes to handle diffusion in texture-space or screen-space. It was for this reason that we shipped with a variant of this technique in both The Order: 1886 and Lone Echo, both of which use a forward renderer:</p>
<p><img src="/images/sss/rad_faces.jpg" alt="two examples of faces rendered with pre-integrated subsurface scattering"></p>
<p>While the results speak for themselves, we also encountered many challenges and limitations:</p>
<p><em>Please note that I&rsquo;ve spent a lot of time working with pre-integrated SSS, since it&rsquo;s what we&rsquo;ve always used in our engine at RAD. So it&rsquo;s likely that the length of this list is a reflection of that, and that the list of downsides for the other techniques would be longer if I had more hands-on experience with them.</em></p>
<ul>
<li>
<p>In practice, we found that using quad derivatives to compute the surface curvature produced poor results in some cases. Eric mentioned this in his presentation, but quad derivatives will always produce values that are constant cross a triangle (you can actually see this quite clearly in the <a href="/images/sss/curvature_derivatives.jpg">image</a> that I used earlier). This means that you get a discontinuity at every triangle edge, which can manifest as a disconinuity in your diffuse shading. It&rsquo;s not always noticeable, but when it is it can be quite distracting. You can actually see some of these artifacts in the images from the powerpoint slides if you look closely: <img src="/images/sss/preintegrated_derivative_artifacts.jpg" alt="diffuse artifacts from using quad derivatives"> These artifacts could also sometimes become even more noticeable due to vertex deformations from skinning and/or blend shapes, which was a real problem for using this on human faces. For these reasons we almost always ended up using pre-calculated curvature maps instead of trying to compute the curvature on the fly. The results become incorrect under deformation or scaling, which is unfortunate. But we found it was more desirable to have smoother and more consistent results.</p>
</li>
<li>
<p>The original presentation and GPU Pro 2 article only explains how to handle lighting from point light sources. This means you&rsquo;re on your own when it comes to figuring it out how to integrate it with baked lighting from lightmaps or probes, with the exception of the normal map prefiltering which works for any light source. While working on The Order I came up with a solution that worked with our (at the time) L2 spherical harmonics probes, which we explained in the <a href="https://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf">course notes</a> from our SIGGRAPH 2013 presentation. There&rsquo;s also things you can do with spherical gaussians, but I&rsquo;ll go into more detail about that in a follow-up post. Either way screen-space and texture-space diffusion can handle this naturally without any special considerations.</p>
</li>
<li>
<p>I haven&rsquo;t thought about it too much, but as of right now I have no idea how you would integrate this technique with area lights using LTC&rsquo;s or similar approaches.</p>
</li>
<li>
<p>Relying on a lookup texture can potentially be undesirable from a performance and shader architecture point of view. On some hardware it can definitely be preferable to do a pile of math instead of a texture sample, but you can&rsquo;t do that without fitting some kind of approximate curve to the pre-computed data. <a href="https://twitter.com/kenpex">Angelo Pesce</a> took a stab at doing this after the original SIGGRAPH presentation, and he shared his results in two <a href="http://c0de517e.blogspot.com/2011/09/mathematica-and-skin-rendering.html">blog</a> <a href="http://c0de517e.blogspot.com/2012/11/addendum-to-mathematica-and-skin.html">posts</a>.</p>
</li>
<li>
<p>For any particular diffusion profile, the technique requires two 2D lookup textures (or two fitted curves). This makes it difficult to support arbitrary diffusion profiles, since it would require generating custom lookup textures (or curves) for each material with a unique profile. Supporting a spatially-varying profile is even more challenging, since you would now need to interpolate between multiple textures or curves. Storing them as a bunch of 2D tables within the slices of a 3D texture is a doable (but expensive) option for achieving this. In a follow-up post I&rsquo;ll talk a bit about a technique I came up with that side-steps this particular issue.</p>
</li>
<li>
<p>I&rsquo;ve found the proposed solution for handling scattering within shadow penumbras to be difficult to work with in practice. In particular I&rsquo;m not sure that there&rsquo;s a reliable way to estimate the penumbra width. Quad derivatives tend to produce poor results due to the reasons mentioned earlier, and the surface slope isn&rsquo;t enough without also knowing the size of the &ldquo;penumbra&rdquo; that&rsquo;s generated by filtering. And of course there&rsquo;s not even a guarantee that you&rsquo;ll be using a box filter for your shadow filtering, since it&rsquo;s totally reasonable to use a Gaussian or some other kernel. Techniques that rely on dithering and TAA resolve would also completly break this, as would any techniques that approximate translucent shadows from things like hair (we&rsquo;ve always done some tricks with VSM/MSM to approximate translucent shadows from hair). Even if you are able to perfectly reconstruct the penumbra size and the pixel&rsquo;s position within it, you&rsquo;re not even guaranteed that the penumbra will be wide enough to give you the appropriate scattering falloff. The Order was <em>filled</em> with close-up shots of faces with tight, detailed shadows.</p>
</li>
<li>
<p>Using pre-integrated diffusion based on curvature and \( N \cdot L \) means you&rsquo;re making the assumption that the visibility to the light source is constant over the sphere. This is obviously not always true when you have shadows. In fact it&rsquo;s not even true for the simple case of a sphere, since a sphere will always shadow itself where \( N \cdot L \) is less than 0! Wide shadow penumbras and the penumbra diffusion approximation can hide the issues, but they&rsquo;re always there. The worst case is something with relatively high curvature (or scattering distance) but a very tight penumbra. Here&rsquo;s an example: <img src="/images/sss/preintegrated_sss_shadows_comparison.jpg" alt=""> What&rsquo;s really ugly about this particular scenario is that the artifacts reveal the underlying topology of the mesh. We normally shade a virtual curved surface formed by smoothly interpolating vertex normals, but the depth rasterized into a shadow map follows the exact surface formed by the triangles. We often ran into this artifact on our faces when using tight shadows, particularly if the curvature map had too high of a value.</p>
</li>
<li>
<p>The proposed technique for handling small-scale scattering through bumps in the normal map is implicitly making some assumptions about the size of the features in said normal map. In general it works best when the normal map only contains small pore-level details, but in games it&rsquo;s common to also bake larger-scale curvature into the normal map when transfering from a high-resolution sculpt to an in-game mesh. You may find that you get better results by separating these two things as &ldquo;base&rdquo; and &ldquo;detail&rdquo; normal maps, and only applying the pre-filtering to your detail map. We switched to doing this for Lone Echo, and while it&rsquo;s more expensive we prefer the higher-quality results. It&rsquo;s also possible to use a smaller tiling detail map instead for noisy pore-level details, which can certainly save some memory.</p>
</li>
<li>
<p>Like the other techniques mentioned in this article, you&rsquo;ll need additional tech if you want to handle diffusion through thin surfaces like ears. Our artists would sometimes try to fake this with pre-integrated SSS by making the curvature abnormally high, but this has the effect of flattening out the front-facing lighting (since it essentialy assumes the light has diffused everywhere on the sphere).</p>
</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Hopefully this post has helped give you a basic understanding of how subsurface scattering works, and how it can be approximated in real-time graphics. Perhaps it will also give you enough information to make a preliminary decision about which technique to use for your own real-time engine. If you were to ask me right now to choose between the 3 techniques that I discussed, I would probably go with the screen-space option. In general it seems to have the least corner cases to handle, and most closely resembles a ray-tracing approach. But that could also be a bit of &ldquo;the grass is always greener&rdquo; syndrome on my part. I also fully expect a few games to start using ray-traced SSS on high-end hardware at some point in the near future, since you can use that for high-quality results while keeping a screen-space path as a fallback for weaker hardware. You could also do a hybrid approach similar to what DICE did for <a href="https://developer.download.nvidia.com/video/gputechconf/gtc/2019/presentation/s91023-it-just-works-ray-traced-reflections-in-battlefield-v.pdf">reflections in Battlefield V</a>, where you use screen-space for most pixels and only use ray-tracing in areas the necessary data isn&rsquo;t present on the screen.</p>
<p>Please feel free to reach out in the comments or on Twitter if you have questions, or if you spot something incorrect that I should fix!</p>
]]></content:encoded>
		</item>
		
		<item>
			<title>Half The Precision, Twice The Fun: Working With FP16 In HLSL</title>
			<link>https://therealmjp.github.io/posts/shader-fp16/</link>
			<pubDate>Tue, 01 Oct 2019 00:00:00 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/shader-fp16/</guid>
			<description>Those of you who have been working on desktop and console graphics long enough will remember working with fp16 math in shaders during the D3D9 era. Back then HLSL supported the half scalar type, which corresponded to a floating-point value using 16-bits of precision. Using it was crucial for extracting the best performance from Nvidia&amp;rsquo;s FX series, 6-series, and 7-series hardware, since it could perform many fp16 operations at faster rate than it could for full-precision 32-bit values.</description>
			<content:encoded><![CDATA[<p>Those of you who have been working on desktop and console graphics long enough will remember working with fp16 math in shaders during the D3D9 era. Back then HLSL supported the <code>half</code> scalar type, which corresponded to a <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">floating-point value using 16-bits of precision</a>. Using it was crucial for extracting the best performance from Nvidia&rsquo;s FX series, 6-series, and 7-series hardware, since it could perform many fp16 operations at faster rate than it could for full-precision 32-bit values. But then the D3D10 era came along with its unified shader cores, and suddenly fp16 math was no more. None of the desktop hardware supported it anymore, and so HLSL went ahead and mapped the <code>half</code> type to <code>float</code> and called it day. And that&rsquo;s the end of story when it comes to fp16 in shaders, The End.</p>
<p>&hellip;or not. It turns out that fp16 is still useful for the reasons it was originally useful back in the days of D3D9: it&rsquo;s a good way to improve throughput on a limited transitor/power budget, and the smaller storage size means that you can store more values in general purpose registers without having your thread occupancy suffer due to register pressure. As of Nvidia&rsquo;s new Turing architecture (AKA the RTX 2000 series), AMD&rsquo;s Vega (AKA gfx900, AKA GCN 5) series<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and Intel&rsquo;s Gen8 architecture (used in Broadwell) fp16 is now back in the desktop world. Which means that us desktop graphics programmers now have to deal with it again. And of course if you&rsquo;re a mobile developer, it never really left in the first place. But how do you actually use fp16 in your shader code? That&rsquo;s exactly what this blog will explain!</p>
<p>Before we get into the exact details, you should know that there&rsquo;s actually two parallel paths to using fp16 math in your shaders. This article will cover both, so you can decide which works best for your situation.</p>
<h2 id="flexible-precision-aka-minimum-precision-aka-relaxed-precision">Flexible Precision (AKA Minimum Precision, AKA Relaxed Precision)</h2>
<p>This is the older of the two fp16 paths, and has actually been around in <a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/using-hlsl-minimum-precision%5D">D3D11</a> since Windows 8. OpenGL ES has also had a variant of this available for a long time. On this path, what you&rsquo;re basically doing is giving hints to the driver that say &ldquo;it&rsquo;s okay if you do these operations at less than full 32-bit precision, but it&rsquo;s also okay if you do them at full precision&rdquo;. This basically gives you runtime polymorphic shaders: your final shader only exists as 1 blob of compiled bytecode, but depending on the hardware and driver you may or may not get fp16 ops at runtime. The nice part is that this spares you the pain of having to compile your shader twice, which is great! The downside is that you&rsquo;re not really sure what&rsquo;s going on with the hardware unless you check device caps, and/or check the actual ISA generated by the driver.</p>
<p>When working with HLSL, the way you get this behavior is by using the <code>min16float</code> type and its variants (<code>min16float2</code>, <code>min16float3x3</code>, etc.) <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. By declaring this type for a variable you&rsquo;re providing a hint specifying that it&rsquo;s okay for the driver to store it with less precision, as long as it&rsquo;s greater than or equal to 16 bits. In general it works exactly as you&rsquo;d expect: you can downcast from fp32 to to fp16 by casting with <code>min16float()</code>, and you&rsquo;ll get warnings if an assignment causes an implicit truncation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">float</span> <span class="n">fp32</span> <span class="o">=</span> <span class="n">DoSomething</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">min16float</span> <span class="n">fp16_x</span> <span class="o">=</span> <span class="n">fp32</span><span class="p">;</span>               <span class="c1">// truncation warning
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">min16float</span> <span class="n">fp16_y</span> <span class="o">=</span> <span class="n">min16float</span><span class="p">(</span><span class="n">fp32</span><span class="p">);</span>
</span></span></code></pre></div><p>HLSL allows you to overload functions based on the input type being <code>float</code> or <code>min16float</code>, which means you can create dual versions of your utility functions for fp16 or fp32 when necessary without having to rename them:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">float</span> <span class="nf">DoSomething</span><span class="p">(</span><span class="n">in</span> <span class="kt">float</span> <span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// do some stuff in fp32
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">min16float</span> <span class="nf">DoSomething</span><span class="p">(</span><span class="n">in</span> <span class="n">min16float</span> <span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// do some stuff in fp16
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>If you enable warnings as errors (which you probably should!) you can start out by converting a few high-level values to fp16 and then letting the compiler point out all of the places where you need to explicitly cast to <code>min16float</code>. Unfortunately it won&rsquo;t catch places where you go from fp16 to fp32, since that doesn&rsquo;t truncate. So you&rsquo;ll need to watch out for those on your own. You&rsquo;ll also need to make sure that you&rsquo;re not inadvertantly using the <code>half</code> datatype, since by default this is still mapped to fp32 in HLSL! I would recommend making some defines in a common header file that map <code>half</code> to <code>min16float</code>, since that lets you avoid that potential issue. It also makes your code cleaner to look at IMO, and makes it easy to globally disable fp16 should you need to:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#define half min16float
</span></span></span><span class="line"><span class="cl"><span class="cp">#define half2 min16float2
</span></span></span><span class="line"><span class="cl"><span class="cp">#define half3 min16float3
</span></span></span><span class="line"><span class="cl"><span class="cp">#define half4 min16float4
</span></span></span><span class="line"><span class="cl"><span class="cp">#define half3x3 min16float3x3
</span></span></span><span class="line"><span class="cl"><span class="cp">#define half3x4 min16float3x4
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="c1">// keep going if you need more matrix types
</span></span></span></code></pre></div><p>You&rsquo;ll also need to be careful with literals, which are a little weird when working with the <code>min16float</code> types. In HLSL you have 3 floating point suffixes: <code>f</code>, <code>d</code>, and <code>h</code>. These map to the <code>float</code>, <code>double</code>, and <code>half</code> types respectively. Sadly there&rsquo;s no literal for <code>min16float</code>, and the <code>h</code> suffix maps back to fp32 (since <code>half</code> is mapped to <code>float</code> internally). So for literals you&rsquo;ll instead want to use an unadorned literal (leave off the suffix entirely), and let the compiler sort it out on assignment. The unfortunate side effect of doing this is that calls to overloaded functions can resolve to the fp32 version if you pass a literal:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">float</span> <span class="nf">DoSomething</span><span class="p">(</span><span class="n">in</span> <span class="kt">float</span> <span class="n">x</span><span class="p">,</span> <span class="n">in</span> <span class="kt">float</span> <span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// do some stuff in fp32
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">min16float</span> <span class="nf">DoSomething</span><span class="p">(</span><span class="n">in</span> <span class="n">min16float</span> <span class="n">x</span><span class="p">,</span> <span class="n">in</span> <span class="n">min16float</span> <span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// do some stuff in fp16
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">min16float</span> <span class="n">x</span> <span class="o">=</span> <span class="mf">2.0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">min16float</span> <span class="n">y</span> <span class="o">=</span> <span class="n">DoSomething</span><span class="p">(</span><span class="mf">1.0</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span>     <span class="c1">// truncation warning!
</span></span></span></code></pre></div><p>Fortunately you can work around this by explicitly casting the literal to 16-bit type:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">min16float</span> <span class="n">y</span> <span class="o">=</span> <span class="n">DoSomething</span><span class="p">(</span><span class="n">min16float</span><span class="p">(</span><span class="mf">1.0</span><span class="p">),</span> <span class="n">x</span><span class="p">);</span>     <span class="c1">// no warning
</span></span></span></code></pre></div><p>There&rsquo;s one more big gotcha with flexible precision: you can&rsquo;t store them in buffer resources like constant buffers and structured buffers. It makes sense if you think about it: it&rsquo;s okay for temporary values stored in registers to be runtime polymorphic, but it would be really bad if the size and packing of your constant buffer changed depending on the video card and driver you were using! So you&rsquo;re stuck using 32-bit types for those cases. Ideally you would want to store fp16 values in your buffers, since that gives you more compact data and also lets you avoid any cost there might be from converting fp32 to fp16 in the shader core. But your only real option for doing that is to pack the fp16 values in a 32-bit type, and then convert back and forth in the shader:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">CBLayout</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">xy</span><span class="p">;</span>    <span class="c1">// two fp16 values packed in the upper and lower 16 bits
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ConstantBuffer</span><span class="o">&lt;</span><span class="n">CBLayout</span><span class="o">&gt;</span> <span class="n">CB</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">min16float</span> <span class="n">x</span> <span class="o">=</span> <span class="n">min16float</span><span class="p">(</span><span class="n">f16tof32</span><span class="p">(</span><span class="n">CB</span><span class="p">.</span><span class="n">x</span> <span class="o">&amp;</span> <span class="mh">0xFFFF</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">min16float</span> <span class="n">y</span> <span class="o">=</span> <span class="n">min16float</span><span class="p">(</span><span class="n">f16tof32</span><span class="p">(</span><span class="n">CB</span><span class="p">.</span><span class="n">y</span> <span class="o">&gt;&gt;</span> <span class="mi">16</span><span class="p">));</span>
</span></span></code></pre></div><p>Really you&rsquo;re asking the compiler to convert from fp16 -&gt; fp32 and then truncate back down to fp16, and hoping that the driver&rsquo;s JIT compiler is smart enough to remove all of that when it generates the final ISA. <a href="https://gpuopen.com/first-steps-implementing-fp16/">Tom Hammersley&rsquo;s post</a> on GPUOpen suggest that AMD&rsquo;s drivers are capable of recognizing this pattern, but I have no idea how reliable that is in practice across all vendors.</p>
<p>If you&rsquo;re using the open source <a href="https://github.com/microsoft/DirectXShaderCompiler">DirectX Shader Compiler</a> (DXC) to compile your HLSL, then all of this works in both D3D12 and Vulkan. When compiling to DXIL, you&rsquo;ll see the compiler emit code that uses the <code>half</code> data type. It will also mark the shader as requiring the &ldquo;minimum-precision&rdquo; feature. When <a href="https://github.com/microsoft/DirectXShaderCompiler/wiki/SPIR%E2%80%90V-CodeGen">compiling to SPIR-V</a>, you&rsquo;ll see that ops involving min16float are decorated with <a href="https://www.khronos.org/registry/spir-v/specs/1.0/SPIRV.html#_a_id_relaxedprecisionsection_a_relaxed_precision">RelaxedPrecision</a>, which is the SPIR-V version of minimum precision. FXC also supports the minimum precision types for D3D11 and D3D12, if you&rsquo;re unfortunate enough to still be using that (which probably applies to most of us).</p>
<p>If you want to check what the hardware supports at runtime, in D3D11 you can query the device&rsquo;s <a href="https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#LowPrecisionShaders">minimum precision</a> support by calling <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11device-checkfeaturesupport">ID3D11Device::CheckFeatureSupport</a> and passing <code>D3D11_FEATURE_SHADER_MIN_PRECISION_SUPPORT</code> to get back a <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_feature_data_shader_min_precision_support">D3D11_FEATURE_DATA_SHADER_MIN_PRECISION_SUPPORT</a> struct with the caps. In D3D12 it&rsquo;s similar, except you want to ask for <code>D3D12_FEATURE_D3D12_OPTIONS</code> and look at the <code>MinPrecisionSupport</code> member of the returned <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_feature_data_d3d12_options">D3D12_FEATURE_DATA_D3D12_OPTIONS </a> structure. Unfortunately these values aren&rsquo;t really a guarantee: the driver is free to choose what precision to use for any particular operation even if it reports fp16 support. So you really need to use an IHV disassembly tool if you want to be 100% sure of what your GPU is doing. As of right now this value reports 16-bit support for Vega or later on AMD for both D3D11 and D3D12. Intel also reports that it supports fp16 on my Gen9 integrated GPU. Meanwhile for my Turing-based RTX 2080 the driver reports full fp16 support through D3D11, but not through D3D12. Strange!</p>
<p>Unfortunately for Vulkan there&rsquo;s no caps or extensions to indicate how the hardware will interpret <code>RelaxedPrecision</code> operations. This means that IHV tools or documentation are your only means of determining whether or not your operations will actually execute at fp16 precision.</p>
<p>When targetting Vulkan you also have an additional path for targetting fp16 through <a href="https://github.com/KhronosGroup/SPIRV-Tools">spirv-opt</a>, which is the standard SPIR-V optimizer used by both glslc as well as DXC. Very recently a new <code>--relax-float-ops</code> pass was added to spirv-opt, which automatically tags everything with <code>RelaxedPrecision</code>. More details are provided in <a href="https://www.lunarg.com/wp-content/uploads/2019/09/Automatic-RelaxedPrecision-Decoration-and-Conversion-in-Spirv-Opt_r1.pdf">this whitepaper</a>. This pass can also be enabled when using DXC by using the <code>-Oconfig</code> command line argument to invoke additional spirv-opt passes. I&rsquo;m not sure how actually useful this is in practice, since you&rsquo;re generally going to want to be doing certain things at fp32 when they actually require that amount of precision.</p>
<h2 id="explicit-fp16">Explicit FP16</h2>
<p>Like the name suggests, with this path you&rsquo;ll be writing code that explicitly works with fp16 data types instead of polymorphic types. Being explicit has its advantages: you know for sure that it&rsquo;s going to be run at exactly 16-bit precision without having to query device caps. But the major downside is that your shader will now only work on devices that support the related features and extensions. This means you&rsquo;ll most likely need to compile shaders with and without fp16 types, at least until fp16-capable hardware is ubiquitious on desktop. The other major advantage of being explicit is that you can actually use fp16 data types in your resources, which means you can pack fp16/uint16 data in your constant buffers and structured buffers without needing a pile of ugly code to unpack and convert from 32-bit types. That&rsquo;s not only convenient, it also makes it easier for you and the driver to avoid unnecessary conversions when performing fp16 math.</p>
<p>Explicit fp16 is only <a href="https://github.com/microsoft/DirectXShaderCompiler/wiki/16-Bit-Scalar-Types">supported in DXC</a> through Shader Model 6.2, which means there&rsquo;s no support for FXC or D3D11. To compile your shader for explict fp16 you&rsquo;ll want to pass <code>-enable-16bit-types</code> as an argument and make sure that you&rsquo;re using one of the <code>*_6_2</code> profiles (or higher). Once you flip this switch, the <code>half</code> type stops behaving as a <code>float</code> and instead acts as a true fp16 type. They&rsquo;ve also added a new <code>float16_t</code> type that you can use as well, along with matching <code>float32_t</code> and <code>float64_t</code> types. Here&rsquo;s simplified version of the table from their wiki showing how each type behaves with and without the switch:</p>
<table>
<thead>
<tr>
<th style="text-align:center">HLSL Type</th>
<th style="text-align:center">Without -enable-16bit-types</th>
<th style="text-align:center">-enable-16bit-types</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">float</td>
<td style="text-align:center">float32_t</td>
<td style="text-align:center">float32_t</td>
</tr>
<tr>
<td style="text-align:center">float32_t</td>
<td style="text-align:center">float32_t</td>
<td style="text-align:center">float32_t</td>
</tr>
<tr>
<td style="text-align:center">min10float</td>
<td style="text-align:center">min16float(warning)</td>
<td style="text-align:center">float16_t(warning)</td>
</tr>
<tr>
<td style="text-align:center">min16float</td>
<td style="text-align:center">min16float</td>
<td style="text-align:center">float16_t(warning)</td>
</tr>
<tr>
<td style="text-align:center">half</td>
<td style="text-align:center">float32_t</td>
<td style="text-align:center">float16_t</td>
</tr>
<tr>
<td style="text-align:center">float16_t</td>
<td style="text-align:center">N/A</td>
<td style="text-align:center">float16_t</td>
</tr>
<tr>
<td style="text-align:center">double</td>
<td style="text-align:center">float64_t</td>
<td style="text-align:center">float64_t</td>
</tr>
<tr>
<td style="text-align:center">float64_t</td>
<td style="text-align:center">float64_t</td>
<td style="text-align:center">float64_t</td>
</tr>
<tr>
<td style="text-align:center">int</td>
<td style="text-align:center">int32_t</td>
<td style="text-align:center">int32_t</td>
</tr>
<tr>
<td style="text-align:center">int32_t</td>
<td style="text-align:center">int32_t</td>
<td style="text-align:center">int32_t</td>
</tr>
<tr>
<td style="text-align:center">uint</td>
<td style="text-align:center">uint32_t</td>
<td style="text-align:center">uint32_t</td>
</tr>
<tr>
<td style="text-align:center">uint32_t</td>
<td style="text-align:center">uint32_t</td>
<td style="text-align:center">uint32_t</td>
</tr>
<tr>
<td style="text-align:center">min12int</td>
<td style="text-align:center">min16int(warning)</td>
<td style="text-align:center">int16_t(warning)</td>
</tr>
<tr>
<td style="text-align:center">min16int</td>
<td style="text-align:center">min16int</td>
<td style="text-align:center">int16_t(warning)</td>
</tr>
<tr>
<td style="text-align:center">int16_t</td>
<td style="text-align:center">N/A</td>
<td style="text-align:center">int16_t</td>
</tr>
<tr>
<td style="text-align:center">min12uint</td>
<td style="text-align:center">min16uint(warning)</td>
<td style="text-align:center">uint16_t(warning)</td>
</tr>
<tr>
<td style="text-align:center">min16uint</td>
<td style="text-align:center">min16uint</td>
<td style="text-align:center">uint16_t(warning)</td>
</tr>
<tr>
<td style="text-align:center">uint16_t</td>
<td style="text-align:center">N/A</td>
<td style="text-align:center">uint16_t</td>
</tr>
<tr>
<td style="text-align:center">int64_t</td>
<td style="text-align:center">int64_t</td>
<td style="text-align:center">int64_t</td>
</tr>
<tr>
<td style="text-align:center">uint64_t</td>
<td style="text-align:center">uint64_t</td>
<td style="text-align:center">uint64_t</td>
</tr>
</tbody>
</table>
<p>As you can see you also get 16-bit signed/unsigned integers with this flag, which is great for packing more data into your buffers. The compiler also convieniently maps the <code>min16float</code>/<code>min16int</code>/<code>min16uint</code> minimum precision types to their native 16-bit counterparts, which can simplify porting older code. Just be aware that the compiler will emit a warning in this case, which is meant to remind you that you&rsquo;re no longer getting the &ldquo;flexible precision&rdquo; behavior that those types normally provide.</p>
<p>Like I mentioned earlier, in this mode <code>half</code> is back to representing true fp16 values instead of being sliently mapped to the <code>float</code> type under the hood. This also means that the <code>h</code> suffix for literals actually works the way you want it to, which lets you avoid the amiguities that unadorned literals cause with overload resolution. In my opinion this results in cleaner and easier to understand code with less surprising behavior. Here&rsquo;s the example that I showed earlier with functions overloaded by return and parameter types, except this time we&rsquo;ll use explicit fp16:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">float</span> <span class="nf">DoSomething</span><span class="p">(</span><span class="n">in</span> <span class="kt">float</span> <span class="n">x</span><span class="p">,</span> <span class="n">in</span> <span class="kt">float</span> <span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// do some stuff in fp32
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">half</span> <span class="nf">DoSomething</span><span class="p">(</span><span class="n">in</span> <span class="n">half</span> <span class="n">x</span><span class="p">,</span> <span class="n">in</span> <span class="n">half</span> <span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// do some stuff in fp16
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">half</span> <span class="n">x</span> <span class="o">=</span> <span class="mf">2.0</span><span class="n">h</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">half</span> <span class="n">y</span> <span class="o">=</span> <span class="n">DoSomething</span><span class="p">(</span><span class="mf">1.0</span><span class="n">h</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span>  <span class="c1">// this is fine!
</span></span></span></code></pre></div><p>With explicit fp16 we know that we&rsquo;re giving up the convenience of polymorphic data types, which means our compiled shader will only run on hardware that actually supports fp16 operations. So how do we check this in our graphics APIs? In D3D12, we do this by calling <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-checkfeaturesupport">ID3D12::CheckFeatureSupport</a> and passing <code>D3D12_FEATURE_D3D12_OPTIONS4</code> to obtain a <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_feature_data_d3d12_options4">D3D12_FEATURE_DATA_D3D12_OPTIONS4</a> structure, and checking the value of the <code>Native16BitShaderOpsSupported</code> member. Note that the DXIL produced by the compiler will look very similar to what you would get when using the minimum precision types. The major difference is that it will be marked with the <code>UseNativeBit16</code> flag in the metadata, which tells the runtime and driver that the fp16 ops need to be natively supported.</p>
<p>On Vulkan things are unfortunately a bit more complicated, since you need to deal with extensions. First, you&rsquo;ll want to check if your GPU and driver support the <a href="https://github.com/KhronosGroup/Vulkan-Docs/blob/master/appendices/VK_KHR_shader_float16_int8.txt">VK_KHR_shader_float16_int8</a> extension. If it does, you&rsquo;ll have two extended device properties that you need to check: <code>shaderFloat16</code> for fp16 support, and <code>shaderInt8</code> for 8-bit integer support. If <code>shaderFloat16</code> is set, then you can use native fp16 math operations in your shaders. However this only applies to math operations, and not anything involving I/O with resources! For that you need to check the <a href="https://github.com/KhronosGroup/Vulkan-Docs/blob/1.0/doc/specs/vulkan/appendices/VK_KHR_16bit_storage.txt">VK_KHR_16bit_storage</a> extension, which includes 4 new properties that you need to check: <code>storageBuffer16BitAccess</code>, <code>uniformAndStorageBuffer16BitAccess</code>, <code>storagePushConstant16</code>, and <code>storageInputOutput16</code>. These 4 flags correspond to the SPIR-V capabilities outlined in the <a href="http://htmlpreview.github.io/?https://github.com/KhronosGroup/SPIRV-Registry/blob/master/extensions/KHR/SPV_KHR_16bit_storage.html">SPV_KHR_16bit_storage</a> extension, and basically tell you what classes of resources can use fp16 types in them. Nvidia Turing-based hardware currently <a href="https://vulkan.gpuinfo.org/displayreport.php?id=6907#extended">reports support</a> for <code>shaderFloat16</code>, <code>shaderInt8</code>, <code>storageBuffer16BitAccess</code>, <code>uniformAndStorageBuffer16BitAccess</code>, and <code>storagePushConstant16</code>, but not <code>storageInputOutput16</code>. Meanwhile AMD Vega and Navi-based hardware <a href="https://vulkan.gpuinfo.org/displayreport.php?id=6934#extended">reports support</a> for <code>shaderFloat16</code>, <code>shaderInt8</code>, <code>storageBuffer16BitAccess</code>, <code>uniformAndStorageBuffer16BitAccess</code>, and <code>storageInputOutput16</code>, but not <code>storagePushConstant16</code>. So basically you&rsquo;ll want to avoid using <code>half</code> types as inputs or outputs from your shader entry points since <code>storageInputOutput16</code> isn&rsquo;t universally supported, and you&rsquo;ll also want to avoid using 16-bit push constants since <code>storagePushConstant16</code> isn&rsquo;t universally supported.</p>
<p>That&rsquo;s a lot of details, but we&rsquo;re not quite done yet! It turns out that the original SPIR-V spec had a bunch of instructions included as &ldquo;extension instructions&rdquo;, which are documented <a href="https://www.khronos.org/registry/spir-v/specs/1.0/GLSL.std.450.html">here</a>. These are mostly transcendental functions like <code>Pow</code> and <code>Sin</code>, as well as the <code>FClamp</code> instruction that&rsquo;s commonly used for implementing the <code>saturate()</code> intrinsic. The original spec for these defined them as only taking 32-bit floating point inputs, which meant it was illegal to use them with fp16 values. The <a href="https://github.com/KhronosGroup/SPIRV-Registry/blob/master/extensions/AMD/SPV_AMD_gpu_shader_half_float.asciidoc">SPV_AMD_gpu_shader_half_float</a> extension from AMD lifted this restriction, allowing AMD hardware to support these instructions with fp16 values. Fortunately the SPIR-V spec was revised to add full fp16 support sometime after that AMD extension was released, which means the extension is no longer necessary. Or rather I should say it&rsquo;s <em>almost</em> unnecessary, since the interpolation instructions still only support fp32 (the AMD extension registry has been updated to reflect this). <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>When <a href="https://github.com/Microsoft/DirectXShaderCompiler/blob/master/docs/SPIR-V.rst#minimal-precision-scalar-types">using DXC to compile HLSL to SPIR-V</a> with <code>-enable-16bit-types</code> you should see ops generated that use types declared like this:</p>
<pre tabindex="0"><code>%half = OpTypeFloat 16
%v3half = OpTypeVector %half 3
</code></pre><p>&hellip;as opposed to seeing ops with the <code>RelaxedPrecision</code> tag. You&rsquo;ll also see ops indicating which extension features are required, such as <code>OpCapability Float16</code> for general fp16 math support and <code>OpCapability UniformAndStorageBuffer16BitAccess</code> for using 16-bit types in uniform or storage buffers.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Hopefully this gives you enough information to decide on how to move forward with fp16 in your HLSL codebase. As of right now using the flexible precision path seems to be the only reasonable choice for targetting PC hardware, since fp16 math is only supported on very recent video cards. However it seems clear to me that explicit fp16 is going to be future, since it&rsquo;s much nicer to work with once you can use it. The only question is how long it will take until we can safely ignore all hardware that can&rsquo;t do it. I&rsquo;m not the kind of person to predict the future, so I&rsquo;ll leave that part up to someone else. 😄</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>AMD&rsquo;s Polaris series (gfx803) supports fp16 at full rate (non-packed, unlike Vega) but it doesn&rsquo;t seem to be exposed in any API. Update 10/7/2019: Allan MacKinnon has helpfully pointed me to <a href="https://github.com/GPUOpen-Drivers/AMDVLK/issues/94">this GitHub issue</a> where AMD engineers explained why they never enabled fp16 support for pre-gfx900 hardware.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>There&rsquo;s actually a few other types such as <code>min10float</code> and <code>min16uint</code> to along with the 16-bit float types, but this article is just going to focus on fp16.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>The current version of DXC is erroneously marking the SPIR-V as requiring the SPV_AMD_gpu_shader_half_float extension when any of the extended instructions are emitted for fp16. I&rsquo;ve filed an <a href="https://github.com/microsoft/DirectXShaderCompiler/issues/2500">issue</a>, and I assume it will fixed very soon.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
		</item>
		
		<item>
			<title>Hello, Hugo</title>
			<link>https://therealmjp.github.io/posts/hello-hugo/</link>
			<pubDate>Mon, 12 Aug 2019 00:00:00 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/hello-hugo/</guid>
			<description>For the past 11(!) years I&amp;rsquo;ve hosted my blog on wordpress.com, which had a very low barrier to entry. But for a year or two now I&amp;rsquo;ve been itching to move on to something that I actually enjoy working with, and that doesn&amp;rsquo;t leave me locked in to the WordPress platform and hosting services. Over the past two weeks or so I&amp;rsquo;ve been working on setting up this new blog, which is hosted on GitHub pages and is generated as a static site using Hugo.</description>
			<content:encoded><![CDATA[<p><img src="/images/hello-hugo/hugo-logo-wide.svg" alt="Hugo logo"></p>
<p>For the past 11(!) years I&rsquo;ve hosted my <a href="https://mynameismjp.wordpress.com/">blog</a> on wordpress.com, which had a very low barrier to entry. But for a year or two now I&rsquo;ve been itching to move on to something that I actually enjoy working with, and that doesn&rsquo;t leave me locked in to the WordPress platform and hosting services. Over the past two weeks or so I&rsquo;ve been working on setting up this new blog, which is hosted on <a href="https://pages.github.com/">GitHub pages</a> and is generated as a static site using <a href="https://gohugo.io/">Hugo</a>. If you&rsquo;re not familiar with the term &ldquo;static site&rdquo;, what this basically means is that I use the Hugo executable to convert source markdown files into pure HTML files that I can load onto whichever host I want, without any crazy scripting or server-side stuff to generate that HTML on-the-fly whenever a user loads the web page. From my point of view I get the following benefits:</p>
<ul>
<li>I write the posts using markdown in my text editor of choice instead of fighting with a WYSIWYG editor (with fast live reloading provided by the <a href="https://gohugo.io/commands/hugo_server/">Hugo local server</a>)</li>
<li>The site that people see is clean and fast to load/render, with minimal javascript</li>
<li>I can put the generated HTML on whichever host I please</li>
<li>I can choose from a wide selection of themes with cool features, and customize the theme and individual posts by dropping down to custom HTML/CSS/Javascript if I need to</li>
<li>I don&rsquo;t have to pay money just to keep my blog from serving viewers with ads that have potentially malicious code embedded in them</li>
</ul>
<p>From now on, all new posts will be hosted here on GitHub pages. While I&rsquo;m sure it&rsquo;s annoying for everyone to update their RSS feeds and such, hopefully you all can appreciate the benefits on my end. I have no plans to shut down the old blog, so any old links and such will continue to work until WordPress decides to shut down their free hosting. The only thing that will change over there is that I&rsquo;m going to stop paying for the ad-free version since I don&rsquo;t want to keep paying for that indefinitely.</p>
<p>For the rest of this post I&rsquo;m going to describe my experiences with migrating my blog to Hugo and GitHub Pages. If you&rsquo;re not interested in that, feel free to move on. Just make sure to update your <a href="../index.xml">RSS feed</a> first!</p>
<h2 id="setting-up-hugo-and-github-pages">Setting Up Hugo and GitHub Pages</h2>
<p>Hugo is actually really easy to work with, at least if you&rsquo;re a programmer like me. It&rsquo;s all contained in a single executable that you can download from the <a href="https://github.com/gohugoio/hugo/releases">releases page on GitHub</a>, so there&rsquo;s no need to compile things from source or go through a multi-step installation process. Hugo has <a href="https://gohugo.io/documentation/">comprehensive docs</a> that you can read through, and also has a <a href="https://gohugo.io/getting-started/quick-start/">quick start guide</a> that can get you up and running with minimal fuss. I set things up so that I have two git repositories hosted on GitHub:</p>
<ul>
<li>A private repo that contains all of the source markdown/theme/image files that Hugo uses to generate the static site HTML</li>
<li>A public repo that has the generated HTML files, using GitHub Pages to host</li>
</ul>
<p>The private repo isn&rsquo;t strictly necessary. I like it because it gives me version control on my source content, and it makes it easy to work on my blog posts across multiple computers. The public repo is necessary for using GitHub Pages, but that could easily use any kind of generic hosting service instead. GitHub Pages is nice because it&rsquo;s free, and because it uses standard git functionality to push new changes to the live site. Really it&rsquo;s up to you to decide how you want to manage your source content and host the final generated files.</p>
<p>For LaTeX/math content, I integrated <a href="https://katex.org/">KaTeX</a> by referencing their <a href="https://katex.org/docs/autorender.html">auto-render extension</a>. This was pretty straightforward despite me knowing next to nothing about JS or web development: the <a href="https://themes.gohugo.io/hermit/">theme</a> that I&rsquo;m currently using lets you add a partial HTML file that gets added as an extra footer for every post, so I used that to reference the KaTex script. In fact I even got it working so that the script is conditionally included when the post enables a &ldquo;math&rdquo; config variable in the front matter, which means that the script only gets loaded for posts that actually need it. LaTeX syntax within markdown has some <a href="https://gohugo.io/content-management/formats/#issues-with-markdown">well-known issues</a> that can make it a bit clunky to work with, but fortunately it&rsquo;s very easy to <a href="https://gohugo.io/content-management/formats/#use-mmark">have Hugo</a> use the <a href="https://github.com/mmarkdown/mmark">Mmark parser</a> instead of the default parser. Mmark lets you avoid having to escape special characters like <code>_</code> that frequently pop up in LaTeX, which can save some hair-pulling.</p>
<h2 id="migrating-wordpress-content">Migrating WordPress Content</h2>
<p>This was the hard part of getting things going on my new blog. I strongly considered not converting the old posts at all, and just leaving them on the WordPress-hosted site. But ultimately I decided that it would be for the best to have all of those posts converted to a format that was more easily movable to any kind of hosting. My first attempt at converting my old posts was to run the <a href="https://github.com/wooni005/exitwp-for-hugo">exitwp-for-hugo</a> python script on the exported XML from my wordPress blog. This script caused a segfault when I ran it (not a Python exception, an actual segfault!), so I quickly gave up on it. I had more luck with the <a href="https://github.com/palaniraja/blog2md">blog2md script</a>, which actually ran and produced some markdown files that I could use. blog2md uses node.js, which I previously had zero experience working with. Fortunately I had an Ubuntu installation that I could access through <a href="https://docs.microsoft.com/en-us/windows/wsl/faq">WSL</a>, which made it relatively easy to install node.js and npm through Ubuntu&rsquo;s Advanced Packaging Tool (apt). Despite some warnings about a <a href="https://www.davidhaney.io/npm-left-pad-have-we-forgotten-how-to-program/">deprecated package</a>, the script produced 80 separate markdown files that roughly converted the content present in the exported XML file that WordPress produced for me. While it was great that this got me started, I unfortunately had to do a <em>lot</em> of manual fixing and cleanup to get things looking presentable. In no particular order, these are the main issues that I had to deal with:</p>
<ul>
<li>
<p>The WordPress export seemed to be wildly inconsistent with how it handled newlines. I have no idea if this is because of something I did when I typed the original blog posts, but most of the time the exported XML would just contain a newline character in the text instead of an HTML line break (<code>&lt;br&gt;</code>). The library being used internally by blog2md would just straight-up ignore newlines, collapsing many of my posts into gigantic single paragraphs. I worked around this by writing a pre-process script to add a <code>&lt;br&gt;</code> wherever a newline character was found within the text body, which sometimes added more line breaks than needed. But this was better starting point than having no newlines at all.</p>
</li>
<li>
<p>WordPress <code>[code]</code> blocks did not get converted by the script, so I went through and manually converted those to markdown code blocks with c/cpp formatting.</p>
</li>
<li>
<p>The script was helpful in trying to proactively add the escape character (<code>\</code>) in cases where it would cause issues with special markdown characters like <code>[</code>, <code>_</code>, and <code>*</code>. While this was fine (and desirable) for normal text, it unfortunately applied this to all of my code blocks as well. This meant I had to manually go in and remove the escape characters. This was rather tedious since I couldn&rsquo;t just blindly search and replace, and could only do it within code blocks.</p>
</li>
<li>
<p>Tables from the WordPress export weren&rsquo;t handled at all, so I had to manually convert them using the <a href="https://www.markdownguide.org/extended-syntax/#tables">extended syntax for tables</a> in markdown.</p>
</li>
<li>
<p>Footnotes weren&rsquo;t handled, but these were pretty easy to fix up by converting them to use the <a href="https://www.markdownguide.org/extended-syntax/#footnotes">markdown syntax for footnotes</a>.</p>
</li>
<li>
<p>All of the images work in the converted posts, but they just point to the images hosted on the old wordpress.com site. For now I&rsquo;m just going to leave this since I don&rsquo;t think it will cause any problems in the short term, but ideally I could somehow scrape all of those images so that I can re-host them and make it all easy to migrate in the case that wordpress.com ever shuts down. If anybody knows a thing or two about doing this, please let me know!</p>
</li>
<li>
<p>Speaking of images, a few of the posts from my series about GPU barriers used a WordPress slideshow to allow the reader to flip through the synchronization examples. This obviously can&rsquo;t be converted to markdown, but fortunately I was able to quickly integrate <a href="https://github.com/liwenyip/hugo-easy-gallery/">hugo-easy-gallery</a> by Li-Wen Yip. It also helped that I still have local copies of all of the source images that I generated for the slideshow. 😃</p>
</li>
</ul>
<h2 id="comments">Comments</h2>
<p>A functional comments system is probably one of the strongest benefits of an out-of-the-box blogging platform like WordPress or Blogger, and I&rsquo;ve always liked having them for when people share additional info or find a mistake in my post. Disqus is probably the most popular option for other blogs using Hugo, but I had some <a href="https://en.wikipedia.org/wiki/Disqus#Criticism,_privacy,_and_security_concerns">strong reservations</a> about embedding that into my site. For a while I considered simply having no comments on the new blog. These days most of the discussion tend to happen on Twitter anyway, but I still wanted to have some sort of option for people who don&rsquo;t like going on Twitter<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. For now I&rsquo;m going to try using <a href="https://utteranc.es/">utterances</a> and see how that goes. utterances basically uses a bot to create an issue on a backing GitHub repo, and then uses the GitHub API add comments to that issue and the contents right in the blog post. This means you get the ability to post rich text, and the backing data is all stored somewhere where anyone can access it and where I can also moderate it if necessary (comment spam was huge issue on WordPress). The downside of course is that you need a GitHub account to actually comment, which is unfortunate. But hopefully that already accounts for a decent percentage of people who might want to read a graphics programming blog. 😄</p>
<h2 id="thats-it">That&rsquo;s It</h2>
<p>I hope you all like the new site! I&rsquo;ve got a few posts on the backlog to catch up on, so hopefully I should have some actual new content coming up soon. Don&rsquo;t forget to update your <a href="../index.xml">RSS feed</a> if you&rsquo;re using one!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I also feel like Twitter is actively hostile to archiving threads for future reference. Searching is difficult, and it&rsquo;s hard or impossible to fully read through the various reply chains that invariably break off in the larger threads. And then of course at any point someone who posted in that thread could decide to delete their Twitter thread and irrecoverably break the connection between the tweets.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
		</item>
		
		<item>
			<title>Adventures in Retro Development: SNES Edition</title>
			<link>https://therealmjp.github.io/posts/adventures-in-retro-development-snes-edition/</link>
			<pubDate>Tue, 15 Jan 2019 05:36:42 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/adventures-in-retro-development-snes-edition/</guid>
			<description>When I was growing up, the SNES was easily my favorite console. How could it not be, with top-tier games like Super Metroid and Mega Man X available? I had always wanted to learn how to program for the SNES so that I could develop my own silly games for the hardware that ran my favorite games as a kid. I decided to use this past holiday break as an excuse to finally get started, and I had a great time doing it!</description>
			<content:encoded><![CDATA[<p><img src="/images/converted/adventures-in-retro-development-snes-edition/waycool.png" alt="waycool"></p>
<p>When I was growing up, the SNES was easily my favorite console. How could it not be, with top-tier games like Super Metroid and Mega Man X available? I had always wanted to learn how to program for the SNES so that I could develop my own silly games for the hardware that ran my favorite games as a kid. I decided to use this past holiday break as an excuse to finally get started, and I had a great time doing it! I love learning about old hardware and development environments, so this was really right up my alley. However it was a bit tough getting started, so I thought I would share some of my experiences in case it helps to spare others a bit of pain.</p>
<p>Just for context, I really knew very little about any of this before I dove in. I started learning programming around 2005-2006, and my first forays into graphics programming used DX9-capable hardware. This was a full two decades after the 65816 was first released, and well after the era of sprite and character-based display chips that the SNES graphics hardware was spawned from. My only prior experience in hand-writing assembly programs was a single micocontrollers class that I took in college, so I&rsquo;m hardly an expert in that domain. The point here is that if I can learn how to program this crazy machine, I&rsquo;m sure that you can too!</p>
<h2 id="hardware-basics">Hardware Basics</h2>
<p>The SNES has a few main pieces of hardware that are relevant to the programmer:</p>
<ul>
<li>
<p>The main CPU, which is a clone of a <a href="https://en.wikipedia.org/wiki/WDC_65816/65802">65816</a></p>
</li>
<li>
<p>128 KB of RAM, referred to as &ldquo;Work RAM&rdquo; or WRAM for short</p>
</li>
<li>
<p>Anywhere from 256 KB to 4 MB of ROM data mapped directly into the CPU&rsquo;s address space, depending on the cartridge.</p>
</li>
<li>
<p>The &ldquo;Picture Processing Unit&rdquo; (PPU), which is the dedicated sprite and background-drawing processor</p>
</li>
<li>
<p>64 KB of VRAM, which holds tile and map data used by the PPU for drawing sprites and backgrounds. Not directly accessible to the CPU!</p>
</li>
<li>
<p>A small bit of memory for storing sprite attributes used by the PPU (position, flipping, tile index, etc.),  known as Object Attribute Memory (OAM). Also not mapped to the CPU&rsquo;s address space.</p>
</li>
<li>
<p>Another small bit of memory for storing the 16 color palettes used by sprite and background tiles, known as Color Generator RAM (CGRAM). Each palette contains 16 15-bit colors.</p>
</li>
<li>
<p>A DMA unit that can be used to copy data between memory banks, including  VRAM/OAM/CGRAM. Using this DMA freezes the CPU (they share the same bus), so it&rsquo;s effectively synchronous.</p>
</li>
<li>
<p>A custom 8-bit Sony CPU used as a sound processor, called the SPC700. This guy is hooked up to an 8-channel DSP and 64 KB of its own RAM.</p>
</li>
</ul>
<p>A more complete overview of the hardware can be found on the <a href="https://en.wikibooks.org/wiki/Super_NES_Programming/SNES_Specs">Super NES Programming Wiki</a>. A full dump of all of the system&rsquo;s internals can be found in the <a href="http://problemkaputt.de/fullsnes.htm">Nocash fullsnes hardware specification</a>, which is definitely a link you&rsquo;ll want to bookmark. It has a full list of the system&rsquo;s memory-mapped registers used for I/O with the custom hardware, and it also has the complete mapping of the CPU&rsquo;s address space. In my experience it has the most complete description of how the custom hardware works, which is vital for understanding the specifics of how to work with the PPU.</p>
<p>If you look around, you can also find a scanned copy of the original SNES development manual out there. While it&rsquo;s fun to look at from a historical perspective, it&rsquo;s honestly not really all that useful for actual development. It doesn&rsquo;t do a very good job of explaining the more esoteric parts of the hardware, and its filled with mostly-useless &ldquo;examples&rdquo; that take the form of of large flow charts. The Nocache docs are a much better reference in my opinion, and much easier to navigate than a bloated PDF file.</p>
<h2 id="the-cpu-and-its-address-space">The CPU and its Address Space</h2>
<p>The main CPU is a variant of the 65816, which itself is essentially a 16-bit upgrade of the 6502. The 6502 <em>really</em> got around in the 70&rsquo;s and 80&rsquo;s, so you might be familar with it if you&rsquo;ve done any programming for the Atari 2600/5200, NES, Apple II or Commodore 64. If you&rsquo;ve done 6502 programming then you&rsquo;ll feel right at home, since the operations and addressing modes are largely the same. If you&rsquo;re not familiar with either of them (I wasn&rsquo;t) then don&rsquo;t worry too much, since these processors are fairly easy to understand. To get myself up to speed, I read through large parts of the <a href="https://wiki.nesdev.com/w/images/7/76/Programmanual.pdf">65816 Programming Manual</a>. I found it to be thoroughly interesting and useful, since it starts out by introducing concepts for the 6502 and then works its way up through the new additions for the 65816. So if you already know the 6502 you can skip ahead, but if you don&rsquo;t know either you can get the full package. It&rsquo;s also written in a way meant to be accessible to a newbie programmer from the 80&rsquo;s, so it&rsquo;s pretty good at explaining even the basic concepts that an experienced programmer might take for granted.</p>
<p>In terms of programming 65816 assembly for the SNES, it&rsquo;s pretty straightforward. All of the normal 65816 opcodes work, so general assembly programming guides like the one I linked are almost totally applicable. Probably the most annoying part of working the 65816 is the fact that you can switch its accumulator (A) and index registers (X &amp; Y) between 8-bit and 16-bit modes by setting or clearing two bits in the status register. These not only affect the math operations that you can do on these registers, but they also affect the width of loads an stores performed with that memory. I tended to find myself having to switch to 8-bit mode fairly frequently in order to store a value to an 8-bit variable in memory, or an 8-bit I/O register. The fact that its a persistent state makes things extra confusing and error-prone, since you can&rsquo;t tell which kind of operation is being done just by looking at a piece of assembly code. It also makes it really easy to screw things up by accidentally leaving the processor in the incorrect state expected by a macro or subroutine, leading to subtle bugs. I&rsquo;ve found that some good documentation and conventions can go a long way in avoiding those issues. I feel like having explicit instructions for 8-bit and 16-bit operations would have been much easier from a programmer&rsquo;s point of view, but c&rsquo;est la vie.</p>
<p>To get started with writing your program you just need to know where your WRAM is located in order to store and load from memory, and also where your ROM data is located in the address space. Working with ROM is awesome: you just read from it like normal memory, with nothing special required to access it. This might be a big shock coming from modern game development where you typically need to use OS API&rsquo;s to copy data from disk into RAM before you can actually use. Your code is also stored in ROM and usually you just execute it right from there, which lets you freely intermingle code and content.</p>
<p>To see where things are placed in the address space, you can look at the <a href="http://problemkaputt.de/fullsnes.htm#snesmemorymap">memory map</a>. However the assembler you use can typically help you out here by letting you name different segments, and then specify which segment variables and data should be placed in when you declare them. One funky thing about the address space is that you&rsquo;ll see the &ldquo;system area&rdquo; (which contains the I/O registers and first 8KB of WRAM) mapped in there multiple times. This is called &ldquo;mirroring&rdquo;, and its done to let you access those things without having to constantly switch the data bank register (DBR) or use long addressing mode. The basic gist is that its faster and more flexible for the processor to access things using a 16-byte offset from the 8-bit DBR (forming a full 24-bit address), so ideally you want to work within a single 64 KB bank of memory for as long as you can. The mirroring helps with this, since you can flip through different 32 KB chunks of LoROM and still be able to access the stuff in the system area:</p>
<pre tabindex="0"><code>+------+-------------+-------------------------+
| Bank |   Offset    | Contents                |
+----------------------------------------------+
| 0x00 |   0x0000    | System Area             |
|      |   0x8000    | First 32KB of LoROM     |
+----------------------------------------------+
| 0x01 |   0x0000    | System Area             |
|      |   0x8000    | Second 32K of LoROM     |
+----------------------------------------------+
| 0x02 |   0x0000    | System Area             |
|      |   0x8000    | Third 32K of LoROM      |
+----------------------------------------------+
| 0x03 |   0x0000    | System Area             |
|      |   0x8000    | Fourth 32K of LoROM     |
+----------------------------------------------+
| ...  |    ...      |    ...                  |
+------+-------------+-------------------------+
</code></pre><p>For assembling code and linking together the ROM, I used <a href="https://cc65.github.io/doc/ca65.html">ca65</a>. ca65 is a macro assembler, which means you can define macros that are functionally similar to C/C++ macros. They can also be used to implement unrolled loops, which is handy for cases where you don&rsquo;t want to hand-write a dynamic loop for performance or convenience reasons. I&rsquo;m sure there are other assemblers out there that could do the job, but ca65 seemed to work fine for me. It&rsquo;s actually part of the cc65 package, which is a full C compiler and linker targeting the 6502 and friends. So while I&rsquo;m sure you could somehow use that to write C code that runs on the SNES, I&rsquo;m not sure of the specifics of how that would work in practice. Either way the limited register set and banked memory architecture of the 65816 doesn&rsquo;t seem to lend itself particularly well to a C compiler implementation, so I steered clear of that. Besides, assembly is what the pros used back in the day! :P</p>
<h2 id="the-ppu">The PPU</h2>
<p>The PPU is what you would consider to be the &ldquo;GPU&rdquo; of the console, at least in a very rough sense. Its whole job is to make your sprites and backgrounds show up on the screen, and that&rsquo;s really all it does. If you&rsquo;re coming from the world of modern GPU&rsquo;s (like me), some aspects of it will probably seem very foreign:</p>
<ul>
<li>
<p>There are no &ldquo;command buffers&rdquo; or &ldquo;commands&rdquo; really. The only way you interact with the PPU is by setting its registers via memory-mapped IO (located in the system area), and by changing the contents of VRAM/OAM/CGRAM.</p>
</li>
<li>
<p>You can only interact with the PPU&rsquo;s registers and memory during VBlank, or when the screen is entirely disabled (force blank). This means that you only have about 1.4ms to update your sprite/background state every frame during gameplay.</p>
</li>
<li>
<p>There&rsquo;s no frame buffer! The PPU will draw your sprites and backgrounds on-the-fly as it&rsquo;s generating each scanline. This is why you can only modify PPU state during VBlank, and also why there&rsquo;s restrictions on the number of sprites per scanline.</p>
</li>
</ul>
<p>Since the PPU only has a very limited set of functionality, it&rsquo;s <em>very</em> particular about the data that you feed it. You need to strictly adhere to what the PPU expects, otherwise you&rsquo;re just going to get a bunch of garbage on the screen. This can make it quite difficult to work with, especially when you&rsquo;re just starting out. The <a href="http://problemkaputt.de/fullsnes.htm#snespictureprocessingunitppu">PPU section of the Fullcash specs</a> is going to be your best friend for getting this right, since it describes everything in full detail down to each individual bit.</p>
<p>PPU graphics are divided into two parts: backgrounds, and sprites. We&rsquo;ll talk about backgrounds first.</p>
<h3 id="backgrounds">Backgrounds</h3>
<p>Backgrounds are essentially 2D grids of &ldquo;tiles&rdquo;  that typically cover the entire screen, where each tile is either 8x8 or 16x16 pixels in size. You&rsquo;ll also see these tiles referred to as &ldquo;characters&rdquo; in the official SNES documentation, which I suspect is a vestige of old text-mode display processors found in early PC&rsquo;s. The way it works is that you have a &ldquo;map&rdquo; stored in VRAM, which is a 2D array of integers that specify the index of the tile that you want to use for each slot of the map. The idea is that you can re-use the same tiles (often called a &ldquo;tile set&rdquo;) many times across a given map, which is critical when you consider that 256x256 worth of 4bpp tiles would consume 32 KB of VRAM! The ability to re-use tiles is aided by the fact that you can also flip the tile horizontally or vertically for each map entry, and also specify the palette to use. Careful use of flipping and palette switching could create the impression of a much larger tile set than what you can actually fit in VRAM.</p>
<p>The tiles pointed to by a BG map are either 8x8 or 16x16 in size, and either 4bpp (16 color) or 2bpp (4 color) indexed color. For the 8x8 tile size, things are pretty simple in terms of how the tile index works in the map. Each tile is stored in a swizzled bit-plane format, where a single tile is all contiguous is memory. So you&rsquo;ll have 32 bytes for tile 0, then 32 bytes for tile 1, and so on. So your map just specifies the index of the tile it wants, and that&rsquo;s it. For the 16x16 BG tile size, it&rsquo;s more complicated. A 16x16 tile has to be split up into 4 8x8 sub-tiles, one for each corner. The BG map then specifies the index of the upper-left 8x8 sub-tile. The trick is that these sub-tiles can&rsquo;t be located contiguously in VRAM, which makes VRAM management rather annoying. Instead, the PPU treats the tile memory as if it were a 16x64 2D array of sub-tiles. The 16x16 meta-tile then needs to be stored so that the top two sub-tiles are on the one row of the 16x64 table, while the bottom two sub-tiles must be located on the <em>next</em> row. Effectively this means that there&rsquo;s a gap of 14 sub-tiles between the top and bottom portions of the meta-tile, which works out to be 448 bytes (224 words) . Trust me when I say that this is easy to screw up.</p>
<p>Here&rsquo;s what the placement of a 16x16 tile in VRAM looks like if you were to put its top-left corner in the first tile entry in VRAM:</p>
<p><img src="/images/converted/adventures-in-retro-development-snes-edition/vram_tiles.png" alt="vram_tiles"></p>
<p>At the bottom you can see that the bottom-left sub-tile needs to be placed 14 tiles after the previous sub-tile so that it ends up on the next row of the VRAM tile matrix. Therefore you would want to DMA it to an offset of 0x0100 words from the start of the tile table in VRAM (VRAM generally deals with memory in terms of 2-byte words).</p>
<p>Each background layer, of which there are up to 4 at once depending on how you&rsquo;ve configured the PPU, will typically use a map of 32x32 BG tiles. For an 8x8 tile size this covers 256x256 screen pixels, which is more than enough to cover the entire 256x224 screen resolution. Each background layer has a 10-bit pixel offset that will nudge the whole grid of tiles by specified X and Y values, which allows for smooth scrolling. The map will wrap around in areas exposed by the BG offset, which means if you have a tiled background you can just scroll it forever. In practice most games would constantly be updating the outer edges of the BG map in order to display a level much larger than what&rsquo;s implied by a 32x32 grid of tiles. They would also scroll some of the layers at a slower rate than the &ldquo;primary&rdquo; background layer, which would approximate a parallax effect for things far off in the distance. Another background layer could also be used for a HUD and/or status bar.</p>
<h3 id="sprites">Sprites</h3>
<p>As we just covered, backgrounds are made up tiles that are implicitly laid out in a 2D grid. So you can&rsquo;t just specify an arbitrary screen position for any single tile, or have one tile overlap another tile. Sprites (also called &ldquo;objects&rdquo;) are different: you directly tell the PPU where to draw the tiles by writing X and Y positions into Object Attribute Memory. So instead of a 32x32 map containing just tile indices and a few attributes, you have a 128 element array containing X/Y positions, tile indices, and a few attributes. Basically sparse tiles instead of a grid. Sprites can be 8x8, 16x16, 16x32, 32x32, 32x64, or 64x64 depending on the value of the OBSEL register, with only 2 sizes available for a given frame. The sizes large than 8x8 are set up as meta-tiles in the same way that BG tiles work: subsequent rows in the meta-tile must be located 16 tiles apart in VRAM. So a 32x32 sprite would have its sub-tiles placed in VRAM as 4 rows of 4 8x8 tiles, with a gap of 12 tiles because the end of one meta-tile row and the start of the next meta-tile row. This can make it annoying to move a single sprite into VRAM, since it means that you need to do it as 4 separate DMA operations.</p>
<h2 id="development-environment">Development Environment</h2>
<p>To get a jump start on making a working SNES ROM, I used <a href="https://github.com/Optiroc/libSFX">libSFX</a>, which was developed by <a href="https://twitter.com/Optiroc">David Lindecrantz</a>. This library is a collection of basic tools combined with a bunch of headers for ca65 that are full of macros and defines that correspond to the particulars of the SNES. It definitely saved me a lot of time that I would have otherwise spent going back and forth between my code and the SNES specs to get the right I/O addresses and bit offsets. It also does all of the magic incantations to boot up the system in the expected state (which is something you have to do on a system with no O/S!), which is pretty helpful. On top of all of that it has a makefile template and config system that makes it pretty simple to get your code assembled and linked into the layout expected for an SNES cartridge.</p>
<p>libSFX also includes a tool called SuperFamiconv, which is a command-line program that convert .PNG&rsquo;s into a binary format that can be (almost) directly consumed by the PPU. It will auto-generate a palette, carve up your image into 8x8 tiles, and munge those tiles into the 2bpp or 4bpp bitplane format used in VRAM. For backgrounds it can also attempt to automatically de-duplicate the tiles in the image to generate a tileset, and will spit out a BG map in the required binary format. By linking the outputs right into your ROM, they&rsquo;re immediately available to be DMA&rsquo;d into VRAM at runtime, which is pretty sweet. The tool is also tied into the makefile template for libSFX, essentially forming a basic content processing pipeline. However you still need to be careful about making sure that your sub-tiles end laid out correctly in VRAM, since SuperFamiconv will end up packing all of its data contiguously. One option might be to pre-assemble atlases of tiles/sprites in a PNG in a way that&rsquo;s aware of the weird &ldquo;16 columns of tiles&rdquo; setup and then copy large blocks at level transitions. For backgrounds larger than 32x32 you&rsquo;ll also need to do your own on-the-fly updating of the BG map in VRAM like I mentioned earlier. I would also imagine that for any non-trivial game you would want to use a proper tile-based map editor to assemble your levels, rather than trying to rely on SuperFamiconv to de-duplicate things for you.</p>
<p>One wrinkle with libSFX is that it&rsquo;s really only set up to work on Unix-like systems. Getting something this to work on Windows would traditionally involve Cygwin, which is an experience I normally don&rsquo;t subject myself to in my free time. However for Windows 10 there&rsquo;s now an alternative called the <a href="https://docs.microsoft.com/en-us/windows/wsl/about">Windows Subsystem for Linux (WSL)</a>. This essentially allows you to install a real Linux distro and have it interact with the Windows file system and executables, with no VM&rsquo;s required! I decided that this was as good an opportunity as any to try it out, so I went ahead and installed Ubuntu from the Microsoft Store (wild!) and within minutes I was in a bash shell! From there I just ran sudo apt install gcc/g++/make, and I was ready to clone and build libSFX! I was constantly surprised by how well everything worked, especially for someone like me that has very little Linux experience. What&rsquo;s really cool is that you can invoke Linux from Windows command prompt or batch file by running &ldquo;wsl.exe&rdquo;, effectively allowing you to go in and out Linux from within your scripts. In the end I had one batch file that ran &ldquo;wsl.exe make&rdquo; to build the ROM, then another batch file that would launch snes9x with that ROM.</p>
<p>In the CPU section I mentioned that the 65816 can be switched between 8 and 16-bit register sizes, and that it can be a source of much pain. libSFX has a series of &ldquo;RW&rdquo; macros that to alleviate some of that pain by tracking the current register state in preprocessor variables. This lets it try to filter out redundant size changes, and also lets you print out whichever state is currently set (or more specifically, the state that the preprocessor <em>thinks</em> is set). Unfortunately being a preprocessor that&rsquo;s part of an assembler, it can only track state as it assembles your code in top-down fashion. This means that the internal state can and will be wrong any time a branch or jump occurs, and its up to you to give it enough information to keep it from screwing up. In particular you need to strategically use RW_assume in order to inform the assembler of the expected state, and possible also deploy RW_forced as your nuclear option. In practice it kind of feels like this tracking system is more trouble than its worth, but I think I would need to use it longer to form a better opinion. Either way, just make sure that you take some time to familiarize yourself with it before diving in! Many of the helpful macros provided by libSFX will use the RW macros internally, so you&rsquo;ll need to at least play nice with it at those points in your code. Also, be aware that libSFX will set the processor to a8i16 mode before handing control off to your Main label, and also when calling your VBlank handler.</p>
<h2 id="debugging">Debugging</h2>
<p>While it&rsquo;s a bit of a bummer that you can&rsquo;t really run your game on real SNES hardware without jumping through some serious hoops, the upside is that emulation allows for some really cool debugging functionality. You can do a lot when the entire state of the machine is visible to you! In particular, I would recommend using <a href="https://problemkaputt.de/sns.htm">no$sns</a>, by Nocash. It allows you to&hellip;</p>
<ul>
<li>View the disassembly of the running code</li>
<li>Set breakpoints</li>
<li>Inspect CPU registers and memory</li>
<li>Inspect I/O register states for the PPU/DMA/etc.</li>
<li>Visualize tiles in VRAM</li>
<li>Visualize the BG map</li>
<li>Visualize sprites in OAM</li>
<li>Visualize palettes in CGRAM</li>
<li>Probably more things that I didn&rsquo;t get around to using!</li>
</ul>
<p>The tile visualization in particular is really helpful for making sure that you get the layout right. Just make sure that you switch the disassembly mode to the Native 65XX mode, otherwise it will look very odd!</p>
<h2 id="gotchas-and-insights">Gotchas and Insights</h2>
<p>There were definitely a few things that caused me to lose a bunch of time before I could figure out what&rsquo;s wrong. I also noticed a few ways to speed up my programming. Here they are, in no particular order:</p>
<ul>
<li>
<p>I mostly covered this already, but always keep track of your register sizes! This was perhaps my most common source of bugs, at least when I was first starting.</p>
</li>
<li>
<p>This was also covered earlier, but make sure that your sub-tiles are in the right location in VRAM!</p>
</li>
<li>
<p>Be careful not to omit the &ldquo;#&rdquo; when you want to load an immediate. There&rsquo;s no type-safety to help you if you forget it, in which case you&rsquo;ll load from that address instead.</p>
</li>
<li>
<p>.proc is helpful for scoping constants and labels for a subroutine, ensuring that they don&rsquo;t clash with constants/labels from your other suboutines. There&rsquo;s also &ldquo;proc&rdquo; from libSFX, which does the same thing but also tries to ensure that the RW state is initialized appropriately. Just be aware that you may need to prefix global defines with &ldquo;::&rdquo; when you&rsquo;re inside of a .proc in order to let ca65 know that you&rsquo;re referring to something in the global scope.</p>
</li>
<li>
<p>Use the direct page! It&rsquo;s easy to feel cramped with only 1 true register to work with, but working with values in the direct page is fast because the operands are smaller. So you generally want to use it for storing temporary variables that you&rsquo;re dealing with in a subroutine. Just make sure that you prefix the address with &ldquo;z:&rdquo; to make sure that ca65 emits the zero-page version of the instruction. libSFX also sets up a &ldquo;ZEROPAGE&rdquo; segment for you in the default config, which you can use for declaring variables that live in the direct page.</p>
</li>
<li>
<p>Memory isn&rsquo;t slow relative to the CPU! I had to keep reminding myself of this, since I&rsquo;m so used to modern systems where a cache miss might mean thousands of cycles in latency.</p>
</li>
<li>
<p>Make sure that you always clear the carry before an adc, and set the carry before a sbc! libSFX defines &ldquo;add&rdquo; and &ldquo;sub&rdquo; macros that will do this for you, so feel free to use them.</p>
</li>
<li>
<p>Enable the debug variable in your makefile you have the linker output a map file that you can use to see the actual addresses of your variables and labels.</p>
</li>
</ul>
<h2 id="things-i-didnt-cover">Things I Didn&rsquo;t Cover</h2>
<ul>
<li>
<p>Mode 7 - the infamous Mode 7 is very different from the &ldquo;normal&rdquo; background modes, and has plenty of its own weird quirks. Be sure to carefully read through Nocash specs if you&rsquo;re interested in using it.</p>
</li>
<li>
<p>Audio - I haven&rsquo;t spent any time learning how to program the audio processor, so I would be of no help in that area. I <em>did</em> manage to get some music playing, however. An awesome co-worker made a quick looping track in <a href="http://schismtracker.org/">Schism Tracker</a>, and I was able to convert it into an SPC file using <a href="http://snes.mukunda.com/">SNESMOD</a>. I was then able to link that into my ROM, and start up the SPC program to play that music!</p>
</li>
</ul>
<h2 id="links">Links</h2>
<ul>
<li><a href="http://problemkaputt.de/fullsnes.htm">fullsnes - nocash SNES hardware specification</a></li>
<li><a href="https://wiki.superfamicom.org/">Super Famicom Development Wiki</a></li>
<li><a href="https://en.wikibooks.org/wiki/Super_NES_Programming">Super NES Programming WikiBook</a></li>
<li><a href="http://forums.nesdev.com/viewforum.php?f=12">SNES Development Forum on nesdev.com</a></li>
<li><a href="http://web.archive.org/web/20071111200111/http://www.geocities.com/Qwertie256/attic/snesemu/qsnesdoc.html">SNES Graphics Information</a></li>
<li><a href="https://wiki.nesdev.com/w/images/7/76/Programmanual.pdf">65816 Assembly Programming Guide</a></li>
<li><a href="https://github.com/Optiroc/libSFX">libSFX GitHub Repository</a></li>
<li><a href="https://cc65.github.io/doc/ca65.html">ca65 User&rsquo;s Guide</a></li>
<li><a href="https://cc65.github.io/doc/ld65.html">ld65 User&rsquo;s Guide</a></li>
<li><a href="http://snes.mukunda.com/">SNESMOD</a></li>
<li><a href="http://schismtracker.org/">Schism Tracker</a></li>
<li><a href="https://github.com/nathancassano/snesgss">SNES Game Sound System</a></li>
</ul>
<p>Good luck, fellow retro developers!</p>
<hr>
<h2 id="comments">Comments:</h2>
<h4 id="heading"></h4>
<p><a href="http://pharr.org/matt" title="">matt@pharr.org</a> - <time datetime="2019-02-07 15:17:29">Feb 4, 2019</time></p>
<p>Fun writeup! What did you end up getting running? Any screenshots?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2019-02-09 14:19:14">Feb 6, 2019</time>
<p>Hey Matt! I basically had a very simple side-scrolling shooter working, where you could shoot a few infinitely-spawning enemies. It featured the main character &ldquo;Jack&rdquo; from our Lone Echo game series (I called my game &ldquo;Super Lone Echo, naturally), so I had been meaning to clear it with my company&rsquo;s owner before posting images or video. I guess I should actually do that at some point! :P</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Breaking Down Barriers - Part 6: Experimenting With Overlap and Preemption</title>
			<link>https://therealmjp.github.io/posts/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/</link>
			<pubDate>Mon, 10 Dec 2018 02:01:27 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/</guid>
			<description>This is Part 6 of a series about GPU synchronization and preemption. You can find the other articles here:
Part 1 - What&amp;rsquo;s a Barrier?
Part 2 - Synchronizing GPU Threads
Part 3 - Multiple Command Processors
Part 4 - GPU Preemption
Part 5 - Back To The Real World
Part 6 - Experimenting With Overlap and Preemption
In the previous art_icl_es we took a look at how barriers typically work on GPUs, and then we examined how multiple hardware queues can help with preemption and overall throughput.</description>
			<content:encoded><![CDATA[<p><em>This is Part 6 of a series about GPU synchronization and preemption. You can find the other articles here:</em></p>
<p><a href="../breaking-down-barriers-part-1-whats-a-barrier/">Part 1 - What&rsquo;s a Barrier?</a><br>
<a href="../breaking-down-barriers-part-2-synchronizing-gpu-threads/">Part 2 - Synchronizing GPU Threads</a><br>
<a href="../breaking-down-barriers-part-3-multiple-command-processors/">Part 3 - Multiple Command Processors</a><br>
<a href="../breaking-down-barriers-part-4-gpu-preemption/">Part 4 - GPU Preemption</a><br>
<a href="../breaking-down-barriers-part-5-back-to-the-real-world/">Part 5 - Back To The Real World</a><br>
<a href="../breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/">Part 6 - Experimenting With Overlap and Preemption</a><br></p>
<p>In the previous art_icl_es we took a look at how barriers typically work on GPUs, and then we examined how multiple hardware queues can help with preemption and overall throughput. We then spent some time looking at the support for preemption and multiple queues in actual desktop video cards, and also exploring how they end up getting exposed via DX12 and the Windows 10 driver stack. To help tie everything together, I&rsquo;m now going to show some results that I gathered from running a <a href="https://github.com/TheRealMJP/OverlappedExecution">test DX12 application</a> on a GPU from each of the major desktop IHV&rsquo;s. Before I begin, I&rsquo;d like to urge anyone reading this to take the results with a grain of salt. My test application gathers its timings in a rather unorthodox manner (which I&rsquo;ll explain shortly), and so I can&rsquo;t guarantee their accuracy with 100% certainty. It&rsquo;s also very possible that some of the results here could change with future hardware or driver revisions, so please consider the full context when examining the data.</p>
<h2 id="the-test-app">The Test App</h2>
<p>Before I wrote these articles, I decided that it would be fun and educational to examine how different GPU&rsquo;s respond to the explicit synchronization and multi-queue functionality offered by DX12. Unfortunately, I found it was rather difficult to do this in practice. The IHV&rsquo;s have some vendor-specific tools for gathering timing data from an offline capture, but they&rsquo;re not really set up to easily show you the degree to which different workloads were overlapping on the GPU. <a href="https://blogs.msdn.microsoft.com/pix/2017/01/17/introducing-pix-on-windows-beta/">PIX for Windows</a> has the ability to show overlapping draws or dispatches in its timeline view, but at the time it also wasn&rsquo;t available yet. It also doesn&rsquo;t currently show overlapping work from compute queues, and it can only show you timing during an offline analysis process. (AMD now has a really awesome <a href="https://gpuopen.com/gaming-product/radeon-gpu-profiler-rgp/">GPU profiler</a> that only shows you overlapping work on the GPU, but also shows you what the GPU is doing in response to barriers! Unfortunately I wrote the test app and blog post back in January of 2017, and RGP didn&rsquo;t come out until July of that year).</p>
<p>After getting frustrated at my available options, I decided to try my hand at building my own timing system. D3D12 does have a built-in mechanism for tracking GPU execution timings via <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn903946(v=vs.85).aspx">timestamp queries</a>, which are useful for getting very coarse-grained timing data (for instance, overall GPU frame time, or the time required to complete all opaque draw calls). However they&rsquo;re pretty useless when it comes to determining thread-level parallelism on the GPU. This is because it&rsquo;s not specified how timestamps behave with regards to barriers and synchronization. The documentation says nothing about whether a timestamp should wait for all previous draws/dispatches to complete, or whether they should be affected by transition barriers. This means you wrap a draw call with timestamps, you don&rsquo;t know if the second timestamp will record as soon as the command processor kicks of the draw or if it will record after the draw call is finished. In practice I&rsquo;ve seen varying behavior from different vendors: in some cases a timestamp would cause a sync point, in others it wouldn&rsquo;t. This all lead me to my &ldquo;screw it, I&rsquo;ll build my own timers&rdquo; moment.</p>
<p>Fortunately, making my own shader timer wasn&rsquo;t too difficult&hellip;or at least for my use case, which was a toy application where I was willing to bend the rules a bit. Really all I needed was for a shader to write to some bit of CPU-accessible memory at the start and end of the program, and I could have the CPU monitor that memory and record the timestamp. To do this I needed to use a custom heap that had the exact properties that I wanted:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">D3D12_HEAP_DESC</span> <span class="n">heapDesc</span> <span class="o">=</span> <span class="p">{</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">heapDesc</span><span class="p">.</span><span class="n">SizeInBytes</span> <span class="o">=</span> <span class="mi">128</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">heapDesc</span><span class="p">.</span><span class="n">Flags</span> <span class="o">=</span> <span class="n">D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">heapDesc</span><span class="p">.</span><span class="n">Alignment</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">heapDesc</span><span class="p">.</span><span class="n">Properties</span><span class="p">.</span><span class="n">CPUPageProperty</span> <span class="o">=</span> <span class="n">D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">heapDesc</span><span class="p">.</span><span class="n">Properties</span><span class="p">.</span><span class="n">CreationNodeMask</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">heapDesc</span><span class="p">.</span><span class="n">Properties</span><span class="p">.</span><span class="n">MemoryPoolPreference</span> <span class="o">=</span> <span class="n">D3D12_MEMORY_POOL_L0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">heapDesc</span><span class="p">.</span><span class="n">Properties</span><span class="p">.</span><span class="n">Type</span> <span class="o">=</span> <span class="n">D3D12_HEAP_TYPE_CUSTOM</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">heapDesc</span><span class="p">.</span><span class="n">Properties</span><span class="p">.</span><span class="n">VisibleNodeMask</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">DXCall</span><span class="p">(</span><span class="n">DX12</span><span class="o">::</span><span class="n">Device</span><span class="o">-&gt;</span><span class="n">CreateHeap</span><span class="p">(</span><span class="o">&amp;</span><span class="n">heapDesc</span><span class="p">,</span> <span class="n">IID_PPV_ARGS</span><span class="p">(</span><span class="o">&amp;</span><span class="n">workload</span><span class="p">.</span><span class="n">ReadbackHeap</span><span class="p">)));</span>
</span></span></code></pre></div><p>The important bits here are  the CPUPageProperty, and the MemoryPoolPreference. Setting the page property to WRITE_COMBINE ensures that the memory is uncached from the CPU point of view, which is important since the GPU will be writing to that memory and not a CPU core. Using MEMORY_POOL_L0 also ensures that the heap will live in system memory (as opposed to device memory on the video card), which means that the CPU can access it. It also makes it more likely that the heap will be uncached from the point of view of the GPU. If the GPU writes to an internal cache instead of directly to memory, it&rsquo;s likely that the CPU wouldn&rsquo;t see the write until the cache line was evicted. In the end this hopefully gives us coherent, but slow communication between the GPU and CPU.</p>
<p>The other key component of making this work was figuring out a way have the first thread write its value to the &ldquo;start&rdquo; buffer, and having the last thread write its value to the &ldquo;end&rdquo; buffer. The API doesn&rsquo;t make any guarantees about the order in which threads are executed for a dispatch or draw call, so I didn&rsquo;t want to rely on SV_DispatchThreadID or SV_Position to determine the first or last invocation. Instead, I used atomics on a global counter buffer to figure out an ordered index for each thread:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">uint</span> <span class="n">prevValue</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">CounterBuffer</span><span class="p">.</span><span class="n">InterlockedAdd</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">prevValue</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">prevValue</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">ShaderStartBuffer</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">CBuffer</span><span class="p">.</span><span class="n">FrameNum</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Do actual shader workload
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">prevValue</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">==</span> <span class="n">CBuffer</span><span class="p">.</span><span class="n">NumWorkloadElements</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">ShaderEndBuffer</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">CBuffer</span><span class="p">.</span><span class="n">FrameNum</span><span class="p">;</span>
</span></span></code></pre></div><p>I wrote the frame number in the start/end buffers so that they would be monotonically increasing counters, thus avoiding the need to clear them frame.</p>
<p>The general process for timing a single frame&rsquo;s workload goes like this:</p>
<ul>
<li>Issue a command to clear the CounterBuffer to 0 using ClearUnorderedAccessViewUint</li>
<li>Issue draw/dispatch command</li>
<li>Draw UI to the back buffer</li>
<li>Tell the direct queue to wait on a fence that we&rsquo;ll use to tell the GPU to wait for our signal before starting the GPU work from our commands</li>
<li>Submit command list on the direct queue</li>
<li>Present</li>
<li>(Next frame)</li>
<li>Wait for the last present to finish using a swap chain waitable object (this lines up the CPU thread with the end of VSYNC)</li>
<li>Signal the fence that lets the GPU start the work that was submitted last frame</li>
<li>Loop and check the value of the start buffer, and record the time when it changes</li>
<li>Loop and check the value of the end buffer, and record the time when it changes</li>
<li>Issue commands for the next frame</li>
</ul>
<p>The good news is that it worked! After implementing and testing, the resulting timing values seemed plausible and also roughly matched the values measured by timestamp queries (I also checked against PIX later on once it was available, and it confirmed my results). From there, I added a simple framework for adding a series of dispatch and draw calls on both the DIRECT and COMPUTE queues. I also added some settings that allow the user to enable/disable workloads, configure the amount of threads launched per workload as well as the amount of per-thread work performed, and also specify dependencies that end up getting expressed as transition barriers. Here&rsquo;s what the UI ends up looking like:</p>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/testapp_overview1.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/testapp_overview1.png" alt="testapp_overview"></a></p>
<p>Under the per-workload settings, the &ldquo;Num Groups&rdquo; slider controls how many threads are launched per workload, in increments of 1024. For compute workloads this just controls the dispatch size, while for graphics workloads it adjusts the viewport size to indirectly specify the number of pixel shader invocations. The &ldquo;Num Iterations&rdquo; slider specifies the inner loop count inside of the shader program. The loop body simulates an actual workload by computing some perlin noise using an input and output buffer.</p>
<p>Over by the application settings window, there&rsquo;s a few global settings available. VSYNC can be enabled or disabled, and a slider lets you zoom in on the timeline. The &ldquo;Use Split Barriers&rdquo; setting will cause the app to use split transition barriers instead of standard transition barriers to resolve dependencies. The &ldquo;begin&rdquo; barrier is issued immediately after issuing the draw/dispatch that&rsquo;s being depended on, and the &ldquo;end&rdquo; barrier is issued just before the draw/dispatch that has the dependency (if split barriers are disabled, the single transition barrier is issued immediately before the draw/dispatch with the dependency). There&rsquo;s also a setting to toggle a <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn903835(v=vs.85).aspx">stable power state</a> to get consistent GPU clocks, as well as a setting the causes compute queue workloads to be submitted on a queue that was created with <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn986723(v=vs.85).aspx">D3D12_COMMAND_QUEUE_PRIORITY_HIGH</a>.</p>
<h2 id="results">Results</h2>
<p>I tested my app on the 4 GPU&rsquo;s that I have available on my home PC: and Nvidia Titan V (Volta), an Nvidia GTX 1070 (Pascal), an AMD RX 460 (Polaris, GCN Gen 4), and the Intel HD Graphics 530 that&rsquo;s integrated into my Core i7 6700K CPU (Gen9, Skylake). I also tested on a GTX 980 in my work PC. For each GPU I adjusted the number of threads launched per workgroup to attempt to account for differences in the number of execution units on each GPU. All Nvidia GPU&rsquo;s used 8k threads per workgroup, the RX 460 used 4k, and the Intel GPU used 2k. These are the scenarios that I used for testing:</p>
<ul>
<li><strong>Default</strong> - default settings for the app. All workloads enabled, with dependencies between sequential workloads on the same queue. This shows how well DIRECT/COMPUTE submissions can overlap with separate dependency chains.</li>
<li><strong>No Barriers</strong> - all barriers are disabled, which shows how well workloads from the same queue can overlap</li>
<li><strong>Three Dispatches</strong> - three dispatches are submitted on the DIRECT queue, with the third dispatch depending on the first dispatch. The second dispatch is longer than the first dispatch, which shows sync granularity from barriers.</li>
<li><strong>Three Dispatches, Split Barrier</strong> - same as the above, except a split barrier is used. This can show whether the driver/GPU can use take advantage of split barriers to increase workload overlap for this particular case.</li>
<li><strong>High-Priority Compute</strong> - same as Default, except that the compute queue workloads are submitted on a high-priority COMPUTE queue.</li>
</ul>
<h3 id="default">Default</h3>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx980_default.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx980_default_resized_1024.png" alt="gtx980_default"></a></p>
<center><i>Nvidia GTX 980</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx1070_0000_default.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx1070_0000_default.png" alt="gtx1070_0000_default"></a></p>
<center><i>Nvidia GTX 1070</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/titanv_default.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/titanv_default.png" alt="titanv_default"></a></p>
<center><i>Nvidia Titan V</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/hd530_0000_default.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/hd530_0000_default.png" alt="hd530_0000_default"></a></p>
<center><i>Intel HD 530</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/rx460_0000_default.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/rx460_0000_default.png" alt="rx460_0000_default"></a></p>
<center><i>AMD RX 460</i></center>
<p>In all cases here we see what we would expect from having barriers between each workload: none of the individuals draws/dispatches overlap with other draws/dispatches on the same queue. However we get very different behavior in terms of how each GPU handles simultaneous submissions on both the COMPUTE and DIRECT queue. The 980 appears to serialize the work from the two queues, completely executing the COMPUTE submission before the DIRECT submission. Meanwhile the 1070 has some overlap, but with curiously large gaps between the dispatches on the COMPUTE queue. We also see that workloads with overlap end up taking longer, suggesting that overlapping dispatches or draws are competing for shared resources. The Titan V shows similar results to the 1070, albeit with some smaller gaps. Intel&rsquo;s GPU also serializes the two submissions, which we would expect based on the lack of a COMPUTE queue being exposed in GPUView. Finally we have the RX 460, which seems to be capable of completely overlapping both submissions with no weird gaps. This is definitely what we expect based on AMD&rsquo;s marketing material and developer advice regarding &ldquo;async compute&rdquo;.</p>
<p>One thing to note from these results is that the Nvidia and AMD GPU&rsquo;s all have a noticeable gap following the graphics workload, which is performing a draw to a render target. This suggests that render target barrier results in more post-shader work than a dispatch, perhaps due to ROP and cache activity.</p>
<h3 id="no-barriers">No Barriers</h3>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx980_nobarriers.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx980_nobarriers.png" alt="gtx980_nobarriers"></a></p>
<center><i>Nvidia GTX 980</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx1070_0001_nobarriers.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx1070_0001_nobarriers.png" alt="gtx1070_0001_nobarriers"></a></p>
<center><i>Nvidia GTX 1070</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/titanv_nobarriers.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/titanv_nobarriers.png" alt="titanv_nobarriers"></a></p>
<center><i>Nvidia Titan V</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/hd530_0001_nobarriers.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/hd530_0001_nobarriers.png" alt="hd530_0001_nobarriers"></a></p>
<center><i>Intel HD 530</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/rx460_0001_nobarriers.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/rx460_0001_nobarriers.png" alt="rx460_0001_nobarriers"></a></p>
<center><i>AMD RX 460</i></center>
<p>The 980 and 1070 GPU&rsquo;s show similar behavior in this case: the first 3 dispatches on each queue overlap with each other very well, but the dispatches on the GRAPHICS queue refuse to overlap with the draw call in the graphics workload. This is unfortunate, because it suggests a cost for switching between compute and graphics operations. I tried many situations on both GPU&rsquo;s, and ultimately I was never able to get a dispatch and a draw to overlap on the GRAPHICS queue. Multiple draws overlap each other just fine (as you would expect), but there seems to always be an implicit sync point when switching from compute to graphics (or vice versa). The Titan V manages better overlap for the 3 first 3 dispatches on the compute and graphics queues, but still has the same issue where the draw won&rsquo;t overlap with any dispatches. Intel shows similar behavior in this test, with the dispatches overlapping some but no overlap between draws and dispatches. Meanwhile, AMD maintains its title as the &ldquo;King of Overlapping&rdquo; by showing no visible sync points whatsoever. Also of note is that the total execution time for AMD went from ~2.5ms to ~1.5ms by removing all of the barriers!</p>
<h3 id="three-dispatches">Three Dispatches</h3>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx980_threedispatches.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx980_threedispatches.png" alt="gtx980_threedispatches"></a></p>
<center><i>Nvidia GTX 980</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx1070_0002_threedispatches.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx1070_0002_threedispatches.png" alt="gtx1070_0002_threedispatches"></a></p>
<center><i>Nvidia GTX 1070</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/titanv_threedispatches.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/titanv_threedispatches.png" alt="titanv_threedispatches"></a></p>
<center><i>Nvidia Titan V</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/hd530_0002_threedispatches.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/hd530_0002_threedispatches.png" alt="hd530_0002_threedispatches"></a></p>
<center><i>Intel HD 530</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/rx460_0002_threedispatches.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/rx460_0002_threedispatches.png" alt="rx460_0002_threedispatches"></a></p>
<center><i>AMD RX 460</i></center>
<p>This time, the test results are very similar for all of our GPU&rsquo;s: inserting a transition barrier on the result of dispatch A right before dispatch C causes C to wait for dispatch B to complete execution. This suggests that write-&gt;read barrier is implemented using a full pipeline flush on all 3 vendors.</p>
<h3 id="three-dispatches-split-barrier">Three Dispatches, Split Barrier</h3>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx980_threedispatchessplit.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx980_threedispatchessplit.png" alt="gtx980_threedispatchessplit"></a></p>
<center><i>Nvidia GTX 980</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx1070_0003_threedispatchessplit.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx1070_0003_threedispatchessplit.png" alt="gtx1070_0003_threedispatchessplit"></a></p>
<center><i>Nvidia GTX 1070</i></center>
<p><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/titanv_threedispatchessplit.png" alt="titanv_threedispatchessplit"></p>
<center><i>Nvidia Titan V</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/hd530_0003_threedispatchessplit.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/hd530_0003_threedispatchessplit.png" alt="hd530_0003_threedispatchessplit"></a></p>
<center><i>Intel HD 530</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/rx460_0003_threedispatchessplit.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/rx460_0003_threedispatchessplit.png" alt="rx460_0003_threedispatchessplit"></a></p>
<center><i>AMD RX 460</i></center>
<p>This time we have a split barrier surrounding the second dispatch. So we dispatch workload A, issue the start of a transition barrier, dispatch workload B, then end the transition barrier. Ideally this would allow A to overlap with B and B to overlap with C, but with no overlap between A and C. Unfortunately this doesn&rsquo;t seem to happen on any of the hardware that I tested. On Nvidia and Intel the split barrier doesn&rsquo;t seem to make any difference at all, giving the same behavior as issuing a non-split barrier following dispatch B. AMD is the only one that shows any change, but it looks like they just put a full sync point where the start of the transition barrier is issued (right after dispatch A). UPDATE 12/9/2018: as of driver version 18.12.1.1, my 460 now shows the exact same behavior for split barriers as it does for a standard transition barrier. So it looks like now the driver syncs on the &ldquo;end&rdquo; of the split barrier instead of at the &ldquo;begin&rdquo;.</p>
<h3 id="high-priority-compute">High-Priority Compute</h3>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx980_hiprioritycompute.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx980_hiprioritycompute.png" alt="gtx980_hiprioritycompute"></a></p>
<center><i>Nvidia GTX 980</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx1070_0004_hiprioritycompute.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/gtx1070_0004_hiprioritycompute.png" alt="gtx1070_0004_hiprioritycompute"></a></p>
<center><i>Nvidia GTX 1070</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/titanv_hiprioritycompute.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/titanv_hiprioritycompute.png" alt="titanv_hiprioritycompute"></a></p>
<center><i>Nvidia Titan V</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/hd530_0004_hiprioritycompute.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/hd530_0004_hiprioritycompute.png" alt="hd530_0004_hiprioritycompute"></a></p>
<center><i>Intel HD 530</i></center>
<p><a href="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/rx460_0004_hiprioritycompute.png"><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/rx460_0004_hiprioritycompute.png" alt="rx460_0004_hiprioritycompute"></a></p>
<center><i>AMD RX 460</i></center>
<p>Using the PRIORITY_HIGH flag on the compute queue doesn&rsquo;t seem to make much of a difference on AMD and Nvidia. On Intel however it causes the COMPUTE submission to get executed before the DIRECT submissions, indicating that it affects the order in which submissions get serialized. This makes sense given what we know about this flag, which only influences the OS scheduler in cases where the OS is serializing submissions from multiple software queues so that they can be consumed by a single hardware queue.</p>
<p>I put the test app on <a href="https://github.com/TheRealMJP/OverlappedExecution">GitHub</a>, so feel free to play around it or fork for your own experiments!</p>
<p>UPDATE 12/15/2018: I just ran the overlap tests on my Nvidia RTX 2080, and the results are very similar to what I was getting from the GTX 1070 and Titan V.</p>
<h2 id="preemption-analysis">Preemption Analysis</h2>
<p>To wrap things up, I also wanted to include a quick analysis of preemption support for the GPU&rsquo;s I have available to me. Analyzing preemption is naturally tricky: it mostly comes into play when multiple applications are using the GPU simultaneously, and the exact behavior can vary depending on what those applications are doing with the GPU, and also how &ldquo;important&rdquo; they are in the eyes of the OS. These issues lead me to set up a set of very simple experiments where I could observe the preemption behavior using both tools as well as my own eyes. The basic setup worked like this:</p>
<ul>
<li>
<p>First, I would start up a modified version of <a href="https://github.com/TheRealMJP/DeferredTexturing">DX12 Deferred Texturing demo</a>, and disable VSYNC so that it would run un-throttled. This would saturate the GPU with a constant load, with no gaps between submissions. This gives us a chance to observe whether DWM (the desktop composition engine in Windows) can preempt in order to re-composite and present to the display.</p>
</li>
<li>
<p>Next, I would put on my Oculus Rift headset, which would cause the &ldquo;Oculus Home&rdquo; app to start submitting 3D rendering commands to the GPU (it runs on Unreal Engine). These submissions would compete with the deferred texturing demo for GPU time, showing how the OS and GPU handle scheduling in the case of two applications performing rendering commands.</p>
</li>
<li>
<p>Putting on the headset means that the Oculus compositor service starts up, which is what actually presents frames to the headset. This service can make high-priority submissions to the GPU, which is intended to allow the compositor to continue presenting frames at 90 Hz even if the source VR application is failing to do so (this is very important for maintaining smooth updates from head movements). Thus we can also observe how the GPU handles preemption for a very specialized, high-priority scenario.</p>
</li>
</ul>
<p>First, we&rsquo;re going to look at how things worked on my primary GPU at the time I ran these experiments, which was an Nvidia GTX 1070. Here&rsquo;s what the GPUView capture looked like when running Oculus Home at the same time as my unmodified, un-throttled deferred texturing app:</p>
<p><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/nv_normal.png" alt="NV_Normal"></p>
<center><i>GTX 1070 - Baseline Scenario</i></center>
<p>This is what I call the &ldquo;baseline&rdquo; scenario, where I haven&rsquo;t done anything to really screw with preemption. We can see here that the submissions from Oculus Home and BindlessDeferred.exe actually end up on separate hardware queues, with one called &ldquo;3D&rdquo; and one called &ldquo;Graphics_1&rdquo;. The deferred texturing app is cruising along, generally taking anywhere from 5 to 10 milliseconds to complete a frame. Meanwhile Oculus Home is is taking about 12 milliseconds to complete, which is causing the Oculus service to drop into what they call <a href="https://developer.oculus.com/blog/asynchronous-spacewarp/">Asynchronous Spacewarp</a> (or ASW for short). When ASW kicks in the VR app is throttled to 45 Hz, but the Oculus compositor continues to present frames at 90Hz using interpolation techniques to generate the &ldquo;in between&rdquo; frames. In other words my GPU doesn&rsquo;t have enough resources to keep both apps running at 90Hz, but the Oculus compositor is successfully using preemption to ensure that the headset is staying responsive to user input. In this particular case it looks like the Oculus service is submitting its commands to a compute queue, labeled &ldquo;Compute_0&rdquo;.</p>
<p>So how about DWM? If we expand that image a bit, we can see the CPU side of DWM&rsquo;s submissions down below:</p>
<p><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/nv_normal_dwm.png" alt="NV_Normal_DWM"></p>
<center><i>GTX 1070 - Baseline Scenario (with DWM)</i></center>
<p>So we can see DWM submitting a command buffer, which ends up sitting enqueued for quite some time before it ends up actually executing on the 3D hardware queue. It looks like DWM commands end up getting submitting on a normal graphics/DIRECT queue as opposed to a compute queue, and it also looks like that process is getting bullied out of using the GPU as often as it would like. I was running this on a 144 Hz monitor, and here we see DWM flipping at a rate of around 72 Hz.</p>
<p>For my next scenario I wanted to really turn up the heat to stress the system and see how everything responded. To do this, I modified the deferred texturing demo to over-submit its draw calls by a constant factor. This caused the GPU frame time to increase to about 100 milliseconds, making preemption crucial for maintaining anything resembling responsiveness. Here&rsquo;s the GPUView capture:</p>
<p><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/nv_manydraws.png" alt="NV_ManyDraws"></p>
<center><i>GTX 1070 - Many Draws Scenario</i></center>
<p>Despite our best efforts at hogging the GPU, preemption and multiple queues seem to be working they way you&rsquo;d expect in this scenario. The Oculus Home application is still failing to run at 90Hz due to sharing the GPU, but the Oculus compositor is ticking regularly every 11ms or so. This is all good news, but let&rsquo;s see what happens if we try a different approach:</p>
<p><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/nv_bigdispatch.png" alt="NV_BigDispatch"></p>
<center><i>GTX 1070 - Big Dispatch Scenario</i></center>
<p>This time, I modified BindlessDeferred to launch way more threads than it actually needed to when executing the deferred shading dispatch. This caused the frame time to balloon to about 100ms, and from the capture we can see that it took the Oculus Home app down with it. Even worse, the Oculus compositor app also seems to be unable to get the GPU to complete its small amount of work. The compositor submissions seem to be scheduled on the GPU&rsquo;s compute queue, however it sits there for nearly 100ms before completing. This suggests that our &ldquo;Big Dispatch&rdquo; is hogging the entire GPU, and the other submissions are unable to preempt this dispatch while its threads are executing. We see similar results in my final test scenario, which uses a normal-sized dispatch but artificially increases the execution time of a single thread by looping in the shader program:</p>
<p><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/nv_slowshader.png" alt="NV_SlowShader"></p>
<center><i>GTX 1070 - Slow Shader Scenario</i></center>
<p>Once again everyone suffers in this scenario, and I can tell you first-hand that you do <em>not</em> want to put on a VR headset under these kinds of conditions.</p>
<p>To finish things off, let&rsquo;s take a look at how the same scenarios played out on my AMD RX 460. Here&rsquo;s the baseline capture:</p>
<p><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/amd_normal.png" alt="AMD_Normal"></p>
<center><i>AMD RX 460 - Baseline Scenario</i></center>
<p>The AMD captures are a little harder to parse, since the D3D11 and D3D12 submissions end up on the same hardware queue (named &ldquo;3D&rdquo; here). This also suggests that this hardware may be worse at allowing Oculus Home and BindlessDeferred to share the GPU, since they&rsquo;re pretty much taking turns on the graphics queue instead of executing concurrently. However, we do see something rather interesting here if we look at the submissions from the Oculus compositor. The compositor work is executing on a hardware queue named &ldquo;High Priority Compute&rdquo;, which suggests that it&rsquo;s perhaps making use of the &ldquo;Quick Response Queue&rdquo; functionality mentioned in AMD&rsquo;s marketing material. Let&rsquo;s see how things shake out in our &ldquo;Many Draws&rdquo; scenario:</p>
<p><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/amd_manydraws.png" alt="AMD_ManyDraws"></p>
<center><i>AMD RX 460 - Many Draws Scenario</i></center>
<p>As we feared, the AMD GPU holds up worse (compared to the Nvidia GPU) when it comes to letting the Oculus Home app (darker purple) share the GPU. BindlessDeferred (lighter purple) is taking over 100ms per-frame to complete, and it appears that Home needs to wait for the (single) command buffer submission to completely finish before it can start executing. The Oculus compositor, on the other hand, is executing <em>very</em> regularly. The compute submission comes in every 11ms, and consistently takes about 1.8ms to finish. In other words: Home is nowhere near making 90Hz, but the compositor is making sure that a new time-warped frame comes in every 11.1ms. This allowed things to remain comfortable when I put on the headset&hellip;provided I didn&rsquo;t rotate my head to the sides very quickly. Next, let&rsquo;s look at the &ldquo;Big Dispatch&rdquo; scenario:</p>
<p><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/amd_bigdispatch.png" alt="AMD_BigDispatch"></p>
<center><i>AMD RX 460 - Big Dispatch Scenario</i></center>
<p>As expected, the frame rate of Oculus Home (purple) remains very bad in this scenario. The submission from BindlessDeferred (light green) continues to hog the single graphics queue, forcing Home to wait 100ms or so before it gets a turn. On the bright side, AMD&rsquo;s High Priority Compute queue continues to deliver the goods! Despite the massive number of compute threads swarming the GPU from the big dispatch, the compositor&rsquo;s submission continues to run at a steady rate of 90 Hz. Very impressive! But will it hold up in the final &ldquo;Slow Shader&rdquo; scenario? Let&rsquo;s find out!</p>
<p><img src="/images/converted/breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/amd_slowshader.png" alt="AMD_SlowShader"></p>
<center><i>AMD RX 460 - Slow Shader Scenario</i></center>
<p>Indeed, AMD&rsquo;s high-priority compute keeps working even in the case where very slow shader programs are running on the GPU. The only noticeable difference is that the compositor submission now takes about 2.4ms to complete, but other than that things look good. Ultimately it looks like Nvidia and AMD hardware do a better job in different scenarios, so I don&rsquo;t think we could say that one is inherently &ldquo;better&rdquo; than the other. In the future I&rsquo;m sure we&rsquo;ll see new innovations from both IHV&rsquo;s in this area, so I wouldn&rsquo;t get too hung up on these specific results.</p>
<h2 id="end-of-pipe">End of Pipe</h2>
<p>That&rsquo;s it for the series! Thank you to anyone who stuck around through the whole thing, which has now spanned 9 months! I learned quite a bit myself while doing research for these articles, so I&rsquo;m glad that I kept it going. But I think for my next post I&rsquo;m going to do a shorter write up that doesn&rsquo;t take me two years to finish. :)</p>
<hr>
<h2 id="comments">Comments:</h2>
<h4 id="heading"></h4>
<p><a href="%22%22"></a> - <time datetime="2018-12-11 09:34:08">Dec 2, 2018</time></p>
<p>Really good article, as always! I think there&rsquo;s a small typo in &ldquo;but no overlap between A and B. &ldquo;, should be C instead of B?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2018-12-11 21:29:06">Dec 2, 2018</time>
<p>Indeed, that was a typo! Thank you for pointing that out.</p>
<hr />
####
[Andy Robbins](http:// "raicuandi@gmail.com") - <time datetime="2018-12-10 19:35:53">Dec 1, 2018</time>
<p>Excellent series!</p>
<hr />
####
[]( "") - <time datetime="2018-12-14 21:12:02">Dec 5, 2018</time>
<p>very helpful to me, thank you!</p>
<hr />
####
[SONG]( "yqsong.hong@gmail.com") - <time datetime="2019-04-05 07:55:19">Apr 5, 2019</time>
<p>Great articles. I&rsquo;ve learned a lot about how GPU works in parallel.</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 1: What&#8217;s a Barrier? &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2018/03/06/breaking-down-barriers-part-1-whats-a-barrier/ "") - <time datetime="2019-03-05 13:44:35">Mar 2, 2019</time>
<p>[…] Part 1 – What’s a Barrier? Part 2 – Synchronizing GPU Threads Part 3 – Multiple Command Processors Part 4 – GPU Preemption Part 5 – Back To The Real World Part 6 – Experimenting With Overlap and Preemption […]</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 2: Synchronizing GPU Threads &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2018/04/01/breaking-down-barriers-part-2-synchronizing-gpu-threads/ "") - <time datetime="2019-03-05 13:45:03">Mar 2, 2019</time>
<p>[…] Part 1 – What’s a Barrier? Part 2 – Synchronizing GPU Threads Part 3 – Multiple Command Processors Part 4 – GPU Preemption Part 5 – Back To The Real World Part 6 – Experimenting With Overlap and Preemption […]</p>
<hr />
####
[Bryan]( "Bryan.Corell@disruptivegames.com") - <time datetime="2019-04-11 12:01:14">Apr 4, 2019</time>
<p>Great series! Very in depth and would love to see more :)</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Breaking Down Barriers – Part 5: Back To The Real World</title>
			<link>https://therealmjp.github.io/posts/breaking-down-barriers-part-5-back-to-the-real-world/</link>
			<pubDate>Sun, 09 Sep 2018 00:48:18 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/breaking-down-barriers-part-5-back-to-the-real-world/</guid>
			<description>This is Part 5 of a series about GPU synchronization and preemption. You can find the other articles here:
Part 1 - What&amp;rsquo;s a Barrier?
Part 2 - Synchronizing GPU Threads
Part 3 - Multiple Command Processors
Part 4 - GPU Preemption
Part 5 - Back To The Real World
Part 6 - Experimenting With Overlap and Preemption
Welcome to part 5 of the series! If you&amp;rsquo;ve read all of the articles so far, thanks for hanging in there!</description>
			<content:encoded><![CDATA[<p><em>This is Part 5 of a series about GPU synchronization and preemption. You can find the other articles here:</em></p>
<p><a href="../breaking-down-barriers-part-1-whats-a-barrier/">Part 1 - What&rsquo;s a Barrier?</a><br>
<a href="../breaking-down-barriers-part-2-synchronizing-gpu-threads/">Part 2 - Synchronizing GPU Threads</a><br>
<a href="../breaking-down-barriers-part-3-multiple-command-processors/">Part 3 - Multiple Command Processors</a><br>
<a href="../breaking-down-barriers-part-4-gpu-preemption/">Part 4 - GPU Preemption</a><br>
<a href="../breaking-down-barriers-part-5-back-to-the-real-world/">Part 5 - Back To The Real World</a><br>
<a href="../breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/">Part 6 - Experimenting With Overlap and Preemption</a><br></p>
<p>Welcome to part 5 of the series! If you&rsquo;ve read all of the articles so far, thanks for hanging in there! In the <a href="../breaking-down-barriers-part-4-gpu-preemption/">last article</a>, we spent a good deal of time talking about how GPU preemption might work for an imaginary GPU. We also talked about how having multiple command processors capable of kicking off GPU threads could potentially be helpful for reducing preemption latency, which in turn can improve the overall responsiveness of a GPU that needs to serve multiple applications.  For this article, I want to get back to discussing real-world GPU&rsquo;s, and how they deal with preemption in the context of an actual OS and driver infrastructure. I&rsquo;m mostly going to restrict myself to discussing Microsoft Windows and its associated Windows Display Driver Model (WDDM) and native 3D graphics API&rsquo;s (D3D11 and D3D12), since these are what I know best and have the most experience in. So I&rsquo;m afraid you&rsquo;ll have to look elsewhere if you need comprehensive information about Linux, MacOS, OpenGL, or Metal. I will talk just a bit about Vulkan in a few places, but I&rsquo;m pretty far from an expert on that topic. I&rsquo;m also going to only talk about desktop GPU&rsquo;s, since these are what I deal with almost exclusively in my line of work.</p>
<p>The first part of this article is something of a retrospective on pre-Windows 10 technology, so feel free to skip that if you&rsquo;re not interested. However I think that it provides a lot of valuable context for understanding <em>why</em> D3D12 is designed the way it is (which is of course one of the central themes of this series!), which is why I included it in the first place. It&rsquo;s also possible that my understanding of the Windows driver model is incomplete or inaccurate in a few ways, since I&rsquo;ve only ever been on the application side of things. If you&rsquo;re a seasoned driver developer or OS engineer and you read something that causes you to make a face, please feel free to reach out and/or correct me in the comments.</p>
<h2 id="the-past-xddm-and-wddm-1x">The Past: XDDM and WDDM 1.x</h2>
<p>When I first started learning the very basics of graphics programming, I was in my junior year of college back in 2005. Naturally I used the very latest in 3D graphics API&rsquo;s to draw my excessively-shiny spheres, which at the time was D3D9 running on Windows XP. Things were rather&hellip;different back then. 3D rendering on the GPU still wasn&rsquo;t really mainstream in terms of general application/OS usage, and was generally only used by games that expected to run in exclusive fullscreen modes. Technically it was possible to have two different programs simultaneously use D3D9 to render to two different windows, but the extent to which those two programs would cooperate and play nicely with each other could vary depending on the particular video card you were using, as well as the implementation details of its driver<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. This was due to the fact that the Windows 2000/XP display driver model (known as <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/display/windows-2000-display-driver-model-design-guide">XDDM</a>, or XPDM) mostly let display drivers handle complex issues like scheduling command buffers for execution on the GPU, or managing GPU resource memory from multiple processes. Things were particularly rough on the memory/resource side of things, since the OS and drivers were working through the aging <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/display/directdraw-architecture">DirectDraw infrastructure</a>.  On top of that you still had the entire legacy GDI pipeline with its own <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/display/graphics-system-overview">driver stack</a>, which was supposed to interoperate with D3D and DirectDraw.</p>
<p>All of this makes sense when you consider that it all grew organically from a world where 3D GPU&rsquo;s were still a novelty intended for playing games, and 2D graphics accelerators were ubiquitous. However it was also quite clear that the landscape was rapidly changing by the time that Windows Vista was nearing release. 3D-capable video processors were now found even in low-end machines (thanks to &ldquo;integrated&rdquo; GPU&rsquo;s such as the <a href="https://en.wikipedia.org/wiki/Intel_GMA">GMA series from Intel</a>), and the growing programmability of GPU&rsquo;s was causing intrepid programmers to explore the possibility of using those GPU&rsquo;s for things outside the realm of 3D graphics for games. OS developers like Microsoft were also considering the idea of using the GPU&rsquo;s 3D rendering capabilities as a core component of how they displayed UI on their desktops. In other words, GPU&rsquo;s were really starting to grow up, which meant it was time for the OS/driver stack to grow up along with them. This is where <a href="https://en.wikipedia.org/wiki/Windows_Display_Driver_Model">WDDM</a> comes in.</p>
<p>WDDM, which is short for &ldquo;<a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/display/windows-vista-display-driver-model-design-guide">Windows Display Driver Model</a>&rdquo;, was the new driver stack that replaced XDDM/XPDM in all Windows versions starting with Vista. WDDM was quite a departure from the earlier models, in that it really started to treat the GPU and its on-board memory as a shared resource arbitrated by the OS itself. For instance, with WDDM the OS was now in charge of submitting command buffers to a GPU, while the driver merely provided <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/display/submitting-a-command-buffer">hooks</a> for building command buffers in its hardware-specific format, and then executing a command buffer scheduled by the OS. This allows the OS internal scheduler to decide when a particular program&rsquo;s packet of work should be executed, potentially deciding that one program&rsquo;s commands are more important than another program&rsquo;s commands. In a similar vein, a global video memory manager now &ldquo;owned&rdquo; the on-board memory of the video card, with the driver again providing the <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/display/paging-video-memory-resources">necessary hooks</a> that would allow the OS to move data from system memory to video memory (or vice versa). This helped to unify GPU memory management across all vendors, and removed the need for band-aids like the old <a href="https://docs.microsoft.com/en-us/windows/desktop/direct3d9/managing-resources">MANAGED resource pool</a> from D3D9 by effectively virtualizing all resources. The <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/display/windows-vista-and-later-display-driver-model-operation-flow">general flow</a> of commands through the WDDM driver stack looked something like this:</p>
<p><img src="/images/converted/breaking-down-barriers-part-5-back-to-the-real-world/wddm_flow.png" alt="WDDM_Flow"></p>
<p>If you haven&rsquo;t been a windows driver developer for the past 10 years, there&rsquo;s probably a few steps in there that would make you raise an eyebrow. For instance, what the heck is a &ldquo;<a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/display/introduction-to-command-and-dma-buffers">DMA buffer</a>&rdquo;? It sounds like something fancy that might be related to hardware DMA units, but in reality it&rsquo;s just what the OS calls command buffers on the kernel side of the user-mode/kernel-mode boundary. I suspect that the &ldquo;DMA&rdquo; part of that name comes from the fact that the GPU can directly read from a DMA buffer through a physical address, but I don&rsquo;t actually know for sure. The step that you might find confusing is the one that&rsquo;s labeled &ldquo;Patch DMA Buffer with Physical Addresses&rdquo;. At the time of WDDM 1.0, GPU&rsquo;s had simple memory controllers that could only access their memory using physical addresses. This is rather unlike CPU&rsquo;s, which have the capability to work with virtual addresses that are mapped to physical addresses using a page table. Having the vendor-supplied drivers work directly with physical addresses when dealing with resources like buffers or textures would generally be a Bad Idea for a few reasons, but under WDDM it&rsquo;s a non-starter due to the fact that the OS&rsquo;s video memory manager has the ability to move resources in and out of device memory as it sees fit. So to make things work, WDDM requires that the driver submit allocation and patch lists alongside DMA buffers. The allocation list tells the OS all of the resources that referenced by the commands inside the DMA buffer, which lets the memory manager decide which resources need to be resident in device memory. The patch list then tells the OS where all of the resources are actually referenced within the DMA buffer, which the OS then uses to <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/d3dkmddi/nc-d3dkmddi-dxgkddi_patch">patch the DMA buffer with physical addresses</a> right before that buffer is executed on the GPU.</p>
<p>It should be obvious at this point that there was a whole lot going on behind the scenes whenever an app made a few draw calls! Fortunately D3D application developers only really had to worry about that left column, since everything in the other columns was generally hidden from you. In fact, the way that the D3D10/D3D11 API&rsquo;s were written didn&rsquo;t even directly expose the fact that GPU&rsquo;s were parallel processors being fed by buffers full of command streams. For the most part you could pretend that your Draws and Dispatches were happening synchronously with your CPU commands, and you would still get correct results. The only time the abstraction really leaked through was if you looked at the actual timings of your calls, particularly if you hit a case that required the CPU to wait for the GPU to catch up. All things considered, writing D3D programs really got <em>easier</em> for application developers in the transition from D3D9 to D3D10/11, with some of the reasons why getting spelled out in this <a href="https://docs.microsoft.com/en-us/windows/desktop/direct3darticles/graphics-apis-in-windows-vista">document</a>. <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Driver and OS developers weren&rsquo;t so lucky, and things were certainly quite rocky back during the initial transition from XDDM to WDDM 1.0. But after years of refinements it seems to have proved its worth, as we now take it for granted that the <a href="https://docs.microsoft.com/en-us/windows/desktop/dwm/dwm-overview">desktop composites its windows using the GPU</a>, or that <a href="https://www.chromium.org/developers/design-documents/gpu-accelerated-compositing-in-chrome">Chrome can use GPU acceleration</a> for rendering webpages while you&rsquo;re simultaneously playing Battlefield 1 on highest settings.</p>
<p>While there were a lot of minor updates to WDDM over various Windows versions and service packs, there was one major improvement in Windows 8 that&rsquo;s worth calling out. Starting in WDDM 1.2, drivers could now <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/display/gpu-preemption">specify whether or not they supported preemption</a> of their command buffers, and also what level of granularity they could prempt at. Drivers indicating their supporting by returning two enum values, <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/d3dkmdt/ne-d3dkmdt-_d3dkmdt_graphics_preemption_granularity">one for graphics</a> and <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/d3dkmdt/ne-d3dkmdt-_d3dkmdt_compute_preemption_granularity">one for compute</a>. Looking at the compute enum, the possible values seem quite similar to the premption granularities that we discussed in the earlier articles:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">enum</span> <span class="nc">_D3DKMDT_COMPUTE_PREEMPTION_GRANULARITY</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3DKMDT_COMPUTE_PREEMPTION_NONE</span> <span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3DKMDT_COMPUTE_PREEMPTION_DMA_BUFFER_BOUNDARY</span> <span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3DKMDT_COMPUTE_PREEMPTION_DISPATCH_BOUNDARY</span> <span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3DKMDT_COMPUTE_PREEMPTION_THREAD_GROUP_BOUNDARY</span> <span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3DKMDT_COMPUTE_PREEMPTION_THREAD_BOUNDARY</span> <span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">D3DKMDT_COMPUTE_PREEMPTION_SHADER_BOUNDARY</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">D3DKMDT_COMPUTE_PREEMPTION_GRANULARITY</span><span class="p">;</span>
</span></span></code></pre></div><p>As we discussed in earlier articles, finer-grained preemption can make for much nicer user experience when multitasking, especially when a particular program is really trying to hog the GPU.</p>
<p>UPDATE: it turns out that you can actually view your video card&rsquo;s reported premption granularity by using the old DirectX Caps Viewer! These days its included as part of the Windows 10 SDK, so you can typically find it in C:\Program Files (x86)\Windows Kits\10\bin\<SDKVersion>\x64\dxcapsviewer.exe. An Nvidia Titan V reports pixel-level for graphics and dispatch-level for compute, while an AMD RX Vega reports draw-level for graphics and DMA buffer-level for compute. Many thanks to <a href="https://twitter.com/Locuza_">Locuza</a> for pointing this out to me!</p>
<h2 id="problems-with-d3d11-and-wddm-1x">Problems With D3D11 and WDDM 1.x</h2>
<p>I think it&rsquo;s fair to say that from a D3D application developer&rsquo;s point of view, things generally got better and easier when comparing the Windows XP era to the Windows 7 era. I&rsquo;m sure driver and OS developers had plenty to complain about, but certainly in my day job as a game developer I appreciated the increased orthogonality of the API&rsquo;s as well as the fact that multi-tasking generally &ldquo;just worked&rdquo; across a variety of hardware (including multiple GPU&rsquo;s in the same machine!). But you should never underestimate the ability of a game programmer to complain about something (especially when it comes to Windows!), and we certainly found some things to gripe about. Pretty much all of them revolved around performance and multithreading.</p>
<p>Back when D3D11 was first announced, there was a lot of hype around the fact that it allowed for multi-threaded rendering via so-called <a href="https://docs.microsoft.com/en-us/windows/desktop/direct3d11/overviews-direct3d-11-render-multi-thread-render">deferred contexts</a>. While this turned out to be technically true (it&rsquo;s totally possible to have multiple threads issuing Draw and Dispatch calls in stock D3D11), in practice it wasn&rsquo;t as useful as everyone hoped it would be. Probably the biggest roadblock is something that we discussed way back in <a href="../breaking-down-barriers-part-1-whats-a-barrier/">part 1</a>: it&rsquo;s really tough (or perhaps impossible) to make implicit dependencies work in an efficient way when multiple threads are generating the rendering commands. If you&rsquo;ve got to run through the entire set of commands for a frame to determine when a depth buffer transitions from being writable to being readable as a texture, it quickly pushes you down the path of serializing your command lists before final generation of the hardware command buffer. This is definitely <em>not</em> great if your goal was to distribute the cost of command buffer generation across multiple cores, which is really the only reason to use deferred command lists in the first place. So that was a pretty big bummer, and led to games (mostly) sticking to single-threaded rendering on Windows.</p>
<p>Another issue that has sprung up in recent years is D3D11&rsquo;s inability to leverage multiple command processors/hardware queues on modern GPU&rsquo;s. As we learned in <a href="../breaking-down-barriers-part-3-multiple-command-processors/">Part 3</a>, submitting commands into multiple front-ends can potentially lead to higher shader core utilization, which in turn leads to higher overall throughput. We also saw that effectively utilizing these additional command processors requires identifying and submitting multiple chains of commands that aren&rsquo;t dependent on one another. Having the driver do this automatically is a big ask, especially when data dependencies are implicit rather than explicit. If you think about it, it&rsquo;s very similar to utilizing multiple CPU cores via threads: the compiler can handle detecting dependencies within a single thread, but it doesn&rsquo;t extract out independent sequences of instructions that would long enough to justify having the OS spin up a whole new thread. The problem is made even more difficult by the fact that command buffer building and submission is also implicit in D3D11/WDDM 1.x, which means there&rsquo;s no place for the app to give hints or more explicit input about which front-end a stream of commands should be submitted to. On top of that GPU&rsquo;s typically have restrictions on which commands can be executed on which command processors (for instance, some can only consume compute-oriented commands), and D3D11 has no way of expressing these restrictions through its API&rsquo;s.</p>
<p>One last issue that I&rsquo;ll discuss is less technical in nature, and has to do with the abstraction presented by the API and how it relates to deferred command lists. As I mentioned earlier, D3D11 adopts the model where command buffer recording and submission to the hardware is implicit based on an app&rsquo;s sequence of D3D11 commands, and is completely hidden behind the driver. This also effectively hides the asynchronous/parallel nature of the GPU, and lets you assume that draw/dispatch/copy commands happen synchronously and still get correct results. Or at least, that&rsquo;s the case if you&rsquo;re using the immediate context. Deferred contexts break this abstraction, since their semantics imply that the commands aren&rsquo;t actually executed right away and are instead executed when <a href="https://docs.microsoft.com/en-us/windows/desktop/api/d3d11/nf-d3d11-id3d11devicecontext-executecommandlist">ExecuteCommandList</a> is called on the immediate context. This basically works, but it&rsquo;s also pretty odd when you consider that the immediate context isn&rsquo;t really &ldquo;immediate&rdquo;, and is actually &ldquo;deferred&rdquo; with implicit submission. So really a deferred context was more of a double-deferred context! On top of that weirdness, deferred command lists also had some odd restrictions as a result of their user-facing execution model. In particular, the <a href="https://docs.microsoft.com/en-us/windows/desktop/api/d3d11/nf-d3d11-id3d11devicecontext-map">Map</a> operation was restricted to write-only operations, and disallowed anything involving reading. This stems from the fact that commands on a deferred command list can&rsquo;t be expected to execute (from an app point of view) until ExecuteCommandList was called, which in turn meant that you couldn&rsquo;t expect to read the results of prior commands. This is actually similar to how things work now in D3D12 (more on that in a minute), but it&rsquo;s a bit strange that those semantics only applied when doing multi-threading in D3D11. Finally, I suspect that the automatic memory management/versioning happening behind the scenes when using D3D11_MAP_WRITE_DISCARD really stretched things to their breaking point when having to support that in a multi-threaded, deferred context scenario. At some point it just makes more sense to let apps manage their memory and submission using the app&rsquo;s own knowledge of its particular needs, which leads us to&hellip;</p>
<h2 id="the-present-windows-10-d3d12-and-wddm-20">The Present: Windows 10, D3D12, and WDDM 2.0</h2>
<p>With D3D12 and <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/display/wddm-2-0-and-windows-10">WDDM 2.0</a>, the API and driver stack has pulled back much of the curtain around how work actually gets submitted to the GPU. Instead of abstracting away the fact that commands are getting recorded to buffers and executed asynchronously by a parallel processor, D3D12 (almost) directly exposes those details to applications. The API gives you an interface for recording commands (<a href="https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/nn-d3d12-id3d12graphicscommandlist">ID3D12GraphicsCommandList</a>), another interface for storing the memory into which those commands are recorded (<a href="https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/nn-d3d12-id3d12commandallocator">ID3D12CommandAllocator</a>), and a function that allows you to queue a completed command list for execution (<a href="https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/nf-d3d12-id3d12commandqueue-executecommandlists">ExecuteCommandLists</a>). Of course with this newfound power comes new responsibilities. Since the app is in the driver seat now, it means it has to now be aware of when those command buffers are in-flight so that it can avoid writing to them. This leads to a typical double-buffered &ldquo;submission loop&rdquo; where the application waits for the GPU to finish processing an old command buffer before re-using it:</p>
<p><img src="/images/converted/breaking-down-barriers-part-5-back-to-the-real-world/command_buffer_submission1.png" alt="Command_Buffer_Submission"></p>
<p>You generally deal with similar patterns all over the place when working with D3D12, since you&rsquo;re now responsible for all lifetime management when it comes to memory and resources. No more MAP_DISCARD for you when update a constant buffer, instead you have to craft your own mechanisms for grabbing a safe bit of memory. And if you want to read back results from the GPU, you had best make sure that you&rsquo;ve submitted the relevant command buffer and waited for it to complete before attempting to read that memory on the CPU. Like the barriers that we <a href="../breaking-down-barriers-part-1-whats-a-barrier/">discussed in the first article,</a> it&rsquo;s quite a large burden that&rsquo;s been shifted from the driver to the application. And the shift was mostly done for the same exact reason: to enable better efficiency and multithreading. The natural way to divide up command generation is to have each thread or task generate its own command buffer, and then submit them all as a &ldquo;chain&rdquo; when they&rsquo;re all been recorded. When you combine this with explicit barriers and a single API for specifying pipeline state via PSO&rsquo;s, you get an API that truly allows you parallelize command buffer generation across multiple CPU cores.</p>
<p>Providing more explicit control over submission also gives apps the ability to take advantage of GPU&rsquo;s that have more than one front-end for queuing or processing commands. In <a href="https://mynameismjp.wordpress.com/2018/06/17/breaking-down-barriers-part-3-multiple-command-processors/">Part 3</a>, we talked about how adding an additional command processor to a GPU can help improve overall utilization for cases where an application submits multiple distinct dependency chains (commonly referred to asynchronous compute). If we were stuck in the old D3D11 model where dependencies and submission were implicit, it would be very difficult (or impossible) for a driver to be able to extract out a non-dependent chain of dispatches to execute on a second command processor. Fortunately with D3D12 we&rsquo;re actually in a position to explicitly say &ldquo;this list of commands can be executed separately, and depends on this other list of commands&rdquo;, which can be done via command queues and fences. Command queues, like their name suggest, represent a single queue into which completed command buffers can be submitted for running on the GPU. When you create a command queue, you have to specify 1 of 3 different command list types to run on that queue:</p>
<ol>
<li>COPY - can execute CopyResource commands</li>
<li>COMPUTE - can execute CopyResource and Dispatch commands</li>
<li>DIRECT - can execute CopyResource, Dispatch, and Draw commands</li>
</ol>
<p>These command list types can potentially correspond to different &ldquo;engines&rdquo; on the GPU, which is what D3D/WDDM like to call a command processor. Each engine can have its own distinct set of commands it can run, hence the 3 types of command lists. Typically a discrete GPU will have at least 1 graphics command processor capable of executing all possible commands, and at least 1 DMA unit that can issue copy commands that are optimized for transferring over the PCI-e bus. Many recent GPU&rsquo;s also have additional compute-only command processors, but we&rsquo;ll get into that in more detail later in this article.</p>
<p>As an example, let&rsquo;s return to the post-processing case that we were discussing in Part 3:</p>
<p><img src="/images/converted/breaking-down-barriers-part-5-back-to-the-real-world/bloom_dof_combined2.png" alt="Bloom_DOF_Combined2"></p>
<p>To submit our work in such a way that the two dependency chains could be executed on both command processors, we would do something like this in D3D12:</p>
<ul>
<li>Create a DIRECT command queue, which we&rsquo;ll call <strong>GfxQueue</strong></li>
<li>Create 3 DIRECT command lists, which we&rsquo;ll call <strong>GfxCmdListA</strong>, <strong>GfxCmdListB</strong>, and <strong>GfxCmdListC</strong></li>
<li>Create a COMPUTE command queue and command list, which we&rsquo;ll call <strong>ComputeQueue</strong> and <strong>ComputeCmdList</strong></li>
<li>Create two fences, <strong>FenceA</strong> and <strong>FenceB</strong></li>
<li>Each frame:
<ul>
<li>Record all rendering commands for the Main Pass to <strong>GfxCmdListA</strong></li>
<li>Record all bloom rendering commands to <strong>GfxCmdListB</strong></li>
<li>Record all tone mapping and subsequent rendering commands to <strong>GfxCmdListC</strong></li>
<li>Record all DOF dispatches to <strong>ComputeCmdList</strong></li>
<li>Submit <strong>GfxCmdListA</strong> to <strong>GfxQueue</strong></li>
<li>Signal <strong>FenceA</strong> from <strong>GfxQueue</strong> on completion of <strong>GfxCmdListA</strong></li>
<li>Submit <strong>GfxCmdListB</strong> to <strong>GfxQueue</strong></li>
<li>Tell <strong>ComputeQueue</strong> to wait for <strong>FenceA</strong></li>
<li>Submit <strong>ComputeCmdList</strong> to <strong>ComputeQueue</strong></li>
<li>Signal <strong>FenceB</strong> from <strong>ComputeQueue</strong> on completion of <strong>ComputeCmdList</strong></li>
<li>Tell <strong>GfxQueue</strong> to wait for <strong>FenceB</strong></li>
<li>Submit <strong>GfxCmdListC</strong> to <strong>GfxQueue</strong></li>
</ul>
</li>
</ul>
<p><img src="/images/converted/breaking-down-barriers-part-5-back-to-the-real-world/bloom_dof_annotated.png" alt="Bloom_DOF_Annotated"></p>
<p>Since these D3D12 commands are very explicit about the dependencies between command lists as well as the kind of queue that the command lists should be submitted to, the OS and driver now have enough information to actually schedule the command lists on separate command processors. This is a major improvement over D3D11, whose implicit submission model didn&rsquo;t really allow for this to occur.</p>
<p>Based on how the queue and submission API&rsquo;s work, you may be tempted into thinking that submitting a command list to an ID3D12CommandQueue will directly submit the corresponding command buffer(s) to a hardware queue for a particular GPU front-end. But this isn&rsquo;t really true in D3D12 and WDDM 2.0. When we were going over the details of WDDM 1.x, we discussed how the OS actually had its own software scheduler that was responsible for deciding when a command buffer should actually run on a GPU, and potentially arbitrating submissions from multiple applications. This is very much still the case in D3D12/WDDM2.0. Consequently you&rsquo;re not submitting command buffers to a hardware queue, you&rsquo;re instead submitting them to the OS&rsquo;s scheduler. In addition to letting the OS handle shared resources between multiple applications, it also allows the OS to &ldquo;virtualize&rdquo; the queues for hardware that doesn&rsquo;t actually support concurrent execution on multiple front ends (in D3D/WDDM terminology we would say that the GPU only has a single engine). If you look at the descriptions of the queue types that I listed above, you may have noticed that they&rsquo;re set up such that each queue type&rsquo;s list of supported commands is actually a subset of the next queue type&rsquo;s supported commands. In other words, the DIRECT type can do everything you can do on a COMPUTE or COPY queue, and COMPUTE can do everything that you can do on a COPY queue:</p>
<p><img src="/images/converted/breaking-down-barriers-part-5-back-to-the-real-world/queue_types_venn_resized_300.png" alt="Queue_Types_Venn"></p>
<p>With the queue/engine functionality being specified as subsets of each other, the OS can take a submission from a COPY queue and redirect it to a DIRECT engine if it needs to. This saves applications from having to change how they submit their command buffers based on the supported functionality of the end-user&rsquo;s hardware and drivers. However to pull this off, the OS has to be able to take command buffers that were intended to be concurrent and &ldquo;flatten&rsquo; them down to a serial stream of command buffers. As an example, let&rsquo;s return to the DOF/bloom case study that we used earlier. If we were to submit that sequence of command buffers on a GPU that only has a single DIRECT engine, the OS will have to flatten the submissions into something like this:</p>
<p><img src="/images/converted/breaking-down-barriers-part-5-back-to-the-real-world/bloom_dof_single_engine.png" alt="Bloom_DOF_Single_Engine"></p>
<p>Since the bloom and DOF command buffers are independent of each other, they can be executed in any order with regards to other and still give the same results. However you can tell the OS scheduler to favor one queue&rsquo;s submissions over the other by creating the queue with a <a href="https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ne-d3d12-d3d12_command_queue_priority">higher-than-normal priority</a>. We should also note here that the OS needs to be fully aware of inter-queue dependencies in order to do this kind of flattening operation, which is why fences are heavyweight OS-level constructs instead of being lightweight sync operations. This is also why you can&rsquo;t perform cross-queue synchronization within a command list, which would be convenient for applications but would break the ability of the scheduler to flatten multi-queue submissions. One benefit gained from giving the OS global knowledge of submission dependencies is that it can detect and prevent certain deadlocks in advance., which is nicer than allowing the system to choke.</p>
<p>If you&rsquo;re ever curious to see what the OS and scheduler are actually doing with your command buffers behind the scenes, the best way to do so is to use <a href="https://graphics.stanford.edu/~mdfisher/GPUView.html">GPUView</a> (which is most easily done by using <a href="https://randomascii.wordpress.com/">Bruce Dawson</a>&rsquo;s invaluable <a href="https://github.com/google/UIforETW">UIforETW tool</a>). Here&rsquo;s an example capture from an submitting a bunch of work to both a DIRECT and COMPUTE queue running on an AMD RX 460:</p>
<p><img src="/images/converted/breaking-down-barriers-part-5-back-to-the-real-world/gpuview.png" alt="gpuview"></p>
<p>This screenshot is showing about 4 frame&rsquo;s worth of capture data, which you can tell by looking at the activity in the flip queue. The darker area shows where the GPU is idle while waiting for VSYNC, so we can use that denote where the frame begins and ends. Towards the bottom we have the &ldquo;device contexts&rdquo;, which is showing command buffers that have been submitted on the CPU side of things by a single process. The top one shows the compute submissions, while the bottom one is the graphics/direct submissions. All of these command buffers end up in the hardware queues at the top of the image, which is what we&rsquo;re interested in. Here we see there&rsquo;s two hardware queues, one called &ldquo;3D&rdquo; and the other called &ldquo;COMPUTE_0&rdquo;. By looking at the activity on these queues we can see that they&rsquo;re both processing submissions simultaneously, which suggests that the work from each queue will overlap. Ultimately what we&rsquo;re seeing here is pretty similar what I was illustrating in <a href="https://mynameismjp.wordpress.com/2018/07/03/breaking-down-barriers-part-4-gpu-preemption/">Part 4</a> with the GPU/Driver timelines, which means we can also use this tool to inspect preemption behavior from multiple applications.</p>
<p>For those of you using Vulkan, be aware that their queue/submission model does not work like D3D12&rsquo;s! In Vulkan, every physical device can be queried to discover which queue &ldquo;families&rdquo; it supports (a queue family is analogous to an engine type in D3D12), as well as how many of each queue family is available. You can then pass handles to those exposed queues when <a href="https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#devsandqueues-queue-creation">creating your logical device</a>, which then allows you to submit command buffers to those queues at runtime. This means that if a GPU only has a single front-end, you will probably only see a single queue exposed on the physical device.</p>
<p>There&rsquo;s one more improvement in WDDM 2.0 that I wanted to quickly mention: remember how I described the process for patching command buffers with physical addresses in WDDM 1.x? That&rsquo;s no longer necessary in WDDM 2.0 thanks to its added support for <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/display/gpu-virtual-memory-in-wddm-2-0">GPU virtual memory</a>. With the new driver model, each process gets its own virtual address space for GPU memory, just like they do for CPU memory. The app and driver can freely embed these virtual addresses directly into command buffers or <a href="https://docs.microsoft.com/en-us/windows/desktop/direct3d12/indirect-drawing">ExecuteIndirect argument buffers</a> without having to go through an expensive patching process right before execution. This change also allows the user-mode driver to directly build and <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/d3dumddi/nc-d3dumddi-pfnd3dddi_submitcommandcb">submit</a> a GPU-accessible command buffer instead of requiring the kernel-mode driver to create a DMA buffer. This is a nice performance improvement, and I would imagine it&rsquo;s also an improvement from a security point of view. When using the D3D12 API you often work directly with GPU virtual address for things like creating vertex buffer views or setting root CBV&rsquo;s/SRV&rsquo;s using a single pointer, which is pretty convenient for apps. Vulkan on the other hand doesn&rsquo;t expose GPU virtual addresses (likely because it has to run on platforms and hardware without full virtual memory support), so it will instead work with <a href="https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkBindBufferMemory.html">allocation/offset pairs</a>.</p>
<h2 id="a-quick-look-at-real-gpus">A Quick Look at Real GPU&rsquo;s</h2>
<p>Before we wrap up this article, I wanted to also spend just a bit of timing looking at real-world GPU&rsquo;s and their support for multiple command processors. In Parts 2, 3, and 4 we used my made-up GPU to look at how the basics of preemption and overlapped processing worked. But I wanted to also go over some real-world examples to see how they compare to my overly-simplified example, and also how they relate to D3D12&rsquo;s support for multi-queue submission.</p>
<p>Let&rsquo;s start out by looking at GPU&rsquo;s from AMD&rsquo;s. Take a look at this slide from one of <a href="http://developer.amd.com/wordpress/media/2013/06/2620_final.pdf">AMD&rsquo;s presentations</a>:</p>
<p><img src="/images/converted/breaking-down-barriers-part-5-back-to-the-real-world/amd_command_processors.png" alt="amd_command_processors"></p>
<p>In this diagram there&rsquo;s one &ldquo;GFX&rdquo; command processor, which is is the only command processor capable of issuing Draw commands that use the primitive and pixel pipes. It can also issue Dispatch calls that use the CS pipeline to access the shader cores. In the top left there are a few units labeled &ldquo;ACE&rdquo;, which stands for &ldquo;Asynchronous Compute Engine&rdquo;. These guys are basically an array of simplified, independent command processors that can only process compute-related commands. This means that they can issue Dispatch calls and perform synchronization-related commands, but they can&rsquo;t execute Draw commands that utilize the graphics pipeline. In other words, they exactly fit the required feature set of a COMPUTE engine in D3D12, which is obviously not a coincidence. In terms of functionality they&rsquo;re also rather similar to the dual command processors of my fictional MJP-4000, and they can provide the same benefits for preemption, multitasking, and increased utilization from overlapping multiple workloads. In fact, if you read some of <a href="http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/Asynchronous-Shaders-White-Paper-FINAL.pdf">AMD&rsquo;s documentation</a> you&rsquo;ll see them explain some of the same concepts that I was discussing in my earlier articles.</p>
<p>In practice the ACE&rsquo;s are a bit more sophisticated than what was discussed in my examples, particularly due to the fact that each ACE contains up to 8 hardware queues for feeding command buffers to the ACE&rsquo;s command processor. AMD&rsquo;s processors support up to 8 ACE&rsquo;s on a single chip, potentially allowing for 64 different command streams to be in-flight simultaneously! The separate queues support various scheduling and synchronization operations, effectively allowing them to serve as a very simple hardware task scheduler. These ACE&rsquo;s have been present through every revision of the GCN architecture, including the recent RX Vega series. AMD has also mentioned a few improvements over the course of 4 architecture revisions. Most notably for their semi-recent <a href="http://radeon.com/_downloads/polaris-whitepaper-4.8.16.pdf">Polaris architecture</a>, the ACE&rsquo;s were augmented with what they call a &ldquo;Quick Response Queue&rdquo;.  Their documentation suggests that this allows ACE&rsquo;s to submit high-priority workloads that take priority over work submitted from other command processors, which should allow for the kind of thread-level preemption that I showed in own preemption examples. Polaris also introduced what they called a &ldquo;Hardware Scheduler&rdquo; (HWS), which is described as additional independent processors that can be used to create a layer of virtualization around the physical hardware queues.</p>
<p>UPDATE: It turns out that the Quick Response Queue has <a href="https://www.anandtech.com/show/10195/oculus-rift-launch-day-news">been around</a> since GCN2, and HWS has been around since Fiji/GCN3. Thanks again to Locuza for correcting me on that.</p>
<p>This information suggests that we should see at least 1 compute engine exposed to Windows scheduler, as well as at least 1 queue with COMPUTE_BIT set being exposed on the physical device through Vulkan. The D3D12 side can be verified through experimentation by using ETW captures and GPUView (see the <a href="/images/converted/breaking-down-barriers-part-5-back-to-the-real-world/gpuview_resized_680.png">image</a> that I posted above showing a COMPUTE submission being executed on a hardware compute queue), but for Vulkan we can verify very easily using the <a href="https://vulkan.gpuinfo.org/listreports.php">Vulkan Hardware Database</a>. Here&rsquo;s <a href="https://vulkan.gpuinfo.org/displayreport.php?id=3966#queuefamilies">what we get</a> when looking at the &ldquo;Queue families&rdquo; tab of an <a href="https://radeon.com/_downloads/vega-whitepaper-11.6.17.pdf">RX Vega</a>:</p>
<p><img src="/images/converted/breaking-down-barriers-part-5-back-to-the-real-world/vega_vulkan_db.png" alt="Vega_Vulkan_DB"></p>
<p>It looks like AMD chose to expose their ACE&rsquo;s to Vulkan as 8 compute queues, which is plenty for an individual application. We can also see 2 queues with TRANSFER_BIT specified, which is Vulkan&rsquo;s version of a COPY queue. These correspond to the hardware DMA units that are present on an AMD Vega, as well as on virtually every discrete PC video card in the wild. Like I mentioned earlier, DMA units on GPU&rsquo;s are specifically optimized for transferring batches of data across the PCI-e bus into on-board GPU memory. The typical use case is read-only textures and buffers, which need to live in on-board memory in order for the GPU to be able to read from them with full bandwidth. Drivers from the pre-D3D12 days would use the DMA to perform initialization of resources with app-specified memory, since the DMA unit is optimized for transferring from system memory, and also because it can execute concurrently with graphics operations. DMA units can also transform textures into hardware-specific layouts, which typically involves using some variant of a <a href="https://en.wikipedia.org/wiki/Z-order_curve">Z-order curve</a> to obtain better 2D cache locality. With D3D12 and Vulkan you&rsquo;re of course on your own when initializing resources, and a COPY queue is the natural choice for doing this on discrete GPU&rsquo;s.</p>
<p>As for Nvidia, their hardware also appears to feature functionality that allows for simultaneously submitting multiple workloads to their GPUs. Their Kepler architecture introduced what they refer to as <a href="https://blogs.nvidia.com/blog/2012/08/23/unleash-legacy-mpi-codes-with-keplers-hyper-q/">Hyper-Q</a>, which sounds pretty similar to the ACE&rsquo;s on AMD GPUs:</p>
<p><img src="/images/converted/breaking-down-barriers-part-5-back-to-the-real-world/hyper-q.png" alt="hyper-q"></p>
<p>Their (very limited) documentation describes 32 hardware queues, and mentions that they can be used to submit multiple kernels from the same application or to allow multiple CUDA applications to submit workloads concurrently. Unfortunately it seems this functionality was limited only to CUDA, since <a href="http://www.anandtech.com/show/9124/amd-dives-deep-on-asynchronous-shading">comments from Nvidia</a> indicated that their GPU&rsquo;s could not simultaneously execute Hyper-Q commands alongside graphics commands. According to Nvidia, only their then-recent Maxwell 2.0 architecture was capable of running in a &ldquo;mixed mode&rdquo;, where 1 hardware queue could process graphics commands while the other 31 queues could simultaneously process compute commands. In theory this would allow for similar capabilities to AMD&rsquo;s hardware, however it appears that Nvidia <a href="https://benchmarks.ul.com/news/a-closer-look-at-asynchronous-compute-in-3dmark-time-spy">never exposed</a> the additional compute queues through either D3D12 or <a href="https://vulkan.gpuinfo.org/displayreport.php?id=3829#queuefamilies">Vulkan</a>, and instead allowed the OS to merge compute submissions onto the graphics queue. It wasn&rsquo;t until Nvidia released their more recent Pascal architecture that they decided to finally offer a bit of information on the subject. Their <a href="http://international.download.nvidia.com/geforce-com/international/pdfs/GeForce_GTX_1080_Whitepaper_FINAL.pdf">whitepaper</a> has a section describing a new feature that they refer to as &ldquo;dynamic load balancing&rdquo;, which contains the following diagram and text on page 14:</p>
<p><img src="/images/converted/breaking-down-barriers-part-5-back-to-the-real-world/nv_dynamic_load_balancing.png" alt="NV_Dynamic_Load_Balancing"></p>
<p><em>&ldquo;In Maxwell generation GPUs, overlapping workloads were implemented with static partitioning of the GPU into a subset that runs graphics, and a subset that runs compute. This is efficient provided that the balance of work between the two loads roughly matches the partitioning ratio. However, if the compute workload takes longer than the graphics workload, and both need to complete before new work can be done, and the portion of the GPU configured to run graphics will go idle. This can cause reduced performance that may exceed any performance benefit that would have been provided from running the workloads overlapped.&rdquo;</em></p>
<p>This suggests that the issue wasn&rsquo;t in their queues, but perhaps in the actual functional units that would execute the compute shaders. From their description, it sounds as though their hardware units needed to be configured as either &ldquo;graphics&rdquo; or &ldquo;compute&rdquo; mode (perhaps on the SM level), which meant that for compute to overlap with graphics the hardware needed to be statically partitioned. This isn&rsquo;t optimal, and it explains why they may have decided it was better off to just serialize compute queue submissions into the graphics submissions. Meanwhile the Pascal architecture sounds as though it can re-partition dynamically, which would make concurrent compute execution more feasible. This can be confirmed by noting that Pascal GPU&rsquo;s will execute COMPUTE submissions on a compute hardware queue when <a href="http://www.futuremark.com/static/images/news/nvidia-gtx-1080-async.png">viewed in GPUView</a>, or by looking at the <a href="https://vulkan.gpuinfo.org/displayreport.php?id=3970#queuefamilies">exposed queues in Vulkan:</a></p>
<p><img src="/images/converted/breaking-down-barriers-part-5-back-to-the-real-world/pascal_vulkan_db1.png" alt="Pascal_Vulkan_DB"></p>
<p>We see 8 compute queues exposed here, which matches the RX Vega. Interestingly we also see 16(!) queues with GRAPHICS_BIT set, which is also very intriguing! Unfortunately Nvidia&rsquo;s hasn&rsquo;t said much on the hardware that&rsquo;s backing these queues, so we&rsquo;ll have to wait and see if we learn more in the future.</p>
<p>There are also pages later in the Pascal whitepaper that provide some details on its thread-level preemption capability, which is a big improvement on the draw-level preemption supported by the earlier Maxwell 2.0 architecture. Interestingly it seems their hardware also features instruction-level preemption, but only for CUDA. As far as I know there hasn&rsquo;t been any information released about the queues on their newer Volta or Turing architectures, so we&rsquo;ll probably have to wait a while to see if there have been any improvements.</p>
<p>Intel&rsquo;s Gen9 architecture that was used in their Skylake CPU&rsquo;s does not appear to have anything beyond a single DIRECT/graphics engine exposed through either D3D12 or Vulkan. GPUView only shows 1 hardware queue processing commands, and the Vulkan DB only shows 1 queue being exposed on the physical device. Unfortunately I don&rsquo;t have access to a Gen 9.5 GPU (used on Kaby Lake), so I haven&rsquo;t been able to personally test it. However it appears that these GPU&rsquo;s still only <a href="https://vulkan.gpuinfo.org/displayreport.php?id=3955#queuefamilies">expose a single queue in Vulkan</a>, so I would assume that nothing has changed on that front. Not having even a COPY engine on these GPU&rsquo;s might seem strange at first, but remember that DMA units are mainly a feature of discrete video cards. Integrated GPU&rsquo;s like Intel&rsquo;s utilize CPU memory instead of dedicated video memory, which means that the CPU can write that memory instead of requiring a dedicated hardware unit.</p>
<h2 id="next-up">Next Up</h2>
<p>For the <a href="../breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/">sixth</a> (and final!) part of this series, I&rsquo;m going to share some results that I&rsquo;ve gathered from experimenting with overlapped GPU commands and GPU preemption.</p>
<hr>
<h2 id="comments">Comments:</h2>
<h4 id="heading"></h4>
<p><a href="%22%22">Brian</a> - <time datetime="2018-12-18 10:49:23">Dec 2, 2018</time></p>
<p>Thank you for this wonderful series!</p>
<hr />
####
[Daan Niphuis](https://plus.google.com/104552714963779816079 "dniphuis@gmail.com") - <time datetime="2018-10-28 05:36:43">Oct 0, 2018</time>
<p>I just want to say thanks for this insightful series of posts.</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 1: What&#8217;s a Barrier? &#8211; The Danger Zone](../breaking-down-barriers-part-1-whats-a-barrier/ "") - <time datetime="2019-03-05 13:44:33">Mar 2, 2019</time>
<p>[…] Synchronizing GPU Threads Part 3 – Multiple Command Processors Part 4 – GPU Preemption Part 5 – Back To The Real World Part 6 – Experimenting With Overlap and […]</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 2: Synchronizing GPU Threads &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2018/04/01/breaking-down-barriers-part-2-synchronizing-gpu-threads/ "") - <time datetime="2019-03-05 13:45:01">Mar 2, 2019</time>
<p>[…] Synchronizing GPU Threads Part 3 – Multiple Command Processors Part 4 – GPU Preemption Part 5 – Back To The Real World Part 6 – Experimenting With Overlap and […]</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 3: Multiple Command Processors &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2018/06/17/breaking-down-barriers-part-3-multiple-command-processors/ "") - <time datetime="2019-03-05 13:45:39">Mar 2, 2019</time>
<p>[…] Synchronizing GPU Threads Part 3 – Multiple Command Processors Part 4 – GPU Preemption Part 5 – Back To The Real World Part 6 – Experimenting With Overlap and […]</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Successful cooperation and stability also depended on how well the programs themselves handled the infamous <a href="https://docs.microsoft.com/en-us/windows/desktop/direct3d9/lost-devices">lost device state</a> that occurred in certain scenarios, most notably when alt-tabbing from fullscreen.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>That document briefly mentions one of biggest changes brought about by WDDM, which is the fact that IHV&rsquo;s now had to provide a user-mode driver component alongside their kernel-mode driver. This was a really big deal at the time, since under XDDM the IHV only provided a kernel-mode driver. This meant that anytime the driver had to process a draw call or do some state management, the application had to undergo an expensive user-mode -&gt; kernel mode context switch. The lack of a user-mode driver component was commonly cited as a reason as to why OpenGL could &ldquo;handle more draw calls&rdquo; than D3D9, and why D3D10 would be a major improvement for CPU performance. The concept of a user-mode driver also came just in time for everyone to fully adopt programmable shaders for everything, since drivers now frequently host complex optimizing compilers in their DLL&rsquo;s so that they can JIT-compile shader bytecode.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
		</item>
		
		<item>
			<title>Breaking Down Barriers - Part 4: GPU Preemption</title>
			<link>https://therealmjp.github.io/posts/breaking-down-barriers-part-4-gpu-preemption/</link>
			<pubDate>Wed, 04 Jul 2018 00:57:43 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/breaking-down-barriers-part-4-gpu-preemption/</guid>
			<description>This is Part 4 of a series about GPU synchronization and preemption. You can find the other articles here:
Part 1 - What&amp;rsquo;s a Barrier?
Part 2 - Synchronizing GPU Threads
Part 3 - Multiple Command Processors
Part 4 - GPU Preemption
Part 5 - Back To The Real World
Part 6 - Experimenting With Overlap and Preemption
Welcome back! For the past two articles we&amp;rsquo;ve been taking a in-depth look at how a fictional GPU converts command buffers into lots of shader threads, and also how synchronization of those threads affects the overall performance of the GPU.</description>
			<content:encoded><![CDATA[



  


<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
<script src="/js/load-photoswipe.js"></script>


<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/photoswipe.min.css" integrity="sha256-sCl5PUOGMLfFYctzDW3MtRib0ctyUvI9Qsmq2wXOeBY=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/default-skin/default-skin.min.css" integrity="sha256-BFeI1V+Vh1Rk37wswuOYn5lsTcaU96hGaI7OUVCLjPc=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/photoswipe.min.js" integrity="sha256-UplRCs9v4KXVJvVY+p+RSo5Q4ilAUXh7kpjyIP5odyc=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/photoswipe-ui-default.min.js" integrity="sha256-PWHOlUzc96pMc8ThwRIXPn8yH4NOLu42RQ0b9SpnpFk=" crossorigin="anonymous"></script>


<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

<div class="pswp__bg"></div>

<div class="pswp__scroll-wrap">
    
    <div class="pswp__container">
      <div class="pswp__item"></div>
      <div class="pswp__item"></div>
      <div class="pswp__item"></div>
    </div>
    
    <div class="pswp__ui pswp__ui--hidden">
    <div class="pswp__top-bar">
      
      <div class="pswp__counter"></div>
      <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
      <button class="pswp__button pswp__button--share" title="Share"></button>
      <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
      <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
      
      
      <div class="pswp__preloader">
        <div class="pswp__preloader__icn">
          <div class="pswp__preloader__cut">
            <div class="pswp__preloader__donut"></div>
          </div>
        </div>
      </div>
    </div>
    <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
      <div class="pswp__share-tooltip"></div>
    </div>
    <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
    </button>
    <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
    </button>
    <div class="pswp__caption">
      <div class="pswp__caption__center"></div>
    </div>
    </div>
    </div>
</div>

<p><em>This is Part 4 of a series about GPU synchronization and preemption. You can find the other articles here:</em></p>
<p><a href="../breaking-down-barriers-part-1-whats-a-barrier/">Part 1 - What&rsquo;s a Barrier?</a><br>
<a href="../breaking-down-barriers-part-2-synchronizing-gpu-threads/">Part 2 - Synchronizing GPU Threads</a><br>
<a href="../breaking-down-barriers-part-3-multiple-command-processors/">Part 3 - Multiple Command Processors</a><br>
<a href="../breaking-down-barriers-part-4-gpu-preemption/">Part 4 - GPU Preemption</a><br>
<a href="../breaking-down-barriers-part-5-back-to-the-real-world/">Part 5 - Back To The Real World</a><br>
<a href="../breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/">Part 6 - Experimenting With Overlap and Preemption</a><br></p>
<p>Welcome back! For the past <a href="../breaking-down-barriers-part-2-synchronizing-gpu-threads/">two</a> <a href="../breaking-down-barriers-part-3-multiple-command-processors/">articles</a> we&rsquo;ve been taking a in-depth look at how a fictional GPU converts command buffers into lots of shader threads, and also how synchronization of those threads affects the overall performance of the GPU. Up until now, we&rsquo;ve really only been discussing things in terms of how a single application would use a GPU. This keeps things relatively simple, but in reality there&rsquo;s almost never just one application that has an entire GPU to itself. Instead, the OS arbitrates multiple applications that all utilize the GPU as a shared resource, just like it does for other shared resources like the CPU or system memory. Doing it this way can make things more complex for the GPU and its driver, but it&rsquo;s really important for enabling responsive multi-tasking between multiple applications that rely on the GPU for rendering (which is something that you even <a href="https://youtu.be/0jfx5gVintE?t=84">see on consoles</a> these days!). It&rsquo;s also critical if the OS itself relies on the GPU for rendering elements and compositing them together, which is something that Windows has done ever since the introduction of the <a href="https://docs.microsoft.com/en-us/windows/desktop/dwm/dwm-overview">Desktop Window Manager</a> (DWM) with Windows Vista. In this article I&rsquo;m going to talk a bit about the implications that multi-tasking can have on a GPU, and how that can potentially tie back into  multiple command processors.</p>
<h2 id="fighting-over-a-gpu">Fighting Over a GPU</h2>
<p>As I was saying earlier, in 2018 it&rsquo;s no longer a safe assumption that only a single application will be using the GPU at a given point in time. There are web browsers or other productivity applications that might be using Direct2D to render UI elements. There might be streaming or voice chat applications that need to use the GPU to encode video and audio. Or there might be a VR composition service that needs <a href="https://developer3.oculus.com/blog/asynchronous-timewarp-on-oculus-rift/">always present to the headset at 90Hz</a>, even if the VR application itself is failing to render at that framerate. Serving multiple applications is probably doable for the older MJP-3000 and its single command processor, at least as long as no single application is submitting a significant amount of work at any given time. It&rsquo;s pretty reasonable to expect that a little logic in the OS or driver could work for this: the driver could maintain a queue (or multiple queues) in software, and each application could submit their command buffers to this software queue. Then whenever the GPU is idle, the driver can pick a command buffer from its queue and submit it to the GPU where it can actually get executed. So 3 apps might share the GPU like this:</p>
<p><a href="/images/converted/breaking-down-barriers-part-4-gpu-preemption/multi_app.png"><img src="/images/converted/breaking-down-barriers-part-4-gpu-preemption/multi_app.png" alt="multi_app"></a></p>
<p>Here we have two timelines: one showing what&rsquo;s enqueued in the driver (on the bottom), and one showing what&rsquo;s currently executing on the GPU (top). So the red app submits a command buffer which executes for a while, then the GPU goes idle again. Then the green app submits another command buffer which executes for longer, and eventually the blue app submits a final command buffer that completes very quickly. In this scenario our setup works fine: everybody is able to use the GPU when they need it, which means that the latency for any app&rsquo;s submission is roughly equal to the time it takes to complete the work on the GPU. On the flip side of things, it&rsquo;s not hard to imagine a scenario where things don&rsquo;t work out so nicely:</p>
<p><a href="/images/converted/breaking-down-barriers-part-4-gpu-preemption/multi_app_contested3.png"><img src="/images/converted/breaking-down-barriers-part-4-gpu-preemption/multi_app_contested3.png" alt="multi_app_contested"></a></p>
<p>This time, the red app decided to really hog the GPU for a while. This caused the submission from the green app to sit in the driver&rsquo;s queue for a long time, severely increasing the latency from submission to completion. In fact it waited so long that the blue app submitted while the green app was still waiting, causing the driver&rsquo;s queue to have two submissions queued at the same time. The latency is particularly unfortunate for the blue app, whose submission had barely any work and completed very quickly once it actually had a chance to run. It would be <em>really</em> unfortunate if the blue app was something critical, like the operating system&rsquo;s composition engine, or a browser that the user is currently interacting with.</p>
<p>To properly accommodate high-priority applications using the GPU, what we really want is some form of <strong><a href="https://en.wikipedia.org/wiki/Preemption_(computing)">preemption</a></strong>. We essentially want to be able to interrupt an app&rsquo;s workload that&rsquo;s being executed by the GPU, so that we can sneak in the high-priority work without having to wait for the GPU to go idle. With the older architecture of the MJP-3000, the GPU can only be working on one command buffer at a time, and the command processor always executes that entire command buffer to completion. Therefore the easiest way to do this would be to have the OS or driver try to split an app&rsquo;s submission into multiple smaller command buffers. This would effectively give the driver finer granularity to work with in its submission queue, and could potentially allow a high-priority submission to get in earlier:</p>
<p><a href="/images/converted/breaking-down-barriers-part-4-gpu-preemption/multi_app_preemption.png"><img src="/images/converted/breaking-down-barriers-part-4-gpu-preemption/multi_app_preemption.png" alt="multi_app_preemption"></a></p>
<p>This time the driver has split up the red app&rsquo;s workload into 5 command buffers, and the green app&rsquo;s workload into 4 command buffers. Having more command buffers means that the driver&rsquo;s queue is much deeper than before: at worst it now has 7 pending submissions simultaneously queued. However this also gives the driver more opportunities to switch to a higher-priority submission. This time around the blue submission is treated as high priority, which causes the driver to immediately move it to the top of the submission queue, which also displaces the other submissions. Thanks to the smaller command buffer size, the blue submission only needs to sit in the queue for a little bit before it has a chance to run on the GPU. This reduces the latency compared to the earlier case, where each app just submitted a single combined command buffer.</p>
<p>With the setup I just described, we effectively have <strong>command buffer-level preemption</strong><em>.</em> This means that our preemption granularity is tied to our command buffer granularity, and the preemption latency of a high-priority task is tied to the length of execution of any single command buffer. That last part might make you want to ask, &ldquo;how can the OS or driver know how long a particular command buffer will take?&rdquo;. And of course the short answer is that &ldquo;it can&rsquo;t&rdquo;. Even a command buffer with a single command can potentially take a very long time to completely execute through the entire pipeline, since that one command might be &ldquo;dispatch 5 billion threads&rdquo;. Or it might be &ldquo;dispatch 1024 threads of a really long shader program that takes 10 milliseconds to finish&rdquo;. This leaves the driver with an impossible task, since the best it can do is use heuristics in an attempt to chop up an app&rsquo;s command stream into bite-sized chunks.</p>
<p>The other problem with this approach is that it can potentially lead to lower throughput even when no preemption occurs. For instance, imagine if a GPU needed to completely flush at the end of every command buffer. In that case, splitting things up into lots of small command buffers would lead to lots of otherwise unnecessary sync points where multiple draws or dispatches can&rsquo;t overlap with each other. Another issue to consider is how to handle GPU state that can be set by the command buffer. The fictional MJP series of GPU doesn&rsquo;t really have much state to speak of, since it only handles compute shaders. But on a real GPU that supports a full D3D/GL/Vulkan rasterization pipeline there&rsquo;s all kinds of state that&rsquo;s modifed by individual commands. These include things like the viewport transform, bound render targets, depth buffer states, and the currently-bound shader programs for each stage. If the hardware is not capable of saving and restoring these states when switching command buffers, it may fall to the the driver to generate additional commands for restoring the expected rendering state. This can potentially add both GPU <em>and</em> CPU overhead for each command buffer switch, adding a further complication to the decision regarding how finely to chop up command buffers.</p>
<h2 id="having-the-hardware-help-us-out">Having the Hardware Help Us Out</h2>
<p>If we really want to avoid latency for high-priority jobs, we&rsquo;ll have to do better than a driver-managed command buffer queue in software. Fortunately, there&rsquo;s a few possible ways that we could do better with some changes to the GPU hardware. Probably the most obvious option would be to modify the command processor so that it can support switching to a different command buffer before its current stream has finished. If it were able to do this in between individual commands, then we would call this <strong>command-level preemption</strong>. This sounds nice in theory, but depending on the specifics of the GPU it can get a bit complicated. You&rsquo;d have to be careful to ensure that any of your synchronization commands will continue to work if preemption occurs between the dispatch and the following wait/flush command. On top of that, you would still have the same issues regarding saving and restoring GPU state when preemption occurs. Even if you get this working, your preemption latency is still going to depend on the maximum length of a wait/flush command, which in turn is going to be dictated by the maximum length of a single Draw or Dispatch. This means that if your high-priority command buffer gets submitted right as the GPU is cranking through an expensive full-screen pass (for instance, the tiled lighting compute shader in a deferred renderer), the high-priority submission might end up waiting a while before it can run.</p>
<p>Alternatively, another option to improve preemption latency would be to leverage the multiple command processors on a GPU like the newer MJP-4000. With multiple command processors, an app with heavy GPU workloads could hog one of the command processors all it wants, and a high-priority app could use one of the free command processors to sneak in some work without the other app even noticing.  To do this effectively, we just need to tweak the rules that the thread queues will utilize for sharing the shader cores:</p>
<ul>
<li>
<p>If only one queue has pending threads and there are any empty shader cores, the queue will fill up those cores with work until there are no cores left</p>
</li>
<li>
<p>If both queues have work to do and there are empty cores, those cores are split up based on the priority of the submissions being processed. If one queue has high-priority work and the other doesn&rsquo;t, then the threads from the high-priority dispatch fill up as many shader cores as they can, with any remaining cores going to the threads from the other queue. If both queues have work with the same priority, then the cores are split evenly and assigned threads from both thread queues (if there’s an odd number of cores available, the top queue gets the extra core)</p>
</li>
<li>
<p>Threads always run to completion once assigned to a shader core, which means pending threads can’t interrupt them</p>
</li>
</ul>
<p>Let&rsquo;s now take a look at how this would work out in practice. In the following example, app A will launch 80 threads of shader A (red), which take about 100 cycles for each thread to finish. About 250 cycles into this process, app B will submit a high-priority command buffer that will dispatch 16 threads of shader B (green), which also take about 100 cycles to complete:</p>

<link rel="stylesheet" href="/css/hugo-easy-gallery.css" />

<div class="gallery caption-position-bottom caption-effect-None hover-effect-slideup hover-transition" itemscope itemtype="http://schema.org/ImageGallery">
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/00.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/00.png" alt="0th" />
				    </div>
			      <figcaption>
		          <p>0th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/00.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/01.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/01.png" alt="1st" />
				    </div>
			      <figcaption>
		          <p>1st</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/01.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/02.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/02.png" alt="2nd" />
				    </div>
			      <figcaption>
		          <p>2nd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/02.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/03.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/03.png" alt="3rd" />
				    </div>
			      <figcaption>
		          <p>3rd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/03.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/04.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/04.png" alt="4th" />
				    </div>
			      <figcaption>
		          <p>4th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/04.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/05.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/05.png" alt="5th" />
				    </div>
			      <figcaption>
		          <p>5th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/05.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/06.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/06.png" alt="6th" />
				    </div>
			      <figcaption>
		          <p>6th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/06.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/07.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/07.png" alt="7th" />
				    </div>
			      <figcaption>
		          <p>7th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/07.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/08.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/08.png" alt="8th" />
				    </div>
			      <figcaption>
		          <p>8th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue_preempt/08.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
</div>

<p>Thanks to the additional command processor and thread queue, the high-priority dispatch is able to enqueue its threads immediately in the second thread queue. This allows the threads to start executing about 200 cycles earlier than if it had to wait for dispatch A to completely finish, which demonstrates that we&rsquo;ve successfully reduced latency for the high-priority task. Unfortunately dispatch B still had to spend about 50 cycles enqueued before its threads start executing on the shader cores, since the hardware has no ability preempt threads that have already started running. Therefore we can consider this to be <strong>thread-level preemption</strong>, since the granularity is equivalent to the length of a thread&rsquo;s execution. However it&rsquo;s important to note that since our fictional GPU has many shader cores (just like real GPU&rsquo;s, which can have thousands), in some cases there&rsquo;s no need for preemption at all. For instance, imagine if dispatch A and dispatch B both had 8 threads: in that case both dispatches could perfectly overlap with each other, essentially allowing ideal latency for the high-priority job without affecting the latency of the low-priority job. Therefore we would probably want to illustrate the CPU/GPU timeline like this:</p>
<p><a href="/images/converted/breaking-down-barriers-part-4-gpu-preemption/multi_app_multi_queue1.png"><img src="/images/converted/breaking-down-barriers-part-4-gpu-preemption/multi_app_multi_queue1.png" alt="multi_app_multi_queue"></a></p>
<p>While it&rsquo;s obvious how this could be useful for the case of an app like the Oculus compositor that needs to run its own high-priority job, it could also be useful within the context of a single app that wants to use the GPU for things other than graphics. Let&rsquo;s say that a game wants to run some physics calculations on the GPU during its simulation update phase while the GPU is simultaneously running rendering commands from the previous frame. It wouldn&rsquo;t be so great if the physics system had to wait around for a frame or so to get its results back from the GPU, and so it would make sense to submit a high-priority job to the extra command processor so that it can execute quickly.</p>
<p>With support for thread-level preemption, the only time our high-priority dispatch would get stuck waiting would be if the all of the shader cores were running threads that took a <em>very</em> long time to complete. If we wanted to avoid waiting in that case as well, we would need to essentially suspend threads that are already in-flight in order to allow high-priority threads to run without waiting for existing threads to finish executing. This would be considered <strong>instruction-level preemption</strong>, since our granularity would be at the level of a single instruction that executes on the shader core. This sort of preemption is very common on CPU&rsquo;s, which are often capable of <a href="https://en.wikipedia.org/wiki/Context_switch">switching contexts</a> in the middle of an instruction stream. On GPU&rsquo;s a full context switch can be more impractical, since they typically work with large groups of threads that collectively require hundreds or thousands of registers!</p>
<h2 id="next-up">Next Up</h2>
<p>In <a href="../breaking-down-barriers-part-5-back-to-the-real-world/">Part 5</a>, I&rsquo;m going wrap up our discussion of multiple command processors and preemption by discussing some real-world GPU&rsquo;s from Intel, AMD and Nvidia. I&rsquo;m also going to give an overview of how D3D12 exposes command buffer submission, and also explain how Windows lets you view the low-level queuing and execution of command buffers via ETW and GPUView. See you then!</p>
<hr>
<h2 id="comments">Comments:</h2>
<h4 id="heading"></h4>
<p><a href="http://mynameismjp.wordpress.com/" title="mpettineo@gmail.com">MJP</a> - <time datetime="2018-07-04 12:06:29">Jul 3, 2018</time></p>
<p>Thank you for pointing that out Andy, I goofed on that one! I re-worked the images to use 80 threads instead of 96, and now things work out the way they should. :) And yeah, what you&rsquo;re saying is pretty much the exact reason I started writing these articles. Working with consoles is fantastic for learning all of these low-level details, but unfortunately that knowledge is locked away behind NDA&rsquo;s. And then would I would look at the D3D12 API&rsquo;s and programming guides for barriers, and wonder how how anybody without console experience would know *why* you need those barriers in the first place. I would have loved to use the PS4 GPU (or one of its PC cousins) as an actual real-world case study for the scenarios I bring up, but I don&rsquo;t want to run afoul of NDA&rsquo;s. So for now I&rsquo;m sticking to my made-up GPU, and pointing to things explicitly mentioned in publicly-available documents.</p>
<hr />
####
[anon]( "notarealmailaddress@asyoucanseeclear.ly") - <time datetime="2018-07-04 15:31:49">Jul 3, 2018</time>
<p>AMD has released the GCN and Vega Whitepapers which you linked to in earlier posts but there also is the Register Reference Guide for SI, the linux amdgpu kernel driver and two Open Source Vulkan implementations (radv, xgl+pal). They contain all the information that you need to explain sufficiently how the hardware works IMO.</p>
<hr />
####
[Andy Robbins]( "raicuandi@gmail.com") - <time datetime="2018-07-04 03:39:29">Jul 3, 2018</time>
<p>Really great tutorial on how these things work. Looking back it&rsquo;s hard to understand how this works purely from transition (data) barriers, since they don&rsquo;t really make execution barriers explicit. For me it only became clear what&rsquo;s going on after reading PS4 docs, but if those aren&rsquo;t an option to someone, this series of posts is the next best thing. Spotted a mistake: the interactive demo loses 16 threads from Dispatch A at the same time 16 threads from Dispatch B get to execute. The total duration should be ~700 cycles not ~600.</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 1: What&#8217;s a Barrier? &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2018/03/06/breaking-down-barriers-part-1-whats-a-barrier/ "") - <time datetime="2019-03-05 13:44:31">Mar 2, 2019</time>
<p>[…] a Barrier? Part 2 – Synchronizing GPU Threads Part 3 – Multiple Command Processors Part 4 – GPU Preemption Part 5 – Back To The Real World Part 6 – Experimenting With Overlap and […]</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 2: Synchronizing GPU Threads &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2018/04/01/breaking-down-barriers-part-2-synchronizing-gpu-threads/ "") - <time datetime="2019-03-05 13:44:59">Mar 2, 2019</time>
<p>[…] a Barrier? Part 2 – Synchronizing GPU Threads Part 3 – Multiple Command Processors Part 4 – GPU Preemption Part 5 – Back To The Real World Part 6 – Experimenting With Overlap and […]</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 3: Multiple Command Processors &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2018/06/17/breaking-down-barriers-part-3-multiple-command-processors/ "") - <time datetime="2019-03-05 13:45:37">Mar 2, 2019</time>
<p>[…] a Barrier? Part 2 – Synchronizing GPU Threads Part 3 – Multiple Command Processors Part 4 – GPU Preemption Part 5 – Back To The Real World Part 6 – Experimenting With Overlap and […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Breaking Down Barriers - Part 3: Multiple Command Processors</title>
			<link>https://therealmjp.github.io/posts/breaking-down-barriers-part-3-multiple-command-processors/</link>
			<pubDate>Mon, 18 Jun 2018 02:14:52 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/breaking-down-barriers-part-3-multiple-command-processors/</guid>
			<description>This is Part 3 of a series about GPU synchronization and preemption. You can find the other articles here:
Part 1 - What&amp;rsquo;s a Barrier?
Part 2 - Synchronizing GPU Threads
Part 3 - Multiple Command Processors
Part 4 - GPU Preemption
Part 5 - Back To The Real World
Part 6 - Experimenting With Overlap and Preemption
Welcome to Part 3 of the series! In this article, I&amp;rsquo;m going to talk a bit about how multiple command processors can be used to increase the overall performance of a GPU by reducing the amount of time that shader cores sit idle.</description>
			<content:encoded><![CDATA[



  


<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
<script src="/js/load-photoswipe.js"></script>


<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/photoswipe.min.css" integrity="sha256-sCl5PUOGMLfFYctzDW3MtRib0ctyUvI9Qsmq2wXOeBY=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/default-skin/default-skin.min.css" integrity="sha256-BFeI1V+Vh1Rk37wswuOYn5lsTcaU96hGaI7OUVCLjPc=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/photoswipe.min.js" integrity="sha256-UplRCs9v4KXVJvVY+p+RSo5Q4ilAUXh7kpjyIP5odyc=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/photoswipe-ui-default.min.js" integrity="sha256-PWHOlUzc96pMc8ThwRIXPn8yH4NOLu42RQ0b9SpnpFk=" crossorigin="anonymous"></script>


<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

<div class="pswp__bg"></div>

<div class="pswp__scroll-wrap">
    
    <div class="pswp__container">
      <div class="pswp__item"></div>
      <div class="pswp__item"></div>
      <div class="pswp__item"></div>
    </div>
    
    <div class="pswp__ui pswp__ui--hidden">
    <div class="pswp__top-bar">
      
      <div class="pswp__counter"></div>
      <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
      <button class="pswp__button pswp__button--share" title="Share"></button>
      <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
      <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
      
      
      <div class="pswp__preloader">
        <div class="pswp__preloader__icn">
          <div class="pswp__preloader__cut">
            <div class="pswp__preloader__donut"></div>
          </div>
        </div>
      </div>
    </div>
    <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
      <div class="pswp__share-tooltip"></div>
    </div>
    <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
    </button>
    <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
    </button>
    <div class="pswp__caption">
      <div class="pswp__caption__center"></div>
    </div>
    </div>
    </div>
</div>

<p><em>This is Part 3 of a series about GPU synchronization and preemption. You can find the other articles here:</em></p>
<p><a href="../breaking-down-barriers-part-1-whats-a-barrier/">Part 1 - What&rsquo;s a Barrier?</a><br>
<a href="../breaking-down-barriers-part-2-synchronizing-gpu-threads/">Part 2 - Synchronizing GPU Threads</a><br>
<a href="../breaking-down-barriers-part-3-multiple-command-processors/">Part 3 - Multiple Command Processors</a><br>
<a href="../breaking-down-barriers-part-4-gpu-preemption/">Part 4 - GPU Preemption</a><br>
<a href="../breaking-down-barriers-part-5-back-to-the-real-world/">Part 5 - Back To The Real World</a><br>
<a href="../breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/">Part 6 - Experimenting With Overlap and Preemption</a><br></p>
<p>Welcome to Part 3 of the series! In this article, I&rsquo;m going to talk a bit about how multiple command processors can be used to increase the overall performance of a GPU by reducing the amount of time that shader cores sit idle.</p>
<h2 id="flushing-performance-down-the-drain">Flushing Performance Down The Drain</h2>
<p>Looking back on our fictional GPU that we discussed in Part 2, it&rsquo;s fair to say to its synchronization capabilities are sufficient to obtain correct results from a sequence of compute jobs with coarse-grained dependencies between those jobs. However, the tool that we used to resolve those dependencies (a global GPU-wide barrier that forces all pending threads to finish executing, also known as a &ldquo;flush&rdquo;) is a <em>very</em> blunt instrument. It&rsquo;s bad enough that we&rsquo;re unable to represent dependencies at the sub-dispatch level (in other words, only some of threads from one dispatch depend on only some of threads from another dispatch), but it can also force us into situations where non-dependent dispatches still can&rsquo;t overlap with other. This can be really bad when you&rsquo;re talking about GPU&rsquo;s that require thousands of threads to fill all of their executions units, particularly if your dispatches tend towards low thread counts and high execution times. If you recall from Part 2, we discovered that the cost of each GPU-wide barrier was tied to the corresponding decrease in utilization, and long-running threads can result in cores sitting idle for a long period of time.</p>
<p>In Part 2 we did discuss one way that the MJP-3000 could extract some extra overlap from a sequence of dispatches. Specifically, we talked about a case with two dependent dispatches, and a third dispatch that was independent of the first two.  By splitting the barrier into two steps ( SIGNAL_POST_SHADER and WAIT_SIGNAL), we could effectively sync only a single dispatch&rsquo;s threads instead of waiting for all executing threads to finish. It worked in that case, but it&rsquo;s still very limited tool. In that example we relied on our advance knowledge of dispatch C&rsquo;s thread count and execution time to set up the command buffer in a way that would maximize overlap. But this may not be practical in the real-world, where thread count and execution time might vary from frame-to-frame. That technique also doesn&rsquo;t scale up to more complex dependency graphs, since we&rsquo;re always going to be limited by the fact that there&rsquo;s only 1 command processor that can wait for something to finish, and then kick off more threads.</p>
<p>To give you a better idea of what I&rsquo;m talking about, let&rsquo;s switch to an example that&rsquo;s more grounded in reality. In real engines performing graphics work on GPU&rsquo;s, it&rsquo;s very common to have <strong>chains</strong> of draws or dispatches where each one depends on the previous step. For instance, imagine a bloom pass that requires several downsampling steps, followed by several blur passes, followed by several upscale passes. So you might end up with around 6-8 different dispatches, with a barrier between each one:</p>
<p><a href="/images/converted/breaking-down-barriers-part-3-multiple-command-processors/bloom_timeline.png"><img src="/images/converted/breaking-down-barriers-part-3-multiple-command-processors/bloom_timeline.png" alt="bloom_timeline"></a></p>
<p>Now, let&rsquo;s say we would like to overlap this with a large dispatch that takes about an equal amount of time. Overlapping it with a single step is pretty simple: we just need to make sure that a bloom dispatch and the large dispatch end up on the same side of a barrier, and they will overlap with each other. So for instance we could dispatch the large shader, then dispatch the first downscale, and then issue a barrier. However, this might not be ideal if the GPU has a setup similar to our fictional GPU, where our barriers are implemented in terms of commands that require waiting for all previously-launched shaders to finish executing. If  that&rsquo;s the case, then the first barrier after the long dispatch might cause a long period with no overlap:</p>
<p><a href="/images/converted/breaking-down-barriers-part-3-multiple-command-processors/bloom_timeline_overlap.png"><img src="/images/converted/breaking-down-barriers-part-3-multiple-command-processors/bloom_timeline_overlap.png" alt="bloom_timeline_overlap"></a></p>
<p>This is better than nothing, but not great. Really we want the long dispatch to fully overlap one of the barriers, since the associated cache flushes and other actions are periods where the shader units would otherwise be completely idle. We can achieve this with split barriers, but we could only do this once if our SIGNAL_POST_SHADER instruction is limited to signaling after all previously-queued threads have completed:</p>
<p><img src="/images/converted/breaking-down-barriers-part-3-multiple-command-processors/bloom_timeline_overlap_split.png" alt="bloom_timeline_overlap_split"></p>
<p>Things could get even more complicated if we wanted to overlap our bloom with a <em>different</em> chain of dispatches. For example, we might have a depth of field technique that needs 4-5 dispatches that are all dependent on the previous dispatch, but are otherwise independent from the bloom dispatches. By interleaving the dispatches from both techniques it would be possible to achieve some overlap, but it would be complex and difficult to balance. In both cases we&rsquo;re really being held back by the limitations of our barrier implementation, and our lives would be much easier if we could submit each pass as a set of completely independent commands that aren&rsquo;t affected by each other&rsquo;s sync operations.</p>
<h2 id="two-command-processors-are-better-than-one">Two Command Processors Are Better Than One</h2>
<p>After much, much complaining from the programmers writing software for the MJP-3000 about how much performance they were losing to sync points, the hardware engineers at MJP Industries finally had enough. When designing the all-new MJP-4000, they decided that the simplest way to handle multiple independent command streams would be to go ahead and copy/paste the front-end of the GPU! The logical hardware layout now looks like this:</p>
<p><img src="/images/converted/breaking-down-barriers-part-3-multiple-command-processors/multi_queue_overview.png" alt="multi_queue_overview"></p>
<p>As you can see, there&rsquo;s now two command processors attached to two separate thread queues. This naturally gives the hardware the ability to handle two command buffers at once, with two different processors available for performing the flush/wait operations required for synchronization. The MJP-4000 still has the same number of shader cores as the 3000 (16), which means we haven&rsquo;t actually increased the theoretical maximum throughput of the GPU. However the idea is that by using dual command processors we can reduce the amount of time that those shader cores sit idle, therefore improving the overall throughput of the jobs that it runs.</p>
<p>Looking at the diagram, you might (rightfully) wonder &ldquo;how exactly do the two front-ends share the shader cores?&rdquo; On real GPU&rsquo;s there are several possible ways to handle that, which we will touch on a bit in a later article. But for now, let&rsquo;s assume that the dual thread queues use a very simple scheme for sharing the cores:</p>
<ul>
<li>
<p>If only one queue has pending threads and there are any empty shader cores, the queue will fill up those cores with work until there are no cores left</p>
</li>
<li>
<p>If both queues have work to do and there are empty cores, those cores are split up evenly and assigned threads from both thread queues (if there&rsquo;s an odd number of cores available, the top queue gets the extra core)</p>
</li>
<li>
<p>Threads always run to completion once assigned to a shader core, which means pending threads can&rsquo;t interrupt them</p>
</li>
</ul>
<p>Let&rsquo;s now look at how all of this all would work out in practice. Let&rsquo;s imagine we have two independent chains of dispatches to run on the GPU at roughly the same time. Chain 1 consists of two dependent dispatches, which we&rsquo;ll call Dispatch A (red) and Dispatch B (green). Dispatch A consists of 68 threads that each take 100 cycles to complete. Dispatch B then reads the results of Dispatch A, which means Chain 1 will need a FLUSH command between Dispatch A and Dispatch B. Once the flush completes, Dispatch B then launches another 8 threads that take 400 cycles to complete. Chain 2 also consists of two dependent dispatches, which we&rsquo;ll call Dispatch C (blue) and Dispatch D (purple). Dispatch C will launch 80 threads that take 100 cycles to complete, followed by another 80 threads that take 100 cycles to complete. Here&rsquo;s how it plays out:</p>

<link rel="stylesheet" href="/css/hugo-easy-gallery.css" />

<div class="gallery caption-position-bottom caption-effect-None hover-effect-slideup hover-transition" itemscope itemtype="http://schema.org/ImageGallery">
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue/00.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue/00.png" alt="0th" />
				    </div>
			      <figcaption>
		          <p>0th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue/00.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue/01.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue/01.png" alt="1st" />
				    </div>
			      <figcaption>
		          <p>1st</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue/01.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue/02.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue/02.png" alt="2nd" />
				    </div>
			      <figcaption>
		          <p>2nd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue/02.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue/03.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue/03.png" alt="3rd" />
				    </div>
			      <figcaption>
		          <p>3rd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue/03.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue/04.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue/04.png" alt="4th" />
				    </div>
			      <figcaption>
		          <p>4th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue/04.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue/05.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue/05.png" alt="5th" />
				    </div>
			      <figcaption>
		          <p>5th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue/05.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue/06.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue/06.png" alt="6th" />
				    </div>
			      <figcaption>
		          <p>6th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue/06.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue/07.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue/07.png" alt="7th" />
				    </div>
			      <figcaption>
		          <p>7th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue/07.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue/08.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue/08.png" alt="8th" />
				    </div>
			      <figcaption>
		          <p>8th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue/08.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue/09.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue/09.png" alt="9th" />
				    </div>
			      <figcaption>
		          <p>9th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue/09.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue/10.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue/10.png" alt="10th" />
				    </div>
			      <figcaption>
		          <p>10th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue/10.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/multi_queue/11.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/multi_queue/11.png" alt="11th" />
				    </div>
			      <figcaption>
		          <p>11th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/multi_queue/11.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
</div>

<p>This sequence is easily the most complex example that we&rsquo;ve looked at, so let&rsquo;s break it down step by step:</p>
<ul>
<li>
<p>At cycle 0, we see that Chain 1&rsquo;s command buffer has been submitted to the top-most command processor, which is ready to execute the Dispatch A command.</p>
</li>
<li>
<p>Next we fast-forward to 50 cycles later, where we see that Dispatch A&rsquo;s threads have been pushed to the top thread queue, and 16 threads are already running on the shader cores. It&rsquo;s at this point that Chain 2&rsquo;s command buffer is submitted to the bottom command processor, with the first command to be processed is Dispatch C.</p>
</li>
<li>
<p>1 cycle later at cycle 51, Dispatch C&rsquo;s threads are sitting in the thread queue, but none of them are actually executing. This is because our all of the shader cores are already busy, and we&rsquo;ve already stated that our GPU doesn&rsquo;t interrupt running threads.</p>
</li>
<li>
<p>The 80 threads from Dispatch C need to wait until cycle 102, which is when Dispatch A has written its results to memory and the shader cores are finally freed up.</p>
</li>
<li>
<p>1 cycle later in cycle 103 the two thread queues will evenly split the available cores, and 8 threads from both A and C get assigned to the shader cores. Once we&rsquo;ve hit this point, the two dispatches continue to evenly share the available cores (since every batch of 8 threads starts and finishes at the same time as the other dispatch&rsquo;s batch), which continues for about another 500 cycles.</p>
</li>
<li>
<p>At cycle 608, something interesting happens. At this point only 4 threads from Dispatch A were running, since that&rsquo;s all that was left after executing the previous 64 threads. The top-most command processor is running a FLUSH command, which means it&rsquo;s waiting for those 4 threads to complete before it moves on and executes the next DISPATCH command. This means that if we <em>didn&rsquo;t</em> have the second command processor + thread queue launching threads from Dispatch C, then 12 of those 16 shader cores would have been idle due to the FLUSH! However by utilizing our dual command buffers, we&rsquo;ve ensured that those idle cores stay busy with work from another chain!</p>
</li>
<li>
<p>Moving on to cycle 710, Dispatch A finally finishes writing its results, and Dispatch B gets its 8 threads enqueued in the top-most thread queue. Unfortunately those 8 threads have to wait about 100 cycles for a batch of Dispatch C&rsquo;s threads to finish, since the bottom thread queue managed to get in there and hog all of the shader cores while the top-most command processor was handing the FLUSH and DISPATCH commands.</p>
</li>
<li>
<p>At cycle 810 things return back to what we had before: both thread queues sharing the shader cores evenly. It should be noted that this is another point where we would normally have idle cores if we didn&rsquo;t have dual front-ends, since Dispatch B only launched 8 threads! This case is actually even worse than what we had earlier with idle cores being caused by a FLUSH, since threads from Dispatch B take 400 cycles to complete. So it&rsquo;s a good thing we have that extra front-end!</p>
</li>
<li>
<p>At cycle 911 Dispatch C finally starts to wind down, which cause a period of 100 cycles where 4 cores are idle. But of course 100 cycles at 75% utilization is still a <em>lot</em> better than 400 cycles with 50% utilization, so that&rsquo;s really not so bad.</p>
</li>
<li>
<p>Around 100 cycles later we hit cycle 1012, where Dispatch D rolls in to once again fill up those remaining 8 shader cores.</p>
</li>
<li>
<p>The sharing continues for about another 200 cycles until Dispatch B finishes at cycle 1214, at which point Dispatch D is free to use all of the cores it wants.</p>
</li>
<li>
<p>Finally, at cycle 1618 the last batch from Dispatch D finishes, and all of our results are in memory.</p>
</li>
</ul>
<p>Whew, we made it! The details were a bit complex, but we can distill this down to something much simpler: <strong>by executing two command buffers at once, we got an overall higher utilization compared to if we ran them one at a time</strong>. Pretty neat, right? However, this higher utilization came at the cost of higher <strong>latency</strong> for each individual chain. This is similar to what we saw in Part 2 when we overlapped multiple dispatches: it took longer for each separate chain to finish, but the combined execution time of Chain 1 + Chain 2 is lower when they&rsquo;re interleaved compared to running them one at a time. Another way that you could say this is that we improved the <strong>overall throughput</strong> of the GPU. This is pretty impressive when you consider that we did this without adding additional shader cores to the GPU, and without changing the shader programs themselves! Keeping the shader core count at 16 means that we still have the same theoretical peak throughput that we had with the MJP-3000, and no amount of extra queues can get us past that peak. All we really did is make sure that the shader cores have less idle time, which can still be a big win when that percentage of idle time is high.</p>
<p>For real-world GPU&rsquo;s that actual do graphics work (and not just dispatches like our imaginary GPU), there can be even more opportunities for filling idle shader cores with work from another command stream. As I mentioned in part 1, real GPU&rsquo;s often have idle time due to flush caches and decompression steps. There&rsquo;s also certain operations that you can do on GPU that barely even use the shader cores at all! Probably the most common of these is depth-only rasterization, which is typically used for depth prepasses or for generating shadow maps. For depth-only rendering the shader cores will be used to transform vertices in the vertex shader (and possibly also in the Hull/Domain/Geometry shader stages), but no pixel shader is run. Vertex counts tend to be much lower than pixel counts, which makes it very easy for these passes to end up getting bottlenecked by fixed-function triangle setup, rasterization, and/or depth buffer processing. When those fixed-function units become the bottleneck, the vertex shaders will only use a fraction of the available shader cores, leaving many idle cores available for other command streams to utilize. <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>If we were to try to draw an analogy to the CPU world, the closest match to our multi-queue setup would probably be <a href="https://en.wikipedia.org/wiki/Simultaneous_multithreading">Simultaneous Multithreading</a>, or <strong>SMT</strong> for short. You may also know this as <a href="https://en.wikipedia.org/wiki/Hyper-threading">Hyper-Threading</a>, which is the Intel marketing name for this feature. The basic idea is pretty similar: it allows CPU&rsquo;s to issue instructions from two different threads simultaneously, but they share most of the resources required for actually executing those instructions. The end goal of SMT is also to improve overall utilization by reducing idle time, although in the case of CPU&rsquo;s the idle time that they&rsquo;re avoiding mainly comes from stalls that occur during cache misses or page faults. <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>  If we squint like we did in Part 2 and look at our command buffer streams as instruction streams, then the analogy holds fairly well. From this point of view it&rsquo;s possible to think of a GPU as a kind of meta-processor, where the GPU executes various high-level instructions that in turn launch threads consisting of lower-level instructions!</p>
<h2 id="syncing-across-queues">Syncing Across Queues</h2>
<p>In our hypothetical scenario, Chain 1 and Chain 2 were considered to be completely independent: they didn&rsquo;t have to know or care about each other at all. Those chains may have been from different sub-systems in a game, or from two completely different programs sharing a single GPU. However it&rsquo;s fairly common for a single application to have two different chains of dispatches that start from a single point, and whose final results are needed by another single dispatch. As a more concrete example, let&rsquo;s return to the post-processing chain that we discussed at the beginning of the article. Let&rsquo;s say that after we finish rendering our main pass to a render target, we have two different post-processing operations that need to happen: bloom and depth of field (DOF). We already discussed how the bloom pass consists of scaling and blur passes that are all dependent on the previous step, and the DOF pass works in a very similar way. After both of these passes continue, we want to have a single shader that samples the bloom and DOF output, composites them onto the screen, and then tone maps from HDR-&gt;SDR to get the final result. If we were to build a dependency graph for these operations, it would look something like this:</p>
<p><img src="/images/converted/breaking-down-barriers-part-3-multiple-command-processors/bloom_dof_taskgraph.png" alt="Bloom_DOF_TaskGraph"></p>
<p>From the graph it&rsquo;s clear that the bloom/DOF chains are independent while they&rsquo;re executing, but they need to be synchronized at the start and end of the chains to make sure that everything works correctly. This implies that if we were to run the two chains as two separate command buffers submitted to the dual front-ends of the MJP-4000, we would need some kind of cross-queue synchronization in order to ensure correct results. Conceptually this is similar to our need for a barrier to ensure that all threads of a dispatch complete executing a dependent dispatch, except in this case we need to ensure that multiple <em>chains</em> have completed. If we were to use such a barrier to synchronize our post-processing chain, it would look something like this:</p>
<p><img src="/images/converted/breaking-down-barriers-part-3-multiple-command-processors/bloom_dof_combined2.png" alt="Bloom_DOF_Combined2"></p>
<p>There are a few ways that this kind of cross-queue barrier could be implemented on a GPU, but the MJP-4000 takes a simple approach by re-purposing the SIGNAL_POST_SHADER and WAIT_SIGNAL instructions. Both command processors can access the same set of labels (indicated by the labels area being shared on the logical layout diagram), which gives them the basic functionality needed to wait for each other when necessary. For our bloom/DOF case we could have the DOF chain write a label when its finished, and the main command stream could wait on that label before continuing on to the tone mapping dispatch.</p>
<h2 id="next-up">Next Up</h2>
<p>In <a href="../breaking-down-barriers-part-4-gpu-preemption/">Part 4</a>, we&rsquo;ll discuss how having multiple command processors can be useful for scenarios where multiple applications are sharing the same GPU.</p>
<hr>
<h2 id="comments">Comments:</h2>
<h4 id="heading"></h4>
<p><a href="%22pursue_zhao@163.com%22"></a> - <time datetime="2018-12-12 19:51:44">Dec 3, 2018</time></p>
<p>Hi, there is a niggle that for the figure of MJP-4000 work flow, at 608 cy, the threads remaining of C should be 20, not 28, and the following illustration may not be right.</p>
<hr />
####
[]( "") - <time datetime="2018-06-25 21:51:51">Jun 1, 2018</time>
<p>dude, is it based on documented source or Plato&rsquo;s Cave?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2019-01-09 13:57:47">Jan 3, 2019</time>
<p>Thank you for explaining that, Apollo. What you&rsquo;re describing is exactly what I was trying to show in the diagram, but as you pointed out it&rsquo;s doing a poor job of conveying that since it&rsquo;s not actually showing where different commands are processed by the command processor. I&rsquo;m going to re-visit that diagram, and re-work it to make it (hopefully) easier to understand.</p>
<hr />
####
[Apollo](http://aiellis.com "apolloiellis@gmail.com") - <time datetime="2019-01-09 06:18:56">Jan 3, 2019</time>
<p>Anonymous had a good question. The second downsample has to wait for the WAITSIGNAL, since it is dependent on the first downsample writes. It can’t just run. So the graph wouldn’t look like this, unless the blue bar is the WAITSIGNAL, in which case the long dispatch would have to finish first, as he said, before another POSTSHADER signal can be used. Also in this case the diagram is not showing the POSTSHADER signal which must have been at the beginning of the dispatch since, the long dispatch ran immediately i.e at the beginning.</p>
<hr />
####
[]( "pursue_zhao@163.com") - <time datetime="2018-12-12 18:48:46">Dec 3, 2018</time>
<p>Hi MJP, I understand why now. For other guys may have the same question, I would like to post my answer here. The small blue barrier is split barrier. The first downscale is before SIGNAL_POST_SHADER and long dispatch and the second downscale is before WAIT_SIGNAL, so that the previous downscale and long dispatch can overlap and save time. But if another split barrier is added between downscale and Blur H, then its SIGNAL_POST_SHADER will wait long dispatch to complete coz long dispatch is previously-queued, so that this split barrier will be the same with a FLUSH.</p>
<hr />
####
[]( "pursue_zhao@163.com") - <time datetime="2018-12-12 18:10:02">Dec 3, 2018</time>
<p>Hi, thanks so much for your nice sharing. You mentioned in the bloom example that split barrier could only be used once if SIGNAL_POST_SHADER is limited to signaling after all previous-queued threads have completed. My question is that does the small blue barrier mean SIGNAL_POST_SHADER, and why it can be used only once ?</p>
<hr />
####
[]( "") - <time datetime="2019-05-21 07:23:16">May 2, 2019</time>
<p>Thanks for the great series here! This clause is missing its end: &ldquo;There are a few ways that this kind of cross-queue barrier,&rdquo;</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2019-05-21 11:36:05">May 2, 2019</time>
<p>Thank you for pointing that out!</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 1: What&#8217;s a Barrier? &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2018/03/06/breaking-down-barriers-part-1-whats-a-barrier/ "") - <time datetime="2019-03-05 13:44:30">Mar 2, 2019</time>
<p>[…] 1 – What’s a Barrier? Part 2 – Synchronizing GPU Threads Part 3 – Multiple Command Processors Part 4 – GPU Preemption Part 5 – Back To The Real World Part 6 – Experimenting […]</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 2: Synchronizing GPU Threads &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2018/04/01/breaking-down-barriers-part-2-synchronizing-gpu-threads/ "") - <time datetime="2019-03-05 13:44:57">Mar 2, 2019</time>
<p>[…] 1 – What’s a Barrier? Part 2 – Synchronizing GPU Threads Part 3 – Multiple Command Processors Part 4 – GPU Preemption Part 5 – Back To The Real World Part 6 – Experimenting […]</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>This sort of scenario is usually what game developers are referring to when they say that they were able to make something &ldquo;free&rdquo; by using &ldquo;async compute&rdquo;: they&rsquo;re basically saying that there was enough idle execution units to absorb an entire dispatch or sequence of dispatches. But we&rsquo;ll talk about this more in a future article.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>GPU&rsquo;s also have their own ways of dealing with the high latency of memory access, one of which basically involves over-committing threads to shader cores and then cycling through them fairly quickly.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
		</item>
		
		<item>
			<title>Breaking Down Barriers - Part 2: Synchronizing GPU Threads</title>
			<link>https://therealmjp.github.io/posts/breaking-down-barriers-part-2-synchronizing-gpu-threads/</link>
			<pubDate>Mon, 02 Apr 2018 06:29:17 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/breaking-down-barriers-part-2-synchronizing-gpu-threads/</guid>
			<description>This is Part 2 of a series about GPU synchronization and preemption. You can find the other articles here:
Part 1 - What&amp;rsquo;s a Barrier?
Part 2 - Synchronizing GPU Threads
Part 3 - Multiple Command Processors
Part 4 - GPU Preemption
Part 5 - Back To The Real World
Part 6 - Experimenting With Overlap and Preemption
Welcome to part 2 of the series! In the previous article, I explained the basics of what a barrier is, and talked about the various reasons for why you need to use a barrier on a GPU.</description>
			<content:encoded><![CDATA[



  


<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
<script src="/js/load-photoswipe.js"></script>


<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/photoswipe.min.css" integrity="sha256-sCl5PUOGMLfFYctzDW3MtRib0ctyUvI9Qsmq2wXOeBY=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/default-skin/default-skin.min.css" integrity="sha256-BFeI1V+Vh1Rk37wswuOYn5lsTcaU96hGaI7OUVCLjPc=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/photoswipe.min.js" integrity="sha256-UplRCs9v4KXVJvVY+p+RSo5Q4ilAUXh7kpjyIP5odyc=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/photoswipe-ui-default.min.js" integrity="sha256-PWHOlUzc96pMc8ThwRIXPn8yH4NOLu42RQ0b9SpnpFk=" crossorigin="anonymous"></script>


<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

<div class="pswp__bg"></div>

<div class="pswp__scroll-wrap">
    
    <div class="pswp__container">
      <div class="pswp__item"></div>
      <div class="pswp__item"></div>
      <div class="pswp__item"></div>
    </div>
    
    <div class="pswp__ui pswp__ui--hidden">
    <div class="pswp__top-bar">
      
      <div class="pswp__counter"></div>
      <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
      <button class="pswp__button pswp__button--share" title="Share"></button>
      <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
      <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
      
      
      <div class="pswp__preloader">
        <div class="pswp__preloader__icn">
          <div class="pswp__preloader__cut">
            <div class="pswp__preloader__donut"></div>
          </div>
        </div>
      </div>
    </div>
    <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
      <div class="pswp__share-tooltip"></div>
    </div>
    <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
    </button>
    <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
    </button>
    <div class="pswp__caption">
      <div class="pswp__caption__center"></div>
    </div>
    </div>
    </div>
</div>

<p><em>This is Part 2 of a series about GPU synchronization and preemption. You can find the other articles here:</em></p>
<p><a href="../breaking-down-barriers-part-1-whats-a-barrier/">Part 1 - What&rsquo;s a Barrier?</a><br>
<a href="../breaking-down-barriers-part-2-synchronizing-gpu-threads/">Part 2 - Synchronizing GPU Threads</a><br>
<a href="../breaking-down-barriers-part-3-multiple-command-processors/">Part 3 - Multiple Command Processors</a><br>
<a href="../breaking-down-barriers-part-4-gpu-preemption/">Part 4 - GPU Preemption</a><br>
<a href="../breaking-down-barriers-part-5-back-to-the-real-world/">Part 5 - Back To The Real World</a><br>
<a href="../breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/">Part 6 - Experimenting With Overlap and Preemption</a><br></p>
<p>Welcome to part 2 of the series! In the <a href="../breaking-down-barriers-part-1-whats-a-barrier/">previous article</a>, I explained the basics of what a barrier is, and talked about the various reasons for why you need to use a barrier on a GPU. One such use case that I brought up was to ensure that the many threads kicked off by a Draw or Dispatch call don&rsquo;t overlap with previous Draw/Dispatch&rsquo;s threads when there&rsquo;s some kind of dependency between the two calls. In this article I&rsquo;m going to dive into the specifics of how GPU&rsquo;s handle this kind of synchronization, and also discuss the high-level performance implications of these approaches.</p>
<h2 id="programming-the-mjp-3000">Programming the MJP-3000</h2>
<p>To explain the basics of GPU thread synchronization, I&rsquo;m going to walk through some examples using a completely fictional architecture: the MJP-3000. This made-up GPU is much simpler than real graphics hardware, which will (hopefully) make it easier to demonstrate high-level concepts without getting lost in the weeds. I also don&rsquo;t want to give the impression that what I describe is <em>exactly</em> how real GPU&rsquo;s do things, especially since many of those details aren&rsquo;t publicly available. However the commands and behavior are still loosely based on real-world GPU&rsquo;s, since otherwise the example wouldn&rsquo;t be very useful!</p>
<p>With the prologue out of the way, let&rsquo;s have a look at the amazing feat of engineering that is the MJP-3000:</p>
<p><a href="/images/converted/breaking-down-barriers-part-2-synchronizing-gpu-threads/gpu-overview1.png"><img src="/images/converted/breaking-down-barriers-part-2-synchronizing-gpu-threads/gpu-overview1.png" alt="gpu-overview"></a></p>
<p>The interesting parts here are the <strong><a href="https://fgiesen.wordpress.com/2011/07/02/a-trip-through-the-graphics-pipeline-2011-part-2/">command processor</a></strong> on the left, and the <strong>shader cores</strong> in the middle. The command processor is the brains of the operation, and its job is to read commands (the green blocks) from a <strong>command buffer</strong> and coordinate the <strong>shader cores</strong>. The command processor reads commands one at time from the command buffer, always in the exact order they&rsquo;re submitted. When the command processor encounters the appropriate commands, it can add a group of threads to the <strong>thread queue</strong> immediately to the right of the command processor. The 16 shader cores pull threads from this queue in a first-in first-out (FIFO) scheme, after which the shader program for that thread is actually executed on the shader core. The cores are all identical, and completely independent of each other. This means that together they can simultaneously run 16 threads of the same shader program, or they can each run a thread from a completely different program. The shader cores can also read or write to arbitrary locations in device memory, which is on the right. Since the cores are independent and can all access memory, you can think of the array like a 16-core CPU. The major difference is that unlike a CPU they can&rsquo;t tell themselves what to do, since they instead rely on the command processor to enqueue work for them. The <strong>Current Cycle Count</strong> in the top-left corner shows how many GPU cycles have executed for a particular example, which will help us keep track of how long it took for a particular example to complete execution.</p>
<p>For some reason, the designers of the MJP-3000 decided that their hardware could only run compute shaders. I suppose they felt that it would make things a lot simpler to only focus on the one shader stage that doesn&rsquo;t rely on a complicated rasterization pipeline. Because of that, the command processor only has 1 command that actually kicks off threads to run on the shader cores: <strong>DISPATCH</strong>. The DISPATCH command specifies two things: how many threads need to run, and what shader program should be executed. When a DISPATCH command is encountered by the command processor, the threads from that dispatch are immediately placed in the thread queue, where they are grabbed by waiting shader cores. Since there are 16 cores, only 16 threads can be executing at any given time. Any threads that aren&rsquo;t running on the shader cores stay in the thread queue until a core finishes a different thread and pulls the waiting thread out of the queue. The command processor can parse a DISPATCH command and enqueue its threads in 1 cycle, and the shader cores can dequeue a thread from the thread queue in 1 cycle.</p>
<h2 id="dispatches-and-flushes">Dispatches and Flushes</h2>
<p>Let&rsquo;s now try a simple example where we dispatch 32 threads that each write something to a separate element of  a buffer located in device memory. This dispatch will run shader program &ldquo;A&rdquo;, which takes 100 cycles to complete. So with 16 cores we would expect the whole dispatch to take around 200 cycles from start to end. Let&rsquo;s go through the steps:</p>

<link rel="stylesheet" href="/css/hugo-easy-gallery.css" />

<div class="gallery caption-position-bottom caption-effect-None hover-effect-slideup hover-transition" itemscope itemtype="http://schema.org/ImageGallery">
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/single_dispatch/00.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/single_dispatch/00.png" alt="0th" />
				    </div>
			      <figcaption>
		          <p>0th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/single_dispatch/00.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/single_dispatch/01.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/single_dispatch/01.png" alt="1st" />
				    </div>
			      <figcaption>
		          <p>1st</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/single_dispatch/01.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/single_dispatch/02.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/single_dispatch/02.png" alt="2nd" />
				    </div>
			      <figcaption>
		          <p>2nd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/single_dispatch/02.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/single_dispatch/03.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/single_dispatch/03.png" alt="3rd" />
				    </div>
			      <figcaption>
		          <p>3rd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/single_dispatch/03.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/single_dispatch/04.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/single_dispatch/04.png" alt="4th" />
				    </div>
			      <figcaption>
		          <p>4th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/single_dispatch/04.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/single_dispatch/05.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/single_dispatch/05.png" alt="5th" />
				    </div>
			      <figcaption>
		          <p>5th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/single_dispatch/05.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
</div>

<p>In the first step, the command processor encounters a DISPATCH command in the command buffer that requests 32 threads of program A. 1 cycle later, the command processor has enqueued the 32 requested threads in the thread queue. 1 cycle after that, the 16 shader cores have each picked up a thread of program A and have started executing them. Meanwhile, 16 threads are left in the queue. 100 cycles later the first batch of threads have completed, and their result is in memory. 1 cycle after that we&rsquo;re at the 103 cycle count, and the second batch of 16 threads are pulled from the now-empty queue to start executing on the shader cores. Finally after a total of 203 cycles, the threads are all finished and their results are in memory.</p>
<p>Now that we understand the basics of how this GPU works, let&rsquo;s introduce some synchronization. As we already know from the previous article, synchronization implies that we&rsquo;re going to somehow wait for all of our threads to hit a certain point before continuing. On a GPU where you&rsquo;re constantly spinning up lots of new threads, this actually translates into something more like &ldquo;wait for all of the threads from one group to finish before the threads from a second group start executing&rdquo;. The common case where we&rsquo;ll need to do this is where one dispatch needs to read the results that were written out by another dispatch. So for instance, say we run 24 threads of program A that collectively write their results to 24 elements of a buffer. After program A completes that we want to run 24 threads of program B, which will then read those 24 elements from the original output buffer and use them to compute new results written into a different buffer. If we were to try to do this by simply putting two DISPATCH commands in our command buffer, it would go something like this (program A is red, and program B is green):</p>



<div class="gallery caption-position-bottom caption-effect-None hover-effect-slideup hover-transition" itemscope itemtype="http://schema.org/ImageGallery">
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/00.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/00.png" alt="0th" />
				    </div>
			      <figcaption>
		          <p>0th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/00.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/01.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/01.png" alt="1st" />
				    </div>
			      <figcaption>
		          <p>1st</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/01.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/02.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/02.png" alt="2nd" />
				    </div>
			      <figcaption>
		          <p>2nd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/02.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/03.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/03.png" alt="3rd" />
				    </div>
			      <figcaption>
		          <p>3rd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/03.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/04.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/04.png" alt="4th" />
				    </div>
			      <figcaption>
		          <p>4th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/dispatch_overlap/04.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
</div>

<p>Take a look at the the third step: since dispatch A wasn&rsquo;t a multiple of 16, the bottom 8 shader cores pulled from dispatch B to keep the cores from going idle. This caused the two dispatches to <em>overlap</em>, meaning that the end of dispatch A was still executing while the start of dispatch B was simultaneously executing. This is actually really bad for our case, because we now have a race condition: the threads of dispatch B might read from dispatch A&rsquo;s output buffer before the threads of dispatch A have finished! Without knowing the specifics of which memory is accessed by programs A and B and how exactly the threads execute on the GPU, we have no choice but to insert a sync point between the two dispatches. This sync point will need to cause the command processor to wait until all threads of dispatch A run to completion before processing dispatch B. So let&rsquo;s now introduce a FLUSH command that will do exactly that: when the command processor hits the flush, it waits for all shader cores to become idle before processing any further commands. The term &ldquo;flush&rdquo; is common for this sort of operation because it implies that it will &ldquo;flush out&rdquo; all pending work that&rsquo;s waiting to execute. Let&rsquo;s now try the same scenario again, this time using a flush to synchronize:</p>



<div class="gallery caption-position-bottom caption-effect-None hover-effect-slideup hover-transition" itemscope itemtype="http://schema.org/ImageGallery">
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/00.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/00.png" alt="0th" />
				    </div>
			      <figcaption>
		          <p>0th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/00.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/01.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/01.png" alt="1st" />
				    </div>
			      <figcaption>
		          <p>1st</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/01.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/02.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/02.png" alt="2nd" />
				    </div>
			      <figcaption>
		          <p>2nd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/02.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/03.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/03.png" alt="3rd" />
				    </div>
			      <figcaption>
		          <p>3rd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/03.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/04.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/04.png" alt="4th" />
				    </div>
			      <figcaption>
		          <p>4th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/04.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/05.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/05.png" alt="5th" />
				    </div>
			      <figcaption>
		          <p>5th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/05.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/06.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/06.png" alt="6th" />
				    </div>
			      <figcaption>
		          <p>6th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/06.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/07.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/07.png" alt="7th" />
				    </div>
			      <figcaption>
		          <p>7th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_between_dispatches/07.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
</div>

<p>Notice how the command processor hits the FLUSH command, and then stops reading commands until dispatch A is completely finished and the thread queue is empty. This ensures that dispatch B never overlaps with dispatch A, which means it will be safe for any thread in dispatch B to access any result that that was output by dispatch A. This is pretty much exactly what I was talking about in part 1 when I mentioned the need for barriers to prevent dependent Draw/Dispatch calls from overlapping. In fact, you can usually expect something like a FLUSH to happen on current GPU&rsquo;s if you issued dispatch A, issued a barrier to transition the output buffer from a write state to a read state, and then issued dispatch B (it&rsquo;s also similar to what you would get in response to issuing a D3D12_RESOURCE_UAV_BARRIER in D3D12, since that also implies waiting for all pending writes to finish). Hopefully this example makes it even more clear as to why a barrier is necessary for this sort of data dependency, and why results could be wrong if the barrier is omitted.</p>
<p>It&rsquo;s also very important to note that in this case the flush/barrier was not free from a performance point of view: our total processing time for both dispatches went from 304 cycles to 406 cycles. That&rsquo;s a 25% increase! The reason for this should be intuitive: with the flush between dispatches, we now have more idle shader cores during the tail end of both dispatches. In fact the increase in processing time is exactly the same as the increase in the amount of idle time: without the flush we had about 0% idle cores over both dispatches, but <em>with</em> the flush our cores were idle about 25% of the time on average. This leads us to a simple conclusion: <strong>the performance cost of a flush is directly tied to the decrease in utilization</strong>. This ultimately means that the relative cost of introducing a thread synchronization barrier will vary depending on the number of threads, how long those threads execute, and how well the threads can fully saturate the available shader cores. We can confirm this with a simple thought experiment: imagine we ran dispatch A and dispatch B with 40 threads each instead of 24. The process would go almost exactly as it did before, except both dispatches would have another &ldquo;phase&rdquo; of 100 cycles where all 16 cores were in-use. Without our barrier the whole process would take about (40 + 40) / 16 = 500 cycles, while with the barrier it would take about 600 cycles. Therefore the relative cost of the barrier is about 16.5% as opposed to the 25% cost when our thread counts were lower.</p>
<p>The other way to look at this is that <strong>removing an unnecessary flush can result in a performance increase that&rsquo;s relative to the amount of idle shader cores</strong>. So if we&rsquo;re syncing between two dispatches and they have no dependency between them, it&rsquo;s most likely a good idea to remove the barrier and let them overlap with each other. For larger dispatches (in terms of thread count) that can saturate the GPU on their own there won&rsquo;t be much benefit, since there won&rsquo;t be much idle time to exploit. However for very small dispatches the difference can be significant. This time let&rsquo;s imagine that dispatch A and B both have 8 threads each. With a flush in between the total time will be about 200 cycles, but with no flush they can perfectly overlap and finish in only 100 cycles! Or as another example, imagine we had another completely independent workload of 8 threads that we&rsquo;ll call dispatch C (and color its threads blue). If we were to overlap it with dispatch A, we could essentially get it for &ldquo;free&rdquo; by utilizing the idle cores:</p>



<div class="gallery caption-position-bottom caption-effect-None hover-effect-slideup hover-transition" itemscope itemtype="http://schema.org/ImageGallery">
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/00.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/00.png" alt="0th" />
				    </div>
			      <figcaption>
		          <p>0th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/00.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/01.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/01.png" alt="1st" />
				    </div>
			      <figcaption>
		          <p>1st</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/01.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/02.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/02.png" alt="2nd" />
				    </div>
			      <figcaption>
		          <p>2nd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/02.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/03.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/03.png" alt="3rd" />
				    </div>
			      <figcaption>
		          <p>3rd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/03.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/04.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/04.png" alt="4th" />
				    </div>
			      <figcaption>
		          <p>4th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/04.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/05.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/05.png" alt="5th" />
				    </div>
			      <figcaption>
		          <p>5th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap/05.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
</div>

<p>If you squint a bit and look at our GPU as if it were a CPU executing instructions instead of a GPU executing commands, then this kind of overlapping of work could be considered a kind of <a href="https://en.wikipedia.org/wiki/Instruction-level_parallelism"><strong>Instruction Level Parallelism</strong></a>. In this case the parallel operations are being explicitly specified in our command stream, making it somewhat similar to how <a href="https://en.wikipedia.org/wiki/Very_long_instruction_word">VLIW</a> architectures work.</p>
<h2 id="waits-and-labels">Waits and Labels</h2>
<p>In the previous example, we were able to basically hide dispatch C in the idle time left by the barrier between dispatch A and dispatch B. But what if dispatch C was very complicated, and took much longer than 100 cycles to complete? Let&rsquo;s re-do the example, except this time dispatch C will execute for 400 cycles instead of 100:</p>



<div class="gallery caption-position-bottom caption-effect-None hover-effect-slideup hover-transition" itemscope itemtype="http://schema.org/ImageGallery">
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/00.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/00.png" alt="0th" />
				    </div>
			      <figcaption>
		          <p>0th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/00.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/01.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/01.png" alt="1st" />
				    </div>
			      <figcaption>
		          <p>1st</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/01.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/02.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/02.png" alt="2nd" />
				    </div>
			      <figcaption>
		          <p>2nd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/02.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/03.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/03.png" alt="3rd" />
				    </div>
			      <figcaption>
		          <p>3rd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/03.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/04.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/04.png" alt="4th" />
				    </div>
			      <figcaption>
		          <p>4th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/04.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/05.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/05.png" alt="5th" />
				    </div>
			      <figcaption>
		          <p>5th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/05.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/06.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/06.png" alt="6th" />
				    </div>
			      <figcaption>
		          <p>6th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/flush_with_overlap_long/06.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
</div>

<p>Things didn&rsquo;t go as well this time around. We still got a bit of overlap between A and C, but that was immediately followed by 300 cycles where half of our shader cores were idle. This happened because our FLUSH command ends up waiting for dispatch C to finish, since the flush works by waiting for the thread queue to become completely empty. We could re-arrange things so that dispatch C gets kicked off <em>after</em> the flush, but this is not ideal either because there would still be a bit of idle time during the tail end of dispatch A, and also a long period of half-idle cores when dispatch C is running.</p>
<p>Lucky for us, there&rsquo;s a new driver update for the MJP-3000 that should be able to help us out. MJP xPerience 3D Nocturnal<strong>™</strong> Driver v5.444.198754 adds support for two new commands that can be parsed and executed by the command processor. The first one is called SIGNAL_POST_SHADER, and the other is called WAIT_SIGNAL. The first command is pretty fancy: it tells the command processor to write a signal value to an address in memory (often called a <em>fence</em> or <em>label</em>) once all shaders have completed. The cool part is that it&rsquo;s a &ldquo;deferred&rdquo; write: the write is actually performed by the thread queue once it determines that all previously-queued threads have run to completion. This allows the command processor to move on to other commands while previous dispatches are still executing. The other command, WAIT_SIGNAL, tells the command processor to stall and wait for a memory address to be signaled. This can be used in conjunction with SIGNAL_POST_SHADER to wait for a particular dispatch to complete, but with the added bonus that the command processor can kick off more work in between those steps. To help visualize this process, let&rsquo;s update the GPU diagram with a new component:</p>
<p><a href="/images/converted/breaking-down-barriers-part-2-synchronizing-gpu-threads/label-overview.png"><img src="/images/converted/breaking-down-barriers-part-2-synchronizing-gpu-threads/label-overview.png" alt="label-overview"></a></p>
<p>Once a SIGNAL_POST_SHADER command is executed, any pending labels will show up as a colored block in a new area under the thread queue. The number on the block shows the current status of the label: &ldquo;0&rdquo; means it hasn&rsquo;t been signaled yet, and &ldquo;1&rdquo; means that it&rsquo;s in the signaled state and any dependent waits will be released.</p>
<p>Let&rsquo;s now try out our new commands with the previous example:</p>



<div class="gallery caption-position-bottom caption-effect-None hover-effect-slideup hover-transition" itemscope itemtype="http://schema.org/ImageGallery">
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/00.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/00.png" alt="0th" />
				    </div>
			      <figcaption>
		          <p>0th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/00.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/01.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/01.png" alt="1st" />
				    </div>
			      <figcaption>
		          <p>1st</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/01.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/02.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/02.png" alt="2nd" />
				    </div>
			      <figcaption>
		          <p>2nd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/02.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/03.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/03.png" alt="3rd" />
				    </div>
			      <figcaption>
		          <p>3rd</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/03.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/04.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/04.png" alt="4th" />
				    </div>
			      <figcaption>
		          <p>4th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/04.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/05.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/05.png" alt="5th" />
				    </div>
			      <figcaption>
		          <p>5th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/05.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/06.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/06.png" alt="6th" />
				    </div>
			      <figcaption>
		          <p>6th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/06.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/07.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/07.png" alt="7th" />
				    </div>
			      <figcaption>
		          <p>7th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/07.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/08.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/08.png" alt="8th" />
				    </div>
			      <figcaption>
		          <p>8th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/08.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
				<div class="box">
				  <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
				    <div class="img" style="background-image: url('https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/09.png');" >
				      <img itemprop="thumbnail" src="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/09.png" alt="9th" />
				    </div>
			      <figcaption>
		          <p>9th</p>
			      </figcaption>
				    <a href="https://therealmjp.github.io/images/converted/barriers/split_barrier_overlap/09.png" itemprop="contentUrl"></a>
				  </figure>
				</div>
</div>

<p>Very nice! By removing the long stall on dispatch C, we&rsquo;ve effectively eliminated all of the idle time and kept the GPU busy for the entire duration of the 3 dispatches. As a result we&rsquo;ve increased our overall throughput: previously the process took about 700 cycles, but now it&rsquo;s down to about 500 cycles. Unfortunately this is still more time than it took to complete when we only had dispatch A and B to worry about, which means the <em>latency</em> for the A-&gt;B job increased by about 100 cycles. But at the same time the latency for dispatch C is is lower than it would be if it weren&rsquo;t overlapped, since it would otherwise need to wait for either A or B to finish before it could start processing.</p>
<p>If the MJP-3000 were being programmed via D3D12 or Vulkan, then this signal/wait behavior is probably what you would hope to see when issuing a <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn899226(v=vs.85).aspx#split_barriers">split barrier</a> (vkCmdSetEvent + vkCmdWaitEvents in Vulkan-ese). Split barriers let you effectively specify 2 different points in a resource&rsquo;s lifetime: the point where you&rsquo;re done using it in its current state (read, write, etc.), and the point where you actually need the resource to be in its new state. By doing this and issuing some work between the begin and end of the barrier, the driver (potentially) has enough information to know that it can overlap the in-between work while it&rsquo;s waiting for the pre-barrier work to finish. So for the example I outlined above, the D3D12 commands might go something like this:</p>
<ul>
<li>Issue Dispatch A which writes to Buffer A</li>
<li>Begin Transition Buffer A from writable -&gt; readable</li>
<li>Issue Dispatch C which writes to Buffer C</li>
<li>End Transition Buffer A from writable -&gt; readable</li>
<li>Issue Dispatch B which writes to Buffer B</li>
</ul>
<p>For real-world GPU&rsquo;s the benefits of split barriers can possibly be even greater than the sync point removal that I demonstrated with my imaginary GPU. As I mentioned in part 1, barriers on GPU&rsquo;s are also responsible for handing things like cache flushes and decompression steps. These things increase the relative cost of a barrier past the simple &ldquo;idle shader core tax&rdquo; that we saw on our imaginary GPU, which gives us even more incentive to try to overlap the barrier with with some non-dependent work. However, the ability to overlap barrier operations with Draws and Dispatches is totally dependent on the specifics of the GPU architecture.</p>
<p>Before we wrap up, I&rsquo;d like to point out that our GPU is still rather limited in terms of how it can overlap different dispatches, even with the new label/wait functionality that we just added. You can only do so much when the command processor is completely tied up every time that you need to wait for a previous dispatch to finish, which really starts to hurt you if you have more complex dependency chains. Later on in part 3 we&rsquo;ll revisit this topic, and look at at how some hardware changes can help us get around these limitations.</p>
<h2 id="next-up">Next Up</h2>
<p>In <a href="../breaking-down-barriers-part-3-multiple-command-processors/">Part 3</a>, I&rsquo;m going to discuss why explicit API&rsquo;s expose multiple queues for submitting command buffers. I&rsquo;ll also show how multiple queues could work on the fictional GPU architecture we&rsquo;ve been using as an example, and discuss some implementations in real-world GPU&rsquo;s.</p>
<hr>
<h2 id="comments">Comments:</h2>
<h4 id="heading"></h4>
<p><a href="%22jorge@iryoku.com%22">IRYOKU</a> - <time datetime="2018-04-13 05:40:38">Apr 5, 2018</time></p>
<p>This was an amazing article, thanks for sharing!</p>
<hr />
####
[Baggers]( "techsnuffle@gmail.com") - <time datetime="2018-04-04 00:35:44">Apr 3, 2018</time>
<p>Once again awesome articles, I can tell I&rsquo;m going to be referring to these for years :) A few tiny niggles - missing link between two shader cores. Row 1 column 3 (0 indexed) - &ldquo;group of treads&rdquo; in first paragraph after first mjp-3000 diagram - &ldquo;dispatch B might read from dispatch B’s output buffer before the threads of dispatch A have finished!&rdquo; should that be &ldquo;might read from dispatch A’s output&rdquo;?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2018-04-04 14:05:10">Apr 3, 2018</time>
<p>Thank you for the kind words, and for pointing out those issues! I&rsquo;ve fixed the two typos, and I&rsquo;ll fix the image one I&rsquo;m on my home PC.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2018-04-03 18:48:55">Apr 2, 2018</time>
<p>Most GPU&rsquo;s will actually have multiple threads &ldquo;sharing&rdquo; a shader core. The hardware scheduler will cycle through those threads at a relatively fast rate compared to CPU&rsquo;s, which allows the hardware to hide the latency of memory operations. The basic idea is that if one thread stalls waiting for memory, the hardware can switch to another thread that can (hopefully) get some work done. The main difference from CPU&rsquo;s is that there&rsquo;s usually a finite limit of how many threads can be committed to a shader core, which is commonly referred to as &ldquo;occupancy&rdquo;. When a CPU switches threads, the current context is saved off to memory, which is usually backed by a very generous cache hierarchy. GPU threads however have *tons* of registers that live in very large register files, and very small caches. This usually makes it impractical to save off those registers to memory for a context switch, especially at the rate at which GPU&rsquo;s switch threads. So instead most GPU&rsquo;s will statically partition a core&rsquo;s register file among all threads currently running on that core, with the number of registers being dependent on the complexity of the shader program being run. So if the core has 256 registers and the program needs 64 registers, then you can have a max 4 of threads committed to a core at any given time. This is why shader authors often try to optimize for register usage, since less registers means higher occupancy and more ability for the hardware to hide latency. This is a good slide deck that covers some of these details: <a href="http://bps10.idav.ucdavis.edu/talks/03-fatahalian">http://bps10.idav.ucdavis.edu/talks/03-fatahalian</a>_gpuArchTeraflop_BPS_SIGGRAPH2010.pdf It also talks about how GPU&rsquo;s actually use SIMD for their shader cores, which is something that I left out of my made-up GPU in order to keep things simpler. But the fact that GPU threads are always executed on SIMD units in groups of 32 or 64 also has a bearing on register usage and occupancy, since it effectively means that your register count can actually be 32x or 64x larger! So for instance if you take AMD&rsquo;s GCN architecture, each CU on the GPU has 4 SIMD units, and each SIMD has a register file with 256 registers. AMD works with &ldquo;wavefronts&rdquo; of 64 threads, which means that each SIMD has 64x256 32-bit registers (64KB). If you take the PS4&rsquo;s GPU with 18 CU&rsquo;s, that adds up to 4.5 MB of registers!</p>
<hr />
####
[Rim]( "remigius@netforge.nl") - <time datetime="2018-04-10 10:42:49">Apr 2, 2018</time>
<p>Another very nice read indeed. I recently followed a course on the computational complexity of scheduling algorithms and this got me wondering, isn&rsquo;t optimizing your command submission and locking schedule really difficult (as in NP-hard) for anything but trivial scenarios? There&rsquo;ll probably be heuristics that are good enough in practice, but I&rsquo;m curious what you think, if you care to comment of course.</p>
<hr />
####
[nikitablack](http://nikitablack.github.io/ "nikita_chernyi@mail.ru") - <time datetime="2018-04-03 01:10:22">Apr 2, 2018</time>
<p>The article is amazing. Thank you for sharing the knowledge in accessible manner. You told that in your amazing MJP-3000 GPU you have 16 cores and hence 16 threads. Does that holds for real-world GPUs that one core can spawn only one thread (on PC we can span infinite number of threads even on a single core).</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 3: Multiple Command Processors &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2018/06/17/breaking-down-barriers-part-3-multiple-command-processors/ "") - <time datetime="2019-03-05 13:45:35">Mar 2, 2019</time>
<p>[…] 1 – What’s a Barrier? Part 2 – Synchronizing GPU Threads Part 3 – Multiple Command Processors Part 4 – GPU Preemption Part 5 – Back To The […]</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 1: What&#8217;s a Barrier? &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2018/03/06/breaking-down-barriers-part-1-whats-a-barrier/ "") - <time datetime="2019-03-05 13:44:28">Mar 2, 2019</time>
<p>[…] 1 – What’s a Barrier? Part 2 – Synchronizing GPU Threads Part 3 – Multiple Command Processors Part 4 – GPU Preemption Part 5 – Back To The […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Breaking Down Barriers - Part 1: What&#39;s a Barrier?</title>
			<link>https://therealmjp.github.io/posts/breaking-down-barriers-part-1-whats-a-barrier/</link>
			<pubDate>Tue, 06 Mar 2018 09:21:34 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/breaking-down-barriers-part-1-whats-a-barrier/</guid>
			<description>This is Part 1 of a series about GPU synchronization and preemption. You can find the other articles here:
Part 1 - What&amp;rsquo;s a Barrier?
Part 2 - Synchronizing GPU Threads
Part 3 - Multiple Command Processors
Part 4 - GPU Preemption
Part 5 - Back To The Real World
Part 6 - Experimenting With Overlap and Preemption
If you&amp;rsquo;ve done any amount of D3D12 or Vulkan programming, then you&amp;rsquo;ve probably spent a good bit of that time grappling with barriers.</description>
			<content:encoded><![CDATA[<p><em>This is Part 1 of a series about GPU synchronization and preemption. You can find the other articles here:</em></p>
<p><a href="../breaking-down-barriers-part-1-whats-a-barrier/">Part 1 - What&rsquo;s a Barrier?</a><br>
<a href="../breaking-down-barriers-part-2-synchronizing-gpu-threads/">Part 2 - Synchronizing GPU Threads</a><br>
<a href="../breaking-down-barriers-part-3-multiple-command-processors/">Part 3 - Multiple Command Processors</a><br>
<a href="../breaking-down-barriers-part-4-gpu-preemption/">Part 4 - GPU Preemption</a><br>
<a href="../breaking-down-barriers-part-5-back-to-the-real-world/">Part 5 - Back To The Real World</a><br>
<a href="../breaking-down-barriers-part-6-experimenting-with-overlap-and-preemption/">Part 6 - Experimenting With Overlap and Preemption</a><br></p>
<p>If you&rsquo;ve done any amount of D3D12 or Vulkan programming, then you&rsquo;ve probably spent a good bit of that time grappling with barriers. They can be pretty tricky to get right: the validation layers seem to be always pointing out new issues whenever you change some rendering code, or when a new version of the validation layer comes in through Windows Update. And on top of that the IHV&rsquo;s keep telling you that you need to be really careful and how you use barriers if you want GPU performance to meet or exceed what you get from D3D11, which is an additional challenge on top of simply achieving artifact-free results.</p>
<p>So what gives? Why the heck do we even need barriers in the first place, and why do things go so wrong if we misuse them? If you&rsquo;ve done significant console programming or are already familiar with the lower-level details of modern GPU&rsquo;s, then you probably know the answer to these questions, in which case this article isn&rsquo;t really for you. But if you don&rsquo;t have the benefit of that experience, then I&rsquo;m going to do my best to give you a better understanding of what&rsquo;s going on behind the scenes when you issue a barrier.</p>
<h2 id="a-high-barrier-to-entry">A High Barrier To Entry</h2>
<p>Like almost everything else in programming and computers, the term &ldquo;barrier&rdquo; is already a bit overloaded. In some contexts, a &ldquo;<a href="https://en.wikipedia.org/wiki/Barrier_(computer_science)">barrier</a>&rdquo; is a <a href="https://en.wikipedia.org/wiki/Synchronization_(computer_science)">synchronization point</a> where a bunch of threads all have to stop once they reach a particular point in the code that they&rsquo;re running. In this case you can think of the barrier as an immovable wall: the threads are all running, but stop dead in their tracks when they &ldquo;hit&rdquo; the barrier:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">ThreadFunction</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DoStuff</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Wait for all threads to hit the barrier
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">barrier</span><span class="p">.</span><span class="n">Wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// We now know that all threads called DoStuff()
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>This sort of thing is helpful when you want to know when a bunch of threads have all finished executing their tasks (the &ldquo;join&rdquo; in the <a href="https://en.wikipedia.org/wiki/Fork%E2%80%93join_model">fork-join model</a>), or when you have threads that need to read other&rsquo;s results. As a programmer you can implement a thread barrier by &ldquo;spinning&rdquo; (looping until a conditions met) on a variable updated via atomic operations, or by using <a href="http://cs.umw.edu/~finlayson/class/fall14/cpsc425/notes/07-conditions.html">semaphores and condition variables</a> when you want your threads to go to sleep while they&rsquo;re waiting.</p>
<p>In other contexts the term &ldquo;barrier&rdquo; will refer to a <a href="https://en.wikipedia.org/wiki/Memory_barrier">memory barrier</a> (also known as a &ldquo;fence&rdquo;), particularly if you&rsquo;ve somehow fallen down the rabbit hole of <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ee418650.aspx">lock-free programming</a>. In these scenarios you&rsquo;re usually dealing with reordering of memory operations that&rsquo;s done by the compiler and/or the processor itself, which can really throw a wrench in the works when you have multiple processors communicating through shared memory. Memory barriers help you out by letting you force memory operations to complete either before or after the barrier , effectively keeping them on one &ldquo;side&rdquo; of the fence. In C++ you can insert these into your code using platform-specific macros like <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms684208(v=vs.85).aspx">MemoryBarrier</a> in the Windows API, or through the cross-platform <a href="http://en.cppreference.com/w/cpp/atomic/atomic_thread_fence">std::atomic_thread_fence</a>. A common use case might look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl">    <span class="c1">// DataIsReady and Data are written to
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// by a different thread
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span><span class="p">(</span><span class="n">DataIsReady</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Make sure that reading Data happens
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// *after* reading from DataIsReady
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">MemoryBarrier</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">DoSomething</span><span class="p">(</span><span class="n">Data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>These two meanings of the term &ldquo;barrier&rdquo; have different specifics, but they also have something in common: they&rsquo;re mostly used when one thing is producing a result and another thing needs to read that result. Another way of saying that is that one task has a <strong>dependency</strong> on a different task. Dependencies happen all of the time when writing code: you might have one line of code that adds two numbers to compute an offset, and the very next line of code will use that offset to read from an array. However you often don&rsquo;t need to really be aware of this, because the compiler can <strong>track</strong> those dependencies for you and make sure that it produces code to give you the right results. Manually inserting barriers usually doesn&rsquo;t come in until you do things in a way that the compiler can&rsquo;t see how the data is going to be written to and read from at compile-time. This commonly happens due to multiple threads accessing the same data, but it can also happen in other weird cases (like when another piece of hardware writes to memory). Either way, using the appropriate barrier will make sure that the results will be <strong>visible</strong> to dependent steps, ensuring that they don&rsquo;t end up reading the wrong data.</p>
<p>Since compilers can&rsquo;t handle dependencies for you automatically when you&rsquo;re doing multithreaded CPU programming, you&rsquo;ll often spend a lot of time figuring how to express and resolve dependencies between your multithreaded tasks. In these situations it&rsquo;s common to build a <a href="https://en.wikipedia.org/wiki/Dependency_graph">dependency graph</a> indicating which tasks depend on the results of other tasks. That graph can help you decide what order to execute your tasks, and when you need to stick a sync point (barrier) between two tasks (or groups of tasks) so that the earlier task completely finishes before the second task starts executing. You&rsquo;ll often see these graphs drawn out as a tree-like diagram, like in this easy-to-understand example from <a href="https://software.intel.com/en-us/node/517349">Intel&rsquo;s documentation for Thread Building Blocks</a>:</p>
<p><img src="/images/converted/breaking-down-barriers-part-1-whats-a-barrier/tbb_dependency_graph.jpg" alt="TBB_Dependency_Graph"></p>
<p>Even if you&rsquo;ve never done task-oriented multithreaded programming, this diagram makes the concept of dependencies pretty clear: you can&rsquo;t put the peanut better on the bread before you&rsquo;ve gotten the bread! At a high level this determines the order of your tasks (bread before peanut butter), but it also subtly implies something that would be obvious to you if you were doing this in real life: you can&rsquo;t start applying your peanut butter until you&rsquo;ve gotten out your slices of bread from the cabinet. If you were doing this in real life by yourself, you wouldn&rsquo;t even think about this. There&rsquo;s only 1 of you, so you would just go through each step one at a time. But we were originally discussing  this in the context of multithreading, which means that we&rsquo;re talking about trying to run different tasks on different cores in parallel. Without properly waiting you could end up with the peanut butter task running at the same time as the bread step, and that&rsquo;s obviously not good!</p>
<p><img src="/images/converted/breaking-down-barriers-part-1-whats-a-barrier/overlapped_tasks.png" alt="Overlapped_Tasks"></p>
<p>To avoid these kinds of issues, task schedulers like TBB give you mechanisms that force a task (or group of tasks) to wait until a prior task (or group of tasks) completely finishes executing. Like I mentioned earlier, you could call this mechanism a barrier, or a sync point:</p>
<p><img src="/images/converted/breaking-down-barriers-part-1-whats-a-barrier/overlapped_tasks_fixed.png" alt="Overlapped_Tasks_Fixed"></p>
<p>This sort of thing is pretty easy to implement on modern PC CPU&rsquo;s, since you have a lot of flexibility as well as some powerful tools at your disposal: atomic operations, synchronization primitives, OS-supplied condition variables, and so on.</p>
<h2 id="back-to-gpu-land">Back To GPU Land</h2>
<p>So we&rsquo;ve covered the basics of what a barrier is, but I still haven&rsquo;t explained why we have them in API&rsquo;s designed for talking to the GPU. After all, issuing Draw and Dispatch calls isn&rsquo;t really the same as scheduling a bunch of parallel tasks to execute on separate cores, right? I mean, if you look at a D3D11 program&rsquo;s sequence of API calls it looks pretty damn serial:</p>
<p><img src="/images/converted/breaking-down-barriers-part-1-whats-a-barrier/renderdoc_d3d11.png" alt="Renderdoc_D3D11"></p>
<p>If you&rsquo;re used to dealing with GPU&rsquo;s through an API like this, you&rsquo;d be forgiven for thinking that the GPU just goes through each command one at a time, in the order you submit them. And while this may have been true a long time ago, the reality is actually quite a bit more complicated on modern GPU&rsquo;s. To show you what I&rsquo;m talking about, let&rsquo;s take a look at what my <a href="https://github.com/TheRealMJP/DeferredTexturing">Deferred Texturing sample</a> looks like when I take a capture with AMD&rsquo;s awesome profiling tool, <a href="https://gpuopen.com/gaming-product/radeon-gpu-profiler-rgp/">Radeon GPU Profiler</a>:</p>
<p><a href="/images/converted/breaking-down-barriers-part-1-whats-a-barrier/rgp_bindlessdeferred.png"><img src="/images/converted/breaking-down-barriers-part-1-whats-a-barrier/rgp_bindlessdeferred.png" alt="RGP_BindlessDeferred"></a></p>
<p>This snippet is showing just a portion of a frame, specifically the part where all of the scene geometry is rasterized into the G-Buffer. The left-hand side shows the draw call, while the blue bars to the right show when the draw call actually starts and stops executing. And what do you know, there&rsquo;s a whole lot of overlapping going on there! You can see the same thing shown a bit differently if you fire up <a href="https://blogs.msdn.microsoft.com/pix/download/">PIX for Windows</a>:</p>
<p><a href="/images/converted/breaking-down-barriers-part-1-whats-a-barrier/pix_timeline.png"><img src="/images/converted/breaking-down-barriers-part-1-whats-a-barrier/pix_timeline.png" alt="PIX_Timeline"></a></p>
<p>This is a snippet from PIX&rsquo;s timeline view, which is also showing the execution time for the same sequence of draw calls (this time captured on my GTX 1070, whereas the earlier RGP capture was done on my significantly less beefy RX 460). You can see the same pattern: the draws start executing roughly in submission order, but they overlap all over the place. In some cases, draws will start and finish before an earlier draw completes!</p>
<p>If you know even a little bit about GPU&rsquo;s this shouldn&rsquo;t be <em>completely</em> surprising. After all, everyone knows that a GPU is mostly made up of hundreds or thousands of what the IHV&rsquo;s like to call &ldquo;<strong>shader cores</strong>&rdquo;, and those shader cores all work together to solve &ldquo;embarrassingly parallel&rdquo; problems. These days the bulk of work done to process a draw (and pretty much all of the work done to process a dispatch) is performed on these guys, which run the shader programs compiled from our HLSL/GLSL/MetalSL code. Surely it makes sense to have the shader cores process the several thousand vertices from a single draw call in parallel, and to do the same with the thousands or millions of pixels that result from rasterizing the triangles. But does it really make sense to let multiple draw calls or dispatches bleed over into one another so that your actual high-level commands are also executing in parallel?</p>
<p>The correct answer is, &ldquo;yes, absolutely!&rdquo; In fact, hardware designers have put in quite a bit of effort over the years to make sure that their GPU&rsquo;s can do this even if there are some state changes in between the draws. Desktop GPU&rsquo;s have even engineered their ROP&rsquo;s (the units that are responsible for taking the output of a pixel shader and actually writing it to memory) so that they can <a href="https://fgiesen.wordpress.com/2011/07/12/a-trip-through-the-graphics-pipeline-2011-part-9/">resolve blending operations even if the pixel shaders didn&rsquo;t finish in draw order</a>! Doing it this way helps avoid having idle shader cores, which in turn gives you better throughput. Don&rsquo;t worry if this doesn&rsquo;t completely make sense right now, as I&rsquo;m going to walk through some examples in a future post that explain why this is the case. But for now, just take my word for it that allowing draws and dispatches to overlap generally leads to higher throughput.</p>
<p>If a GPU&rsquo;s threads from a draw/dispatch can overlap with other, that means that the GPU needs a way to <em>prevent</em> that from happening in cases where there&rsquo;s a data dependency between two tasks. When this happens, it makes sense do what we do on a CPU, and insert something roughly similar to a thread barrier in order to let us know when a group of threads have all finished their work. In practice GPU&rsquo;s tend to do this in a very coarse manner, such as waiting for all outstanding compute shader threads to finish before starting up the next dispatch. This can be called a &ldquo;flush&rdquo;, or a &ldquo;wait for idle&rdquo;, since the GPU will wait for all threads to &ldquo;drain&rdquo; before moving on. But we&rsquo;ll get into that in more detail in the next article.</p>
<h2 id="caches-are-hard">Caches are Hard</h2>
<p>Hopefully by now it&rsquo;s clear that there&rsquo;s at least one reason for barriers on GPU&rsquo;s: to keep shader threads from overlapping when there&rsquo;s a data dependency. This is really the same scenario with the peanut butter and the bread that we laid out earlier when talking about CPU threads, except with the core count cranked up to the thousands. But unfortunately things get a bit more complicated when we&rsquo;re talking about GPU&rsquo;s as opposed to CPU&rsquo;s.</p>
<p>Let&rsquo;s say that you start up a group of threads running on a PC CPU that write a bunch of data to individual buffers, insert a thread barrier to wait until those threads are finished, and then kick off a second group of threads that reads the output data of the first group of threads. As long as you make sure that you have the right memory/compiler barriers in place to ensure that the second tasks&rsquo;s read operations don&rsquo;t happen too early (and often you get this by default from using OS synchronization primitives or atomic operations), you don&rsquo;t need to care about getting correct results in the presence of a cache hierarchy.  This is because the caches on an x86 core (usually each core has its own individual L1 cache, with a shared L2 and possibly L3 cache) are <a href="https://en.wikipedia.org/wiki/Cache_coherence">coherent</a>, which means that they stay &ldquo;up to date&rdquo; with each other as they access different memory addresses. The details of how they achieve this miraculous feat are <a href="https://fgiesen.wordpress.com/2014/07/07/cache-coherency/">quite complicated</a>, but as programmers we&rsquo;re usually allowed to remain blissfully ignorant of the internal gymnastics being performed by the hardware.</p>
<p>Things are not so simple for the poor folks that write drivers for a GPU. For various reasons, some of them dating back to their legacy as devices that weren&rsquo;t used for general-purpose computing like they are now, GPU&rsquo;s tend to have a bunch of caches that aren&rsquo;t always organized into a strict hierarchy. The details aren&rsquo;t always public, but AMD tends to have quite a bit of public information available about their GPU&rsquo;s that we can learn from. Here&rsquo;s a diagram from slide 50 of <a href="http://32ipi028l5q82yhj72224m8j.wpengine.netdna-cdn.com/wp-content/uploads/2016/03/GDC_2016_D3D12_Right_On_Queue_final.pdf">this presentation</a>:</p>
<p><a href="/images/converted/breaking-down-barriers-part-1-whats-a-barrier/amd_caches.png"><img src="/images/converted/breaking-down-barriers-part-1-whats-a-barrier/amd_caches.png" alt="AMD_Caches"></a></p>
<p>This looks quite different from a CPU&rsquo;s cache hiearchy! We have two things with L1 that go through L2 the way that you expect they would, but then there&rsquo;s color and depth caches that bypass L2 and go right to memory! And there&rsquo;s a DMA engine that doesn&rsquo;t go through cache at all! The diagram here is also a bit misleading, since in reality there&rsquo;s an L1 texture cache on every compute unit (CU), and there can be dozens of compute units on the larger video cards. There&rsquo;s also multiple instruction caches and scalar data L1&rsquo;s, with one of these shared between up to 4 CU&rsquo;s. There&rsquo;s lots of details in <a href="https://www.amd.com/Documents/GCN_Architecture_whitepaper.pdf">this GCN whitepaper</a>, which explains how the various caches work, and also how direct memory writes from shaders (AKA writes to UAV&rsquo;s) go through their local L1 and <em>eventually</em> propagate to L2.</p>
<p>As a consequence of having all of these caches without a strict hierarchy, the caches can sometimes get out of sync with each other. As the GCN whitepaper describes, the L1 caches on the shader units aren&rsquo;t coherent with each other until the writes reach L2. This means that if one dispatch writes to a buffer and another reads from it, the CU L1 cache may need to be <strong>flushed</strong> in between those dispatches to make sure that all of the writes at least made it to L2 (a cache flush refers to the operation of taking modified/dirty cache lines and writing them out to the next cache level, or actual memory if applied to the last cache level). And as <a href="http://32ipi028l5q82yhj72224m8j.wpengine.netdna-cdn.com/wp-content/uploads/2016/03/GDC_2016_D3D12_Right_On_Queue_final.pdf">slide 52</a> describes, it&rsquo;s even worse when a texture goes from being used as a render target to being used as a readable texture. For that case the writes to the render target could be sitting in the color buffer L1 cache that&rsquo;s attached to the ROP&rsquo;s, which means that cache has to be flushed in addition to flushing the other L1&rsquo;s and L2 cache. (note that AMD&rsquo;s new <a href="https://radeon.com/_downloads/vega-whitepaper-11.6.17.pdf">Vega architecture</a> has more unified cache hierarchy where the ROP&rsquo;s are also clients of the L2).</p>
<p>One cool thing about AMD hardware is that their tools actually show you when this happens! Here&rsquo;s a snippet from an RGP capture showing the caches being flushed (and shader threads being synchronized!) on my RX 460 after large dispatch finishes writing to a texture:</p>
<p><img src="/images/converted/breaking-down-barriers-part-1-whats-a-barrier/rgp_cacheflush.png" alt="RGP_CacheFlush"></p>
<p>Now the point of this isn&rsquo;t just to explain how caches are hard and complicated, but to illustrate another way in which GPU&rsquo;s require barriers. Ensuring that your threads don&rsquo;t overlap isn&rsquo;t sufficient for resolving read-after-write dependencies when you also have multiple caches that can have stale data in them. You&rsquo;ve also got to invalidate or flush those caches to make the results visible to subsequent tasks that need to read the data.</p>
<h2 id="squeezing-out-more-bandwidth">Squeezing Out More Bandwidth</h2>
<p>GPU&rsquo;s have gotten more and more focused on compute as time goes on, but they&rsquo;re still heavily optimized for rasterizing triangles into a grid of pixels. Doing this job means that the ROP&rsquo;s can end up having touch a <em>ton</em> of memory every frame. Games now have to render at up to 4k resolutions, which works out to 8294400 pixels if you write every one with no overdraw. Multiply that by 8 bytes per-pixel for 16-bit floating point texture formats, or maybe up to 30 or 40 bytes per-pixel for fat G-Buffers, and you&rsquo;re looking at a lot bandwidth consumption just to touch all of that memory once (and typically many texels will be touched more than once)! It only gets worse if you add MSAA into the mix, which will double or quadruple the memory and bandwidth requirements in the naive case.</p>
<p>To help keep that bandwidth usage from becoming a bottleneck, GPU designers have put quite a bit of effort into building lossless compression techniques into their hardware. Typically this sort of thing is implemented as part of the ROP&rsquo;s, and is therefore used when writing to render targets and depth buffers. There&rsquo;s been a lot of specific techniques used over the years, and the exact details haven&rsquo;t been made available to the public. However <a href="https://gpuopen.com/dcc-overview/">AMD</a> and <a href="https://www.anandtech.com/show/10325/the-nvidia-geforce-gtx-1080-and-1070-founders-edition-review/8">Nvidia</a> have provided at least a bit of information about their particular implementations of delta color compression in their latest architectures. The basic gist of both techniques is that they aim to exploit the similarity in neighboring pixels in order to avoid storing a unique value for every texel of the render target. Instead, the hardware recognizes patterns in blocks of pixels, and stores each pixel&rsquo;s difference (or delta) from an anchor value. Nvidia&rsquo;s block modes give them anywhere from 2:1 to 8:1 compression ratios, which potentially results in huge bandwidth savings!</p>
<p><a href="/images/converted/breaking-down-barriers-part-1-whats-a-barrier/nvidia_dcc.png"><img src="/images/converted/breaking-down-barriers-part-1-whats-a-barrier/nvidia_dcc.png" alt="Nvidia_DCC"></a></p>
<p>So what exactly does this have to do with barriers? The problem with these fancy compression modes is that while the ROP&rsquo;s may understand how to deal with the compressed data, the same is not necessarily true when shaders need to randomly-access the data through their texture units. This means that depending on the hardware and how the texture is used, a decompression step might be necessary before the texture contents are readable by a dependent task (or writable through a means other than ROP&rsquo;s). Once again, this is something that falls under the umbrella of &ldquo;barriers&rdquo; when we&rsquo;re talking about GPU&rsquo;s and the new explicit API&rsquo;s for talking to them.</p>
<h2 id="but-what-about-d3d">But What About D3D?</h2>
<p>After reading through my ramblings about thread synchronization, cache coherency, and GPU compression, you hopefully have at least a very basic grasp of 3 potential reasons that typical GPU&rsquo;s require barriers to do normal things that we expect of them. But if you look at the actual barrier API&rsquo;s in <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn899226(v=vs.85).aspx">D3D12</a> or <a href="https://gpuopen.com/vulkan-barriers-explained/">Vulkan</a>, you&rsquo;ll probably notice that they don&rsquo;t really seem to directly correspond with what we just talked about. After all, it&rsquo;s not like there&rsquo;s a &ldquo;WaitForDispatchedThreadsToFinish&rdquo; or &ldquo;FlushTextureCaches&rdquo; function on ID3D12GraphicsCommandList. And if you think about it, it makes sense that they don&rsquo;t do this. The fact that most GPU&rsquo;s have lots of shader cores where tasks can overlap is a pretty specific implementation detail, and you could say the same about GPU&rsquo;s that have weird incoherent cache hierarchies. Even for an explicit API like D3D12 it wouldn&rsquo;t make sense to leak that kind of detail across its abstraction, since it&rsquo;s totally possible that one day D3D12 could be used to talk to a GPU that doesn&rsquo;t behave the way that I just described (it may have already happened!).</p>
<p>When you think of things from that perspective, it starts to make sense that D3D12/Vulkan barriers are more high-level, and instead are mostly aimed at describing the flow of data from one pipeline stage to another. Another way to describe them is to say that the barriers tell the driver about changes in the <em>visibility</em> of data with regards to various tasks and/or functional units, which as we pointed out earlier is really the essence of barriers. So in D3D12 you don&rsquo;t say &ldquo;make sure that this draw call finishes before this other dispatch reads it&rdquo;, you say &ldquo;this texture is transitioning from a &lsquo;render target&rsquo; state to a &lsquo;shader readable&rsquo; state so that a shader program can read from it&rdquo;. Essentially you&rsquo;re giving the driver a bit of information about the past and future life of a resource, which may be necessary for making decisions about which caches to flush and whether or not to decompress a texture. Thread synchronization is then implied by the state transition rather than explicit dependencies between draws or dispatches, which isn&rsquo;t a perfect system but it gets the job done.</p>
<p>If you&rsquo;re wondering why we didn&rsquo;t need to manually issue barriers in D3D11, the answer to that question is &ldquo;because the driver did it for us!&rdquo;. Remember how earlier I said that a compiler can analyze your code to determine dependencies, and generate the appropriate assembly automatically? This is basically what drivers do in D3D11, except they&rsquo;re doing it at runtime! The driver needs to look at all the resources that you bind as inputs and outputs, figure out when there&rsquo;s visibility changes (for instance, going from a render target to a shader input), and insert the necessary sync points, cache flushes, and decompression steps. While it&rsquo;s nice that you automatically get correct results, it&rsquo;s also bad for a few reasons:</p>
<ul>
<li>Automatically tracking resources and draw/dispatch calls is expensive, which is not great when you want to squeeze your rendering code into a few milliseconds per frame.</li>
<li>It&rsquo;s really bad for generating command buffers in parallel. If you can set a texture as a render target in one thread and then bind it as an input in another thread, the driver can&rsquo;t figure out the full resource lifetime without somehow serializing the results of the two threads.</li>
<li>It relies on an explicit resource binding model, where the context always knows the full set of inputs and outputs for every draw or dispatch. This can prevent you from doing awesome things with bindless resource access.</li>
<li>In some cases the driver might issue unnecessary barriers due to not having knowledge of how the shaders access their data. For example, two dispatches that increment the same atomic counter won&rsquo;t necessarily need a barrier between them, even though they access the same resource.</li>
</ul>
<p>The thinking behind D3D12 and Vulkan is that you can eliminate those disadvantages by having the app provide the driver with the necessary visibility changes. This keeps the driver simpler, and lets the app figure out the barriers in any manner that it wants. If your rendering setup is fairly fixed, you can just hard-code your barriers and have essentially 0 CPU cost. Or you can setup your engine to <a href="https://www.ea.com/frostbite/news/framegraph-extensible-rendering-architecture-in-frostbite">build its own dependency graph</a>, and <a href="https://www.gdcvault.com/play/1024656/Advanced-Graphics-Tech-Moving-to">use that to determine which barriers you&rsquo;ll need</a>.</p>
<h2 id="next-up">Next Up</h2>
<p>In the <a href="../breaking-down-barriers-part-2-synchronizing-gpu-threads/">next article</a>, I&rsquo;m going to dive a bit deeper into the topic of thread-level synchronization and how it&rsquo;s typically implemented on GPU&rsquo;s. Stay tuned!</p>
<hr>
<h2 id="comments">Comments:</h2>
<h4 id="heading"></h4>
<p><a href="%22remigius@netforge.nl%22">Rim</a> - <time datetime="2018-03-06 21:56:13">Mar 2, 2018</time></p>
<p>Things sure changed since the good ol&rsquo; XNA days :) Looks like a very interesting read, thanks for the write-up!</p>
<hr />
####
[Baggers]( "techsnuffle@gmail.com") - <time datetime="2018-04-03 22:51:27">Apr 2, 2018</time>
<p>Fantastic writeup, thanks for making these</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 2: Synchronizing GPU Threads &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2018/04/01/breaking-down-barriers-part-2-synchronizing-gpu-threads/ "") - <time datetime="2018-04-01 22:29:22">Apr 0, 2018</time>
<p>[…] to part 2 of the series! In the previous article, I explained the basics of what a barrier is, and talked about the various reasons for why you need […]</p>
<hr />
####
[Breaking Down Barriers &#8211; Part 3: Multiple Command Processors &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2018/06/17/breaking-down-barriers-part-3-multiple-command-processors/ "") - <time datetime="2019-03-05 13:45:33">Mar 2, 2019</time>
<p>[…] Part 1 – What’s a Barrier? Part 2 – Synchronizing GPU Threads Part 3 – Multiple Command Processors Part 4 – GPU Preemption Part 5 – Back To The Real World Part 6 – Experimenting With Overlap and Preemption […]</p>
<hr />
####
[]( "") - <time datetime="2019-03-21 07:08:33">Mar 4, 2019</time>
<p>This is so nice. I am learning a lot from your articles :-)</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>SG Series Part 6: Step Into The Baking Lab</title>
			<link>https://therealmjp.github.io/posts/sg-series-part-6-step-into-the-baking-lab/</link>
			<pubDate>Mon, 10 Oct 2016 07:13:58 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/sg-series-part-6-step-into-the-baking-lab/</guid>
			<description>This is part 6 of a series on Spherical Gaussians and their applications for pre-computed lighting. You can find the other articles here:
Part 1 - A Brief (and Incomplete) History of Baked Lighting Representations
Part 2 - Spherical Gaussians 101
Part 3 - Diffuse Lighting From an SG Light Source
Part 4 - Specular Lighting From an SG Light Source
Part 5 - Approximating Radiance and Irradiance With SG&amp;rsquo;s</description>
			<content:encoded><![CDATA[<p><img src="/images/converted/sg-series-part-6-step-into-the-baking-lab/bakinglab_post_intro.png" alt="bakinglab_post_intro"></p>
<p><em>This is part 6 of a series on Spherical Gaussians and their applications for pre-computed lighting. You can find the other articles here:</em></p>
<p>Part 1 - <a href="../sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/">A Brief (and Incomplete) History of Baked Lighting Representations</a><br>
Part 2 - <a href="../sg-series-part-2-spherical-gaussians-101/">Spherical Gaussians 101</a><br>
Part 3 - <a href="../sg-series-part-3-diffuse-lighting-from-an-sg-light-source/">Diffuse Lighting From an SG Light Source</a><br>
Part 4 - <a href="../sg-series-part-4-specular-lighting-from-an-sg-light-source/">Specular Lighting From an SG Light Source</a><br>
Part 5 - <a href="../sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/">Approximating Radiance and Irradiance With SG&rsquo;s</a><br>
Part 6 - <a href="../sg-series-part-6-step-into-the-baking-lab/">Step Into The Baking Lab</a><br></p>
<p>Get the code on GitHub: <a href="https://github.com/TheRealMJP/BakingLab">https://github.com/TheRealMJP/BakingLab</a> (pre-compiled binaries available <a href="https://github.com/TheRealMJP/BakingLab/releases">here</a>)</p>
<p>Back in early 2014, myself and David Neubelt started doing serious research into using Spherical Gaussians as a compact representation for our pre-computed lighting probes. One of the first things I did back then was to create a testbed application that we could use to compare various lightmap representations (SH, H-basis, SG, etc.) and quickly experiment with new ideas. As part of that application I implemented my first path tracer, which was directly integrated into the app for A/B comparisons. This turned out to be extremely useful, since having quick feedback was really helpful for evaluating quality and also for finding and fixing bugs. Eventually we used this app to finalize the exact approach that we would use when integrating SG&rsquo;s into The Order: 1886.</p>
<p>A year later in 2015, Dave and I created another test application for experimenting with improvements that we were planning for future projects. This included things like a physically based exposure model utilizing real-world camera parameters, using the <a href="https://en.wikipedia.org/wiki/Academy_Color_Encoding_System">ACES</a>[1] RRT/ODT for tone mapping, and <a href="https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/course-notes-moving-frostbite-to-pbr-v2.pdf">using real-world units</a>[2] for specifying lighting intensities. At some point I integrated an improved version of SG baking into this app that would progressively compute results in the background while the app remained responsive, allowing for quick &ldquo;preview-quality&rdquo; feedback after adjusting the lighting parameters. Once we started working on our <a href="http://blog.selfshadow.com/publications/s2015-shading-course/rad/s2015_pbs_rad_slides.pdf">SIGGRAPH presentation</a>[3] from the 2015 physically based shading course, it occurred to us that we should really package up this new testbed and release it alongside the presentation to serve as a working implementation of the concepts we were going to cover. But unfortunately this slipped through the cracks: the new testbed required a lot of work in order to make it useful, and both Dave and I were really pressed for time due to multiple new projects ramping up at the office.</p>
<p>Now, more than a year after our SIGGRAPH presentation, I&rsquo;m happy to announce that we&rsquo;ve finally produced and published a working code sample that demonstrates baking of Spherical Gaussian lightmaps! This new app, which I call &ldquo;The Baking Lab&rdquo;, is essentially a combination of the two testbed applications that we created. It includes all of the fun features that we were researching in 2015, but also includes real-time progressive baking of 2D lightmaps in various formats. It also allows switching to a progressive path tracer at any time, which serves as the &ldquo;ground truth&rdquo; for evaluating lightmap quality and accuracy. Since it&rsquo;s an amalgamation of two older apps, it uses D3D11 and the older version of my sample framework. So there&rsquo;s no D3D12 fanciness, but it will run on Windows 7. If you&rsquo;re just interested in looking at the code or running the app, then go ahead and head over to GitHub: <a href="https://github.com/TheRealMJP/BakingLab">https://github.com/TheRealMJP/BakingLab</a>. If you&rsquo;re interested in the details of what&rsquo;s implemented in the app, then keep reading.</p>
<h2 id="lightmap-baking">Lightmap Baking</h2>
<p>The primary feature of The Baking Lab is lightmap baking. Each of the test scenes includes a secondary UV set that contains non-overlapping UV&rsquo;s used for mapping the lightmap onto the scene. Whenever the app starts or a new scene is selected, the baker uses the GPU to rasterize the scene into lightmap UV space. The pixel shader outputs interpolated vertex components like position, tangent frame, and UV&rsquo;s to several render targets, which use MSAA to simulate conservative rasterization. Once the rasterization is completed, the results are copied back into CPU-accessible memory. The CPU then scans the render targets, and extracts &ldquo;bake points&rdquo; from all texels covered by the scene geometry. Each of these bake points represents the location of a single hemispherical probe to be baked.</p>
<p>Once all bake points are extracted, the baker begins running using a set of background threads on the CPU. Each thread continuously grabs a new work unit consisting of a group of contiguous bake points, and then loops over the bake points to compute the result for that probe. Each probe is computed by invoking a path tracer, which uses <a href="https://embree.github.io/">Embree</a>[4] to allow for arbitrary ray tracing through the scene on the CPU. The path tracer returns the incoming radiance for a direction and starting point, where the radiance is the result of indirect lighting from various light sources as well as the direct lighting from the sky. The path tracer itself is a very simple unidirectional path tracer, using a few standard techniques like importance sampling, <a href="http://graphics.pixar.com/library/MultiJitteredSampling/paper.pdf">correlated multi-jittered sampling</a>[5], and russian roulette to increase performance and/or convergence rates. The following baking modes are supported:</p>
<ul>
<li><strong>Diffuse</strong> - a single RGB value containing the result of applying a standard diffuse BRDF to the incoming lighting, with an albedo of 1.0</li>
<li><strong>Half-Life 2</strong> - directional irradiance projected onto the <a href="https://advances.realtimerendering.com/s2006/Mitchell-ShadingInValvesSourceEngine.pdf">Half-Life 2 basis</a>[6], making for a total of 3 sets of RGB coefficients (9 floats total)</li>
<li><strong>L1 SH</strong> - radiance projected onto the first two orders of spherical harmonics, making for a total of 4 sets of RGB coefficients (12 floats total). Supports environment specular via a 3D lookup texture.</li>
<li><strong>L2 SH</strong> - radiance projected on the first three orders of spherical harmonics, making for a total of 9 sets of RGB coefficients (27 floats total). Supports environment specular via a 3D lookup texture.</li>
<li><strong>L1 H-basis</strong> - irradiance projected onto the first two orders of <a href="https://www.cg.tuwien.ac.at/research/publications/2010/Habel-2010-EIN/">H-basis</a>[7], making for a total of 4 sets of RGB coefficients (12 floats total).</li>
<li><strong>L2 H-basis</strong> - irradiance projected onto the first three orders of <a href="https://www.cg.tuwien.ac.at/research/publications/2010/Habel-2010-EIN/">H-basis</a>, making for a total of 6 sets of RGB coefficients (18 floats total).</li>
<li><strong>SG5</strong> - radiance represented by the sum of 5 SG lobes with fixed directions and sharpness, making for a total of 5 sets of RGB coefficients (15 floats total). Supports environment specular via an approximate evaluation of per-lobe specular contribution.</li>
<li><strong>SG6</strong> - radiance represented by the sum of 6 SG lobes with fixed directions and sharpness, making for a total of 6 sets of RGB coefficients (18 floats total). Supports environment specular via an approximate evaluation of per-lobe specular contribution.</li>
<li><strong>SG9</strong> - radiance represented by the sum of 9 SG lobes with fixed directions and sharpness, making for a total of 9 sets of RGB coefficients (27 floats total). Supports environment specular via an approximate evaluation of per-lobe specular contribution.</li>
<li><strong>SG12</strong> - radiance represented by the sum of 12 SG lobes with fixed directions and sharpness, making for a total of 12 sets of RGB coefficients (36 floats total). Supports environment specular via an approximate evaluation of per-lobe specular contribution.</li>
</ul>
<p>For SH, H-basis, and HL2 basis baking modes the path tracer is evaluated for random rays distributed about the hemisphere so that Monte Carlo integration can be used to integrate the radiance samples onto the corresponding basis functions. This allows for true progressive integration, where the baker makes N passes over each bake point, each time adding a new sample with the appropriate weighting. It looks pretty cool in action:</p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/aN0StrS2moI" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

<p>The same approach is used for the &ldquo;Diffuse&rdquo; baking mode, except that sampling rays are evaluated using a <a href="http://www.rorydriscoll.com/2009/01/07/better-sampling/">cosine-weighted hemispherical sampling scheme</a>[8]. For SG baking, things get a little bit trickier. If the ad-hoc projection mode is selected, the result can be progressively evaluated in the same manner as the non-SG bake modes. However if either the Least Squares or Non-Negative Least Squares mode are active, we can&rsquo;t run the solve unless we have all of the hemispherical radiance samples available to feed to the solver. In this case we switch to a different baking scheme where each thread fully computes the final value for every bake point that it operates on. However the thread only does this for a single bake point from each work group, and afterwards it fills in the rest of the neighboring bake points (which are arranged in a 8x8 group of texels) with the results it just computed. Each pass of of baker then fills in the next bake point in the work group, gradually computing the final result for all texels in the group. So instead of seeing the quality slowly improve across the light map, you see extrapolated results being filled in. It ends up looking like this:</p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/WhSlZgycQfM" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

<p>While it&rsquo;s not as great as a true progressive bake, it&rsquo;s still better than having no preview at all.</p>
<p>The app supports a few settings that control some of the bake parameters, such as the number of samples evaluated per-texel and the overall lightmap resolution. The &ldquo;Scene&rdquo; group in the UI also has a few settings that allow toggling different components of the final render, such as the direct or indirect lighting or the diffuse/specular components. Under the &ldquo;Debug&rdquo; setting you can also toggle a neat visualizer that shows a visual representation of the raw data stored in the lightmap. It looks like this:</p>
<p><img src="/images/converted/sg-series-part-6-step-into-the-baking-lab/sg_debug_visualizer_resized_1024.png" alt="SG_Debug_Visualizer"></p>
<h2 id="ground-truth-path-tracer">Ground Truth Path Tracer</h2>
<p>The integrated path tracer is primarily there so that you can see how close or far off you are when computing environment diffuse or specular from a light map. It was also a lot of fun to write - I recommend doing it sometime if you haven&rsquo;t already! Just be careful: it may make you depressed to see how poorly your real-time approximation holds up when compared with a proper offline render. :-)</p>
<p>The ground truth renderer works in a similar vein to the lightmap baker: it kicks off multiple background threads that each grab work groups of 16x16 pixels that are contiguous in screen space. The renderer makes N passes over each pixel, where each pass adds an additional sample that&rsquo;s weighted and summed with the previous results. This gives you a true progressive render, where the result starts out noisy and  (very) gradually converges towards a noise-free image:</p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/LUrCpZYQbm0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

<p>The ground truth renderer is activated by checking the &ldquo;Show Ground Truth&rdquo; setting under the &ldquo;Ground Truth&rdquo; group. There&rsquo;s a few more parameters in that group to control the behavior of the renderer, such as the number of samples used per-pixel and the scheme used for generating random samples.</p>
<h2 id="light-sources">Light Sources</h2>
<p>There&rsquo;s 3 different light sources supported in the app: a sun, a sky, and a spherical area light. For real-time rendering, the sun is handled as a directional light with an intensity computed automatically using the <a href="http://cgg.mff.cuni.cz/projects/SkylightModelling/">Hosek-Wilkie solar radiance model</a>[9]. So as you change the position of the sun in the sky, you&rsquo;ll see the color and intensity of the sunlight automatically change. To improve the real-time appearance, I used the disk area light approximation from the 2014 Frostbite presentation. The path tracer evaluates the sun as an infinitely-distant spherical area light with the appropriate angular radius, with uniform intensity and color also computed from the solar radiance model. Since the path tracer handles the sun as a true area light source, it produces correct specular reflections and soft shadows. In both cases the sun defaults to correct real-world intensities using actual photometric units. There is a parameter for adjusting the sun size, which will result in the sun being too bright or too dark if manipulated. However there&rsquo;s another setting called &ldquo;Normalize Sun Intensity&rdquo; which will attempt to maintain roughly the same illumination regardless of the size, which allows for changing the sun appearance or shadow softness without changing the overall scene lighting.</p>
<p>The default sky mode (called &ldquo;Procedural&rdquo;) uses the Hosek-Wilkie sky model to compute a procedural sky from a few input parameters. These include turbidity, ground albedo, and the current sun position. Whenever the parameters are changed, the model is cached to a cubemap that&rsquo; s used for real-time rendering on the GPU. For CPU path tracing, the the sky model is directly evaluated for a direction using the sample code provided by the authors. When combined with the procedural sun model, the two light sources form a simple outdoor lighting environment that corresponds to real-world intensities. Several other sky modes are also supported for convenience. The &ldquo;Simple&quot;mode takes just a color and intensity as input parameter, and flood-fills the entire sky with a value equal to color * intensity. The &ldquo;Ennis&rdquo;, &ldquo;Grace Cathedral&rdquo;, and &ldquo;Uffizi Cross&rdquo; modes use corresponding HDR environment maps to fill the sky instead of a procedural model.</p>
<p>For local lighting, the app supports enabling a single spherical area light using the &ldquo;Enable Area Light&rdquo; setting.  The area light can be positioned using the Position X/Position Y/Position Z settings, and its radius can be specified with the &ldquo;Size&rdquo; setting. There are a 4 different modes for specifying the intensity of the light:</p>
<ul>
<li><strong>Luminance</strong> - the intensity corresponds to the amount of light being emitted from the light source along an infinitesimally small ray towards the viewer or receiving surface. Uses units of cd/m2. Changing the size of the light source will change the overall illumination the scene.</li>
<li><strong>Illuminance</strong> - specifies the amount of light incident on a surface at a set distance, which is specified using the &ldquo;Illuminance Distance&rdquo; setting. So instead of saying &ldquo;how much light is coming out of the light source&rdquo; like you do with the &ldquo;Luminance&rdquo; mode, you&rsquo;re saying &ldquo;how much diffuse light is being reflected from a perpendicular surface N units away&rdquo;. Uses units of lux, which are equivalent to lm/m2.  Changing the size of the light source will  <em>not</em> change the overall illumination the scene.</li>
<li><strong>Luminous Power</strong> - specifies the total amount of light being emitted from the light source in all directions. Uses units of lumens. Changing the size of the light source will  <em>not</em> change the overall illumination the scene.</li>
<li><strong>EV100</strong> - this is an alternative way of specifying the luminance of the light source, using the <a href="https://en.wikipedia.org/wiki/Exposure_valuehttps://en.wikipedia.org/wiki/Exposure_value">exposure value</a>[10] system originally suggested by <a href="http://www.reedbeta.com/blog/2014/06/04/artist-friendly-hdr-with-exposure-values/">Nathan Reed</a>[11]. The base-2 logarithmic scale for this mode is really nice, since incrementing by 1 means doubling the perceived brightness. Changing the size of the light source will change the overall illumination the scene.</li>
</ul>
<p>The ground truth renderer will evaluate the area light as a true spherical light source, using importance sampling to reduce variance. The real-time renderer approximates the light source as a single SG, and generates very simple hard shadows using an array of 6 shadow maps. By default only indirect lighting from the area light will be baked into the lightmap, with the direct lighting evaluated on the GPU. However if the &ldquo;Bake Direct Area Light&rdquo; setting is enabled, then the direct contribution from the area light will be baked into the lightmap.</p>
<p>Note that all light sources in the app are always scaled down by a factor of 2-10 before being using in rendering, as suggested by Nathan Reed in <a href="http://www.reedbeta.com/blog/2014/06/04/artist-friendly-hdr-with-exposure-values/">his blog post</a>[11]. Doing this effectively shifts the window of values that can be represented in a 16-bit floating point value, which is necessary in order to represent specular reflections from the sun. However the UI always will always show the unshifted values, as will the debug luminance picker that shows the final color and intensity of any pixel on the screen.</p>
<h2 id="exposure-and-depth-of-field">Exposure and Depth of Field</h2>
<p>As I mentioned earlier, the app implements a physically based exposure system that attempts to models the behavior and parameters of a real-world camera. Much of the implementation was based on the code from Padraic Hennessy&rsquo;s <a href="https://placeholderart.wordpress.com/2014/11/16/implementing-a-physically-based-camera-understanding-exposure/">excellent series of articles</a>[12], which was in turn inspired by Sébastien Lagarde and Charles de Rousiers&rsquo;s <a href="https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/course-notes-moving-frostbite-to-pbr-v2.pdf">SIGGRAPH presentation from 2014</a>[2]. When the &ldquo;Exposure Mode&rdquo; setting is set to the &ldquo;Manual (SBS)&rdquo; or &ldquo;Manual (SOS)&rdquo; modes, the final exposure value applied before tone mapping will be computed based on the combination of aperture size, ISO rating, and shutter speed. There is also a &ldquo;Manual (Simple)&rdquo; mode available where a single value on a log2 scale can be used instead of the 3 camera parameters.</p>
<p>Mostly for fun, I integrated a post-process depth of field effect that uses the same camera parameters (along with focal length and film size) to compute per-pixel circle of confusion sizes. The effect is off by default, and can be toggled on using the &ldquo;Enable DOF&rdquo; setting. Polygonal and circular bokeh shapes are supported using the technique suggested by Tiago Sousa in his <a href="http://advances.realtimerendering.com/s2013/Sousa_Graphics_Gems_CryENGINE3.pptx">2013 SIGGRAPH presentation</a>[13]. Depth of field is also implemented in the ground truth renderer, which is capable of achieving true multi-layer effects by virtue of using a ray tracer.</p>
<p><a href="/images/converted/sg-series-part-6-step-into-the-baking-lab/dof_gt.png"><img src="/images/converted/sg-series-part-6-step-into-the-baking-lab/dof_gt.png" alt="dof_gt"></a></p>
<h2 id="tone-mapping">Tone Mapping</h2>
<p>Several tone mapping operators are available for experimentation:</p>
<ul>
<li><strong>Linear</strong> - no tone mapping, just a clamp to [0, 1]</li>
<li><strong>Film Stock</strong> - <a href="https://twitter.com/jimhejl">Jim Hejl</a> and Richard Burgess-Dawson&rsquo;s polyomial approximation of <a href="https://twitter.com/hpduiker">Haarm-Peter Duiker</a>&rsquo;s filmic curve, which was created by scanning actual film stock. Based on the implementation provided by <a href="http://filmicworlds.com/blog/filmic-tonemapping-operators/">John Hable</a>[14].</li>
<li><strong>Hable (Uncharted2)</strong> - <a href="https://twitter.com/FilmicWorlds">John Hable</a>&rsquo;s adjustable filmic curve from his <a href="http://www.gdcvault.com/play/1012351/Uncharted-2-HDR">GDC 2010 presentation</a>[15]</li>
<li><strong>Hejl 2015</strong> - Jim Hejl&rsquo;s filmic curve that he <a href="https://twitter.com/jimhejl/status/633777619998130176">posted on Twitter</a>[16], which is a refinement of Duiker&rsquo;s curve</li>
<li><strong>ACES sRGB Monitor</strong> - a fitted polynomial version of the <a href="https://github.com/ampas/aces-dev">ACES</a>[17] reference rendering transform (RRT) combined with the sRGB monitor output display transform (ODT), generously provided by <a href="https://twitter.com/self_shadow">Stephen Hill</a>.</li>
</ul>
<h2 id="debug-settings">Debug Settings</h2>
<p>At the bottom of the settings UI are a group of debug options that can be selected. I already mentioned the bake data visualizer previously, but it&rsquo;s worth mentioning again because it&rsquo;s really cool. There&rsquo;s also a &ldquo;luminance picker&rdquo;, which will enable a text output showing you the luminance and illuminance of the surface under the mouse cursor. This was handy for validating the physically based sun and sky model, since I could use the picker to make sure that the lighting values matched what you would expect from real-world conditions. The &ldquo;View Indirect Specular&rdquo; option causes both the real-time renderer and the ground truth renderer to only show the indirect specular component, which can be useful for gauging the accuracy of specular computed from the lightmap. After that there&rsquo;s a pair of buttons for saving or loading light settings. This will serialize the settings that control the lighting environment (sun direction, sky mode, area light position, etc.) to a file, which can be loaded in whenever you like. The &ldquo;Save EXR Screenshot&rdquo; is fairly self-explanatory: it lets you save a screenshot to an EXR file that retains the HDR data. Finally there&rsquo;s an option to show the current sun intensity that&rsquo;s used for the real-time directional light.</p>
<h2 id="references">References</h2>
<p>[1] <a href="https://en.wikipedia.org/wiki/Academy_Color_Encoding_System">Academy Color Encoding System</a><br>
[2] <a href="https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/course-notes-moving-frostbite-to-pbr-v2.pdf">Moving Frostbite to PBR (course notes)</a><br>
[3] <a href="http://blog.selfshadow.com/publications/s2015-shading-course/rad/s2015_pbs_rad_slides.pdf">Advanced Lighting R&amp;D at Ready At Dawn Studios</a><br>
[4] <a href="https://embree.github.io/">Embree: High Performance Ray Tracing Kernels</a><br>
[5] <a href="http://graphics.pixar.com/library/MultiJitteredSampling/paper.pdf">Correlated Multi-Jittered Sampling</a><br>
[6] <a href="https://advances.realtimerendering.com/s2006/Mitchell-ShadingInValvesSourceEngine.pdf">Shading in Valve’s Source Engine</a><br>
[7] <a href="https://www.cg.tuwien.ac.at/research/publications/2010/Habel-2010-EIN/">Efficient Irradiance Normal Mapping</a><br>
[8] <a href="http://www.rorydriscoll.com/2009/01/07/better-sampling/">Better Sampling</a><br>
[9] <a href="http://cgg.mff.cuni.cz/projects/SkylightModelling/">Adding a Solar Radiance Function to the Hosek Skylight Model</a><br>
[10] <a href="https://en.wikipedia.org/wiki/Exposure_value">Exposure value</a><br>
[11] <a href="http://www.reedbeta.com/blog/2014/06/04/artist-friendly-hdr-with-exposure-values/">Artist-Friendly HDR With Exposure Values</a><br>
[12] <a href="https://placeholderart.wordpress.com/2014/11/16/implementing-a-physically-based-camera-understanding-exposure/">Implementing a Physically Based Camera: Understanding Exposure</a><br>
[13] <a href="http://advances.realtimerendering.com/s2013/Sousa_Graphics_Gems_CryENGINE3.pptx">CryENGINE 3 Graphics Gems</a><br>
[14] <a href="http://filmicworlds.com/blog/filmic-tonemapping-operators/">Filmic Tonemapping Operators</a><br>
[15] <a href="http://www.gdcvault.com/play/1012351/Uncharted-2-HDR">Uncharted 2: HDR Lighting</a><br>
[16] <a href="https://twitter.com/jimhejl/status/633777619998130176">Jim Hejl on Twitter</a><br>
[17] <a href="https://github.com/ampas/aces-dev">Academy Color Encoding System Developer Resources</a><br></p>
<hr>
<h2 id="comments">Comments:</h2>
<h4 id="heading"></h4>
<p><a href="%22laurens@bamtang.com%22">dev</a> - <time datetime="2019-01-08 15:33:47">Jan 2, 2019</time></p>
<p>thanks for the great article, sorry about the beginner question but I wanted to know if there are some considerations we need to know if we want to add more lights to the scene,</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2017-06-25 22:30:38">Jun 0, 2017</time>
<p>Does your FBX file have a secondary UV set? The demo requires that the scene have a second UV set that has a unique UV parameterization for the whole scene, since it uses it for the generated lightmap.</p>
<hr />
####
[]( "") - <time datetime="2018-08-08 10:12:59">Aug 3, 2018</time>
<p>Hello, Thanks for providing the baking lab source code and these articles! Does the baking lab contain any sample code for how one could have a dynamic mesh lit from the lightmaps?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2018-08-08 21:38:43">Aug 3, 2018</time>
<p>Unfortunately it does not: it only shows how to bake 2D lightmaps for suitable static geometry. Each texel of the lightmap bakes the incoming lighting for a hemisphere surrounding the surface normal of the mesh, which means that the information is only useful if being applied to that particular surface. For dynamic meshes, you typically want to bake a set of spherical probes (containing lighting from all directions, and not just on a hemisphere) throughout the entire area that your meshes might be moving. A simple way to do this is to place all of probes within cells of a 3D grid that surrounds your scene, which makes it simple to figure out which probe(s) to sample from, and also makes it simple to interpolate between neighboring probes. It&rsquo;s also possible to use less regular representations that are more sparse in areas with less lighting/geometry complexity, or that rely on hand-placed probes. This can save you memory and/or baking time, but can make probe lookups and interpolation more costly. Either way baking the probe itself is very similar to the code in the sample for baking a hemisphere, with the major difference being that you need to shoot rays in all directions and adjust your monte carlo weights accordingly. You also have to choose a basis that can represent lighting on a sphere, for instance spherical harmonics or a set of SG&rsquo;s oriented about the unit sphere. Part 5 of the article talks a bit about working with these representations, and the tradeoffs involved.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2017-03-05 16:38:06">Mar 0, 2017</time>
<p>Hi Steven, Sorry that I took so long to reply to your question. The answer here is &ldquo;yes&rdquo;, since samples at the extreme edges of the hemisphere can potentially result in a negative contribution when projected onto the basis vectors. HL2 basis is exactly like spherical harmonics in this regard, except that the way the basis vectors are oriented results in ideal projections for a hemisphere since each basis vector has the same projection onto the local Z axis (normal) of the baking point. In fact, HL2 is pretty much exactly the same as L1 SH, except it has different weighting and the basis vectors are rotated (in SH the linear cosine lobes are lined up with the major X/Y/Z axes). This is actually noted in Habel&rsquo;s paper from 2010 where they introduce H-basis: <a href="https://www.cg.tuwien.ac.at/research/publications/2010/Habel-2010-EIN/Habel-2010-EIN-paper.pdf">https://www.cg.tuwien.ac.at/research/publications/2010/Habel-2010-EIN/Habel-2010-EIN-paper.pdf</a> (see section 2.1)</p>
<hr />
####
[Steven]( "steven.brekelmans@gmail.com") - <time datetime="2017-02-11 08:36:23">Feb 6, 2017</time>
<p>First of all thank you for the great series. I have a question about the HL2Baker - when the sample is projected onto the three basis vectors in AddSample(), there&rsquo;s no clamp for rejecting samples that point *away* from the basis. Doesn&rsquo;t this effectively remove power from the accumulation for that basis since the dot product would be negative?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-10-30 12:53:06">Oct 0, 2016</time>
<p>Hey Max, I&rsquo;m glad you enjoyed the articles! The incorrect luminance calculation was an oversight on my part. I must have copy-pasted the formula from some older code without double-checking that I was using the correct weights for computing relative luminance from linear sRGB values. I&rsquo;ve corrected the code in that shader, and also fixed a few other places where I was using the incorrect formula. Thank you for pointing that out!</p>
<hr />
####
[Rim]( "remigius@netforge.nl") - <time datetime="2016-10-12 23:49:36">Oct 3, 2016</time>
<p>Great stuff as always Matt, posts like this make me want to do 3D graphics again :)</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-10-13 09:49:20">Oct 4, 2016</time>
<p>Thank you Rim, nice to hear from you again! There&rsquo;s always time for a little graphics on the side. ;)</p>
<hr />
####
[Max]( "msomeone@gmail.com") - <time datetime="2016-10-29 06:49:41">Oct 6, 2016</time>
<p>Thank you for writing this awesome articles series and releasing BakingLab! I`m very curious why in illuminance computation in Mesh.hlsl shader there are 0.299f, 0.587f, 0.114f weight, not rec.709\sRGB relative luminance weights? Is it somehow related to usage of Hosek&rsquo;s sky model?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2017-04-27 11:17:14">Apr 4, 2017</time>
<p>Hey! So pretty much the entire pipeline works in the a space such that intensity is scaled down by the 2^-10 factor, with all inputs (environment maps, light sources) appropriately scaled down to match that space. This includes the average luminance calculation and exposure calculations. There&rsquo;s no need to ever &ldquo;restore&rdquo; back to the physical intensity range, since you get the correct final results by keeping the scene radiance and exposure values in the same 2^-10 space. Does that make sense? Sorry that the code is a bit confusing when it comes to that scale factor, I probably should have added a few more comments explaining what was going on with that. -Matt</p>
<hr />
####
[olej]( "olejpoland@gmail.com") - <time datetime="2016-11-12 18:15:53">Nov 6, 2016</time>
<p>Hey Matt, First of all thanks for all your hard work and sharing it with us :). Your contributions are invaluable to the graphics programming community. As some unfamiliar with offline rendering middlewares, I was wondering why use Embree for ground truth reference? I remember you mentioning that your bake farms on The Order used OptiX. I would guess that a GPU raytracer would converge way faster then a CPU one, especially when you are limited to a single machine. What are your reasons for this choice and what library would you recommend to someone wanting to write a reference GI preview for a game engine?</p>
<hr />
####
[User1]( "mendele@yandex.ru") - <time datetime="2017-04-27 02:31:32">Apr 4, 2017</time>
<p>Hello MJP! Thanks for sharing. My question is regarding average luminance calculation. LuminanceReductionInitialCS takes as input a back buffer with luminance scaled down with 2^-10, and operates on that values without restoring actual luminance. But restoring gives wrong results (everything becomes too dark). What&rsquo;s the logic behind that? Thanks.</p>
<hr />
####
[Sze](http://divrgents.com "awu.chen.dev@gmail.com") - <time datetime="2017-06-21 17:06:07">Jun 3, 2017</time>
<p>Hi MJP, Thank you so much for providing this extensive blog and the source code. I am running into problems bringing in an external .fbx file into the project. I&rsquo;ve tried modifying the Model filenames as well as creating an entirely new fbx file in the BakingLab.cpp however it&rsquo;s giving me errors &ldquo;block offset if out of range&rdquo; or &ldquo;DirectX Error: The parameter is incorrect&rdquo; It will be amazing if you can let me know how I should proceed with bringing in external fbx files. Thanks again.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-11-12 18:51:57">Nov 6, 2016</time>
<p>I haven&rsquo;t seen any concrete numbers in a while, but I would imagine that OptiX on a high-end GPU will have higher throughput than running Embree on a high-end consumer CPU. However I find Embree much easier to work with, which is why I use it for non-work projects. The Embree API is very clean with few entrypoints, and it&rsquo;s very simple to use it to build an acceleration structure from triangle meshes. From there you can just cast arbitrary rays however you&rsquo;d like, which makes it very easy to fit into your renderer. I would also consider it to be pretty fast and well-optimized, even if it can&rsquo;t match the throughput of a GPU-based ray tracer. The OptiX API is also nice, but it&rsquo;s much more complex than Embree. This is becase OptiX isn&rsquo;t just a ray-tracing kernel: it&rsquo;s also a framework for writing a complete renderer. The framework is important because it abstracts away some of the details of CUDA and the underlying GPU architecture, which is inherently less flexible than pure CPU code. This allows you to write a ray-generation program that runs persistently while the GPU traces the rays and runs separate programs whenever a ray intersects a triangle. They also have an API that they call &ldquo;OptiX Prime&rdquo;, which is just the ray-tracing kernel. I haven&rsquo;t used it myself, but I would imagine it might be tricky to use in a way that plays nicely with a GPU. Either way, if you use it then you&rsquo;ve effectively restricted yourself to Nvidia hardware. At work we don&rsquo;t care as much since we can just keep buying Nvidia GPU&rsquo;s, but that&rsquo;s not a great option for a public demo. At work we also run Linux on the bake farm PC&rsquo;s, which helps avoid some annoying issues that can pop up when using CUDA on a Windows PC. However we&rsquo;ve still had plenty of issues with drivers, overheating, running out of memory, and lack of debugging support. I think for your purposes Embree would be just fine. Like I just explained it&rsquo;s much easier to integrate, especially when you consider that you can natively debug your code. If you want to make it as fast as you can then you can consider using ISPC to vectorize your code.You may also want to look into AMD&rsquo;s FireRays, which can use Embree as a back-end or run on the GPU: <a href="http://gpuopen.com/firerays-2-0-open-sourcing-and-customizing-ray-tracing/">http://gpuopen.com/firerays-2-0-open-sourcing-and-customizing-ray-tracing/</a> I haven&rsquo;t looked at it myself, but I would imagine that the API is heavier than Embree&rsquo;s due to the need to abstract away multiple back-ends.</p>
<hr />
####
[郭叉叉 (@guoxx_)](http://twitter.com/guoxx_ "guoxx_@twitter.example.com") - <time datetime="2017-06-19 05:33:33">Jun 1, 2017</time>
<p>Amazing articles. Thanks for sharing those experience and source code. I have one question about the relative luminance calculation, the equation used is well described in BT.709 standard. But I think it&rsquo;s working with radiometry unit. Since the luminance unit is used for light, relative luminance shouldn&rsquo;t use another equation? For example, there is a light with color temperature and randiant power defined, we can construct a spectrum data and luminous efficiency base on color temperature, after photometric curve weighted, RGB value with photometry unit is used for lighitng, then luminance is stored in backbuffer. If we apply the equation from BT.709. does photometric curve applied twice since curve is already applied when we convert light units from radiometry to photometry? I think the correct way is luminance = r * integral_of_srgb_r_responce_curve + g * integral_of_srgb_g_responce_curve + b * integral_of_srgb_b_responce_curve Please point it out if I made any mistake.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-10-25 15:46:31">Oct 2, 2016</time>
<p>Hey Stephen, thank you for the kind words! Yes, you could absolutely save off the &ldquo;Lightmap&rdquo; resource as a file and then re-load later. In fact there&rsquo;s a helper function in Textures.h called &ldquo;SaveTextureAsDDS&rdquo; that you could use to do this, and it will save the save the entire texture array as a DDS file containing uncompressed fp16 data.</p>
<hr />
####
[Wumpf](http://wumpfblog.wordpress.com "r_andreas2@web.de") - <time datetime="2016-10-15 03:32:56">Oct 6, 2016</time>
<p>Thank you so much for this article series! Haven&rsquo;t done much graphics stuff for quite some time - all those cross references will keep me busy for a while :). I especially enjoyed how you&rsquo;ve described the practical considerations of the production environment in Part 5. It must have been quite annoying to drop the accurate fitting algorithm for the (albeit creative) hack to make it run on a large scale within the time constraints.</p>
<hr />
####
[Stephen Keefer](https://www.facebook.com/app_scoped_user_id/10155402927081038/ "keefer.stephen@gmail.com") - <time datetime="2016-10-25 10:13:39">Oct 2, 2016</time>
<p>Very nice article and description! Excellent sample application and makes me wanna incorporate this into my editor. I am curious about a few things Am I correct in assuming that, if you were to save &ldquo;Lightmap&rdquo; resource view from &ldquo;MeshBakerStatus&rdquo; after the progress reaches 100%, you could then reuse this in next launch (or in game), assuming light parameters are the same.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>SG Series Part 5: Approximating Radiance and Irradiance With SG&#39;s</title>
			<link>https://therealmjp.github.io/posts/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/</link>
			<pubDate>Mon, 10 Oct 2016 07:12:13 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/</guid>
			<description>This is part 5 of a series on Spherical Gaussians and their applications for pre-computed lighting. You can find the other articles here:
Part 1 - A Brief (and Incomplete) History of Baked Lighting Representations
Part 2 - Spherical Gaussians 101
Part 3 - Diffuse Lighting From an SG Light Source
Part 4 - Specular Lighting From an SG Light Source
Part 5 - Approximating Radiance and Irradiance With SG&amp;rsquo;s</description>
			<content:encoded><![CDATA[<p><em>This is part 5 of a series on Spherical Gaussians and their applications for pre-computed lighting. You can find the other articles here:</em></p>
<p>Part 1 - <a href="../sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/">A Brief (and Incomplete) History of Baked Lighting Representations</a><br>
Part 2 - <a href="../sg-series-part-2-spherical-gaussians-101/">Spherical Gaussians 101</a><br>
Part 3 - <a href="../sg-series-part-3-diffuse-lighting-from-an-sg-light-source/">Diffuse Lighting From an SG Light Source</a><br>
Part 4 - <a href="../sg-series-part-4-specular-lighting-from-an-sg-light-source/">Specular Lighting From an SG Light Source</a><br>
Part 5 - <a href="../sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/">Approximating Radiance and Irradiance With SG&rsquo;s</a><br>
Part 6 - <a href="../sg-series-part-6-step-into-the-baking-lab/">Step Into The Baking Lab</a><br></p>
<p>In the two previous articles I showed workable approaches for approximating the diffuse and specular result from a Spherical Gaussian light source. On its own these techniques might seem a bit silly, since it&rsquo;s not immediately obvious why the heck it would be useful to light a scene with an SG light source. But then you might remember that these articles started off by discussing methods for storing pre-computed radiance or irradiance in lightmaps or probe grids, which is a subject we&rsquo;ll finally return to.</p>
<h2 id="finding-the-perfect-fit">Finding The Perfect Fit</h2>
<p>A common process in mathematics (particularly statistics) is to take a set of data points and attempt to figure out some sort of analytical curve that can represent the data. This process is known as <a href="https://en.wikipedia.org/wiki/Curve_fitting">curve fitting</a>[1], since the goal is to find a curve that is a good fit for the data points. There&rsquo;s various reasons to do this (such as <a href="https://en.wikipedia.org/wiki/Regression_analysis">regression analysis</a>[2]), but I find it can helpful to think of it as a form of lossy compression: a few hundred data points might require kilobytes of data, but if you can approximate that data with a curve the coefficients might only need a few bytes of storage. Here&rsquo;s a simple example from Wikipedia:</p>
<p><a href="https://commons.wikimedia.org/wiki/File:Curve_fitting.svg"><img src="https://upload.wikimedia.org/wikipedia/commons/7/7f/Curve_fitting.svg" alt=""></a></p>
<p><em>Fitting various polynomials to data generated by a sine wave. Red is first degree, green is second degree, orange is third degree, blue is forth degree.
By Krishnavedala (Own work) [CC0], via Wikimedia Commons</em></p>
<p>In the image there&rsquo;s a bunch of black dots, which represent a set of data points that we want to fit. In this case the data points come from a sine wave, but in practice the data could take any form. The various colored curves represents attempts at fitting the data using polynomials of varying degrees:</p>
<p>$$ y = c_0 + c_1 \cdot x $$</p>
<p>$$ y = c_0 + c_1 \cdot x + c_2 \cdot x^2 $$</p>
<p>$$ y = c_0 + c_1 \cdot x + c_2 \cdot x^2  + c_3 \cdot x^3 $$</p>
<p>$$ y = c_0 + c_1 \cdot x + c_2 \cdot x^2  + c_3 \cdot x^3 + c_4 \cdot x^4 $$</p>
<p>By looking at graphs and the forms of the polynomials it should be obvious that higher degrees allow for more complex curves, but require more coefficients. More coefficients means more data to store, and may also mean that the fitting process is more difficult and/or more expensive. One of the most common techniques used for fitting is <a href="https://en.wikipedia.org/wiki/Least_squares">least squares</a>[3], which works by minimizing the sum of all differences between the fit curve and the original data.</p>
<p>The other observation we can make it that the resulting fit is essentially a linear combination of basis functions, where the basis functions are \( x \), \( x^2 \), \( x^3 \), and so on. There are many other basis functions we could use here instead of polynomials, such as our old friend the Gaussian! Just like polynomials, a sum of Gaussians can represent more complex functions with a handful of coefficients. As an example, let&rsquo;s take a set of data points and use least squares to fit varying numbers of Gaussians:</p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/gaussian_fit.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/gaussian_fit.png" alt="Gaussian_Fit"></a></p>
<center><i>Fitting Gaussians to a data set using least squares. The left graph shows a fit with a single Gaussian, the middle graph shows a fit with two Gaussians, and the right graph shows a fit with three Gaussians.</i></center>
<p>For this example I used <a href="http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html">curve_fit</a>[4] from the scipy optimization library, which uses a non-linear least squares algorithm. Notice how as I added more Gaussians, the resulting sum became a better approximation of the raw data.</p>
<h2 id="fitting-on-a-sphere">Fitting On a Sphere</h2>
<p>So far we&rsquo;ve been fitting 1D data sets, but the techniques we&rsquo;re using also work in multiple dimensions. So for instance, let&rsquo;s say we had a bunch of scattered samples in random directions on a sphere defined by a 2D spherical coordinate system. And let&rsquo;s say that these samples represent something like&hellip;oh I don&rsquo;t know&hellip;the amount of incoming lighting along an infinitesimally narrow ray oriented in that direction.  If we take all of these data points and throw some least squares at it, we can end up with a series of N Spherical Gaussians whose sum can serve as an approximation for radiance in any direction on the sphere! We just need our fitting algorithm to spit out the axis, amplitude, and sharpness of each Gaussian, or if we want can fix one of more of the SG parameters ahead of time and only fit the remaining parameters. It should be immediately obvious why this is useful, since a set of SG coefficients can be stored very compactly compared to a gigantic set of radiance samples. Of course if we only use a few Gaussians the resulting approximation will probably lose details from the original radiance function, but this is no different from spherical harmonics or other common techniques for storing approximate representations of radiance or irradiance. Let&rsquo;s take a look at what a fit actually looks like using an HDR environment map as input data:</p>
<p><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sgfit_comparison_ls_alt.png" alt="SGFit_Comparison_LS_Alt"></p>
<center><i>Approximating radiance on a sphere using a sum of Spherical Gaussians. The left image shows the original source radiance function taken from an HDR environment map. The middle image shows a least squares fit of 12 SG's, and right image shows a fit of 24 SG's.</i></center>
<p>These images were generated <a href="https://twitter.com/YuriyODonnell">Yuriy O&rsquo;Donnell</a>&rsquo;s <a href="https://github.com/kayru/Probulator">Probulator</a>[5], which is an excellent tool for comparing various ways of approximating radiance and irradiance on a sphere. One important thing to note here is that the  fit was only performed on the amplitude of the SG&rsquo;s: the axis and sharpness are pre-determined based on the number of SG&rsquo;s. Probulator generates the lobe axis directions using <a href="http://blog.marmakoide.org/?p=1">Vogel&rsquo;s method</a>[6], but any technique for distributing points on a sphere would also work. Fitting only the lobe amplitude significantly simplifies the solve, since there are less parameters to optimize. Solving for a single parameter also allows us to use <a href="https://en.wikipedia.org/wiki/Linear_least_squares_(mathematics)">linear least squares</a>[7], while fitting all of the parameters would require use of complex and expensive <a href="https://en.wikipedia.org/wiki/Non-linear_least_squares">non-linear least squares</a>[8] algorithms. Solving for less parameters also decreases the storage costs, since only the amplitudes need to be stored per-probe while the directions and sharpness can be global constants. Either way it&rsquo;s good to keep in mind when examining the results. In particular it helps explain why the white lobe in the middle image doesn&rsquo;t quite line up with the bright windows in the source environment. Aside from that, the results are probably what you would expect: doubling the number of lobes increases the possible complexity and sharpness of the resulting approximation, which in this case allows it to provide a better representation of some of the high-frequency details in the source image.</p>
<h2 id="going-negative">Going Negative</h2>
<p>One odd thing you might notice in the SG approximation is the overly dark areas towards the bottom-right of the sphere. They look somewhat similar to the darkening that can show up in SH approximations, which happens due to negative coefficients being used for the SH polynomials. It turns out that something very similar is happening with our SG fit: the least squares optimizations is returning negative coefficients for some of our lobes in an attempt to minimize the error of the resulting fit.If you&rsquo;re having trouble understanding why this would happen, let&rsquo;s go back to 1D for a quick example. For the last 1D example I cheated a bit: the data we were fitting our Gaussians to was actually just some random noise applied to the sum of 3 Gaussians. This is why our Gaussian fit so closely resembled the source data. This time, we&rsquo;ll fitting some lobes to a more complex data set:</p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/ls_negative_fit_data.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/ls_negative_fit_data.png" alt="LS_Negative_Fit_Data"></a></p>
<center><i>A data set with a discontinuity near 0, making it more difficult to fit curves to.</i></center>
<p>This time the data has a bunch of values near the center that have a value of zero. With such a data set it&rsquo;s now less obvious how a sum of Gaussians could approximate the values. If we throw least squares at the problem and have it fit two lobes, we get the following:</p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/ls_negative_fit.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/ls_negative_fit.png" alt="LS_Negative_Fit"></a></p>
<center><i>The result of using least squares to fit 2 Gaussian lobes to the above data set. The left graph shows the first lobe (red), the middle graph shows the second lobe (green), and the right graph shows the sum of the two lobes (blue) overlaid onto the original data set.</i></center>
<p>This time around the optimization resulted in a positive amplitude for the first lobe, but a <em>negative</em> amplitude for the second lobe. Looking at the sum overlaid onto to the data makes it clear why this happened: the positive lobe takes care of the all of the positive data points to the left and right, while the negative lobe brings the sum closer to zero in the middle of the graph. Upon closer inspection the negative actually causes the approximation to dip <em>below</em> zero into the negatives. We can assume that having this dip results in lower overall error for the approximation, since that&rsquo;s how least squares works.</p>
<p>In practice, having negative coefficients and negative values from our approximation can be undesirable. In fact when approximating radiance or irradiance negative values really just don&rsquo;t make sense, since they&rsquo;re physically impossible. In our experience we also found that the visual result of lighting a scene with negative lobes can be quite displeasing, since it tends to look very unnatural to have surfaces that are completely dark. Perhaps you remember this image from the first article showing what L2 SH looks like with a bright area light in the environment:</p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sh_ringing_comparison.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sh_ringing_comparison.png" alt="sh_ringing_comparison"></a></p>
<center><i>A sphere with a Lambertian diffuse BRDF being lit by a lighting environment with a strong area light source. The left image shows the ground-truth result of using monte-carlo integration. The middle image shows the result of projecting radiance onto L2 SH, and then computing irradiance. The right image shows the result of applying a windowing function to the L2 SH coefficients before computing irradiance.</i></center>
<p>We found that character faces in particular tended to look really bad when our SH light probes had strong negative lobes, and this turned out to be one of our motivations for investigating alternative approximations. We also ran into some trouble when attempting to compress signed floating point values in BC6H textures: some compressors didn&rsquo;t even support compressing to that format, and those that did had noticeably worse quality.</p>
<p>With that in mind, it would be nice to constrain a least squares solver in such a way that it only gave us positive coefficients. Fortunately for us such a technique exists, and it&rsquo;s known as <a href="https://en.wikipedia.org/wiki/Non-negative_least_squares">non-negative least squares</a>[9] (or NNLS for short). If we use that technique for fitting SG&rsquo;s to our original radiance function instead of standard least squares, we get this result instead:</p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sgfit_comparison_nnls_alt.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sgfit_comparison_nnls_alt.png" alt="SGFit_Comparison_NNLS_Alt"></a></p>
<center><i>Fitting SG's to a radiance function using a non-negative least squares solver. The left image shows the original source radiance function taken from an HDR environment map. The middle image shows an NNLS fit of 12 SG's, and right image shows a fit of 24 SG's.</i></center>
<p>This time we don&rsquo;t have the dark areas in the bottom right, since the fit only uses positive lobes. But unfortunately there&rsquo;s no free lunch here,  since the resulting approximation is also a bit &ldquo;blurrier&rdquo; compared to the normal least squares fit.</p>
<h2 id="comparing-irradiance">Comparing Irradiance</h2>
<p>Now that we&rsquo;ve covered how to generate an SG approximation of radiance from source data, we can take a look at how well it stacks up against other options for a simple use case. Probably the most obvious application is computing irradiance from the radiance approximation, which can be directly used to compute standard Lambertian diffuse lighting. The following images were captured using Probulator, and they show the Stanford Bunny being lit using a few common techniques for approximating irradiance:</p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/probe_basis_comparison_pisa.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/probe_basis_comparison_pisa_resized_589.png" alt="Probe_Basis_Comparison_Pisa"></a></p>
<center><i>A bunny model being lit by various irradiance approximations generated from the "Pisa" HDR environment map</i></center>
<p>With the exception of Valve&rsquo;s Ambient Cube, all of the approximations hold up very well when compared with the ground truth. The non-negative least squares fit is just a bit more washed out than the least squares fit, but both seem to produce perfectly acceptable results. The SH result is also very good, with no noticeable ringing artifacts. However this particular environment is a somewhat easier case, as the range of intensities isn&rsquo;t as large as you might find in some realistic lighting scenarios. For a more extreme case, let&rsquo;s now look at a comparison using the &ldquo;Ennis&rdquo; environment map:</p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/probe_basis_comparison.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/probe_basis_comparison_resized_587.png" alt="Probe_Basis_Comparison"></a></p>
<center><i>A bunny model being lit by various irradiance approximations generated from the "Ennis" HDR environment map</i></center>
<p>This time there&rsquo;s a much more noticeable difference between the various techniques. This is because the source environment map has a very bright window to the left, which effectively serves as a large area light source. With this particular environment the SG results start to compare pretty favorably to the SH or ambient cube approximations. The results from L2 SH have severe ringing artifacts, which manifests as areas that are either too dark or too bright on the side of the bunny facing to the right. Meanwhile , the windowed version of L2 SH blurs the lighting too much, making it appear as if the environment is more uniform than it really is. The ambient cube probe doesn&rsquo;t suffer from ringing, but it does have problems with the bright lighting from the left bleeding onto the top and side of the bunny. Looking at the least squares solve for 12 SG&rsquo;s, the result is pretty nice but there is a bit of ringing evident on the upper-right side of the bunny model. This ringing isn&rsquo;t present in the non-negative least squares solve, since all of the coefficients end up being positive.</p>
<p>As I mentioned earlier, these Probulator comparisons use fixed lobe directions and sharpness. Consequently we only need to store amplitude, meaning that the storage cost of the 12 SG lobes is equivalent to 12 sets of floating-point RGB coefficients (36 floats total). L2 SH requires 9 sets of RGB coefficients, which adds up to 27 floats. The ambient cube requires only 6 sets of RGB coefficents, which is half that of the SG solve. So for this particular comparison the SG representation of radiance requires the most storage, however this highlights one of the nice points using SG as your basis: you can solve for any number of lobes you&rsquo;d like, allowing you to choose easily trade off quality vs. storage and performance cost. Valve&rsquo;s ambient cube is only defined for 6 lobes, and that number can&rsquo;t be increased since the lobes must remain orthogonal to each other.</p>
<h2 id="fitting-on-a-hemisphere">Fitting on a Hemisphere</h2>
<p>For full lighting probes where the sampling surface can have any orientation, storing the radiance or irradiance on a sphere makes perfect sense. However it makes less sense if we would like to store baked lighting in 2D textures where all of the sample points lie on the surface of a mesh. For that case storing data for a full sphere is wasteful, since half of the data will point &ldquo;into&rdquo; the surface and therefore won&rsquo;t be useful. With SG&rsquo;s this is fairly trivial to correct: we can just choose to solve for lobes that only lie on the upper hemisphere surrounding the surface&rsquo;s normal direction.</p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sg_probe_vs_hemisphere1.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sg_probe_vs_hemisphere1.png" alt="SG_Probe_vs_Hemisphere"></a></p>
<center><i>The left side shows a configuration where 9 SG lobes are distributed about a sphere, forming a full spherical probe. The right side shows 5 SG lobes located on a hemisphere surrounding the normal of a surface (the blue arrow). </i></center>
<p>To fully generate the compact radiance representation for an entire 2D lightmap, we need to gather radiance samples at every texel location, and then perform a solve to fit the samples to a set of SG lobes. It&rsquo;s really no different from the spherical probe case we used as a testbed in Probulator, except now we&rsquo;re generating many probes. The other main differences is that for lightmap generating the appropriate radiance requires sampling a full 3D scene, as opposed to using an environment map as we did with Probulator. This sort of problem is best solved with a ray tracer, using an algorithm such as path tracing to compute the incoming radiance for a particular ray. The following image shows a visualization of  what the lightmap result looks like for a simple scene:</p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sg_debug_visualizer.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sg_debug_visualizer.png" alt="SG_Debug_Visualizer"></a></p>
<center><i>Hemispherical radiance probes generated at the texel locations of a 2D lightmap applied to a simple scene. Each probe uses 9 SG lobes oriented about the surface normal of the underlying geometry.</i></center>
<h2 id="specular">Specular</h2>
<p>In the previous article we covered techniques that can be used to compute a specular term from SG light sources. If we apply them to a set of lobes that approximate the incoming radiance, then we can compute an approximation of the full environment specular response. For small lobe counts this is only going to be practical for materials with a relatively high roughness. This is because our SG approximation of the incoming radiance won&rsquo;t be able to capture high-frequency details from the environment, and it would it be very obvious if those details were missing from the reflections on smooth surfaces. However for rougher surfaces where the BRDF itself starts to act as a low-pass filter, an SG approximation won&rsquo;t have as much noticeable error. As an example, here&rsquo;s what SG specular looks like for a test scene with a GGX roughness value of 0.25:</p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/bakinglab_indirectspecular_comparison.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/bakinglab_indirectspecular_comparison.png" alt="BakingLab_IndirectSpecular_Comparison"></a></p>
<center><i>Comparison of indirect specular approximation for a test scene with a GGX roughness of 0.25. The top-left image is a path-traced rendering of the final scene with full indirect and direct lighting. The top-right image shows the indirect environment specular term from SG9 lightmaps, with the exposure increased by 8x. The bottom left image shows the indirect specular term from L2 SH. The bottom right image shows the indirect specular term from a path-traced render of the scene.</i></center>
<p>Compared to the ground truth, the SG approximation does pretty well in some places and not-so-well in others. In general it captures a lot of the overall specular response, but suffers from some of the higher-frequency detail being absent in the probes. This results in certain areas looking a bit &ldquo;washed-out&rdquo;, such as the right-most wall of the scene. You can also see that the reflections of the cylinder, sphere, and torus are not properly represented in the SG version for the same reason. On the positive side, lightmap samples are pretty dense in terms of their spatial distribution. They&rsquo;re far more dense than what you typically achieve with sparse cubemap probes placed throughout the scene, which typically suffer from all kinds of occlusion and parallax artifacts. The SG specular also compares pretty favorably to the L2 SH result (despite having the same storage cost), which looks even more washed-out than the SG result. The SH implementation used a 3D lookup texture to store pre-computed SH coefficients, and you can see some interpolation artifacts from this method if you look at the far wall perpendicular to the camera.</p>
<h2 id="implementation-in-the-order-1886">Implementation in The Order: 1886</h2>
<p>In the first part of our <a href="http://blog.selfshadow.com/publications/s2015-shading-course/rad/s2015_pbs_rad_slides.pptx">presentation</a>[10] at last year&rsquo;s <a href="http://blog.selfshadow.com/publications/s2015-shading-course/">Physically Based Shading Course</a>[11], Dave covered some of these details and also shared some information about how we implemented SG lighting probes into The Order: 1886. Much of the implementation was very similar to what I&rsquo;ve described in this series of articles: we stored 5-12 SG lobes (the count was chosen per-level chunk) in our 2D lightmaps with fixed axis directions and sharpnesses, and we evaluated diffuse and specular lighting using the approximations that I outlined earlier. For dynamic meshes, we baked uniform 3D grids of spherical probes containing 9 SG lobes that were stored in 3D textures. The grids were defined by OBB&rsquo;s that were hand-placed in the scene by our lighting artists, along with density parameters. In both cases we made use of hardware texture filtering to interpolate between neighboring probes before computing per-pixel lighting.</p>
<p>Much of our implementation closely followed the work of <a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2009/12/sg.pdf">Wang</a>[12] and <a href="http://cg.cs.tsinghua.edu.cn/people/~kun/asg/">Xu</a>[13], at least in terms of the techniques used for approximating diffuse and specular lighting from a set of SG lobes. Where our work diverged quite a bit was in the choice to use fixed lobe directions and sharpness values. Both Wang and Xu generated their set of SG lobes by performing a solve on a single environment map, which produced the necessary axis, sharpness, and amplitude parameters. In our case, we always knew that we were going need many probes in order to maintain high-fidelity pre-computed lighting for our scenes. At the time (early 2014) we were already employing 2D lightmaps containing L1 H-basis hemispherical probes (4 coefficients) and 3D grids containing L2 spherical harmonics probes. Both could be quite dense in spatial terms, which allowed capturing important shadowing detail.</p>
<p>To make SG&rsquo;s work with for these requirements, we had to carefully consider our available trade-offs. After getting a simple test-bed up and running where we could bake 2D lightmaps for a scene, it became quickly apparent that varying the axis directions per-texel wasn&rsquo;t necessarily the best choice for us. Aside from the obvious issue of requiring more storage space and making the solve more complex and expensive, we also ran into issues resulting from interpolating the axis direction over a surface. The problem is most readily apparent at shadow boundaries: one texel might have visibility of a bright light source which causes a lobe to point in that direction, while its neighboring pixel might have no visibility and thus could end up with a lobe pointing in a completely different direction. The axis would then interpolate between the two directions for pixels between the two texels, which can cause noticeable specular shifting. This isn&rsquo;t necessarily an unsolvable problem (the <a href="http://www.cs.columbia.edu/cg/normalmap/">Frequency Domain Normal Map Filtering paper</a>[14] extended their EM solver with a term that attempts to align neighboring lobes for coherency), but considering our time and memory constraints it made sense to just sidestep the problem altogether. Ultimately we ended up using fixed lobe directions, using the UV tangent frame as the local coordinate space for lightmap probes. Tangent space is natural for this purpose since it&rsquo;s Z axis is the surface normal of the mesh, and also because it tends to be continuous over a mesh (you&rsquo;ll have discontinuities wherever you have UV seams, but artists tend to hide those as best they can anyway). For the 3D probe grids, the directions were in world space for simplicity.</p>
<p>After deciding to fix the lobe directions, we also ultimately decided to go with a fixed sharpness value as well. This of course has the same obvious benefits as fixing the axis direction (less storage, simpler solve), which were definitely appealing. However another motivating factor came from the way were doing our solve. Or rather, our lack of a proper solve. Our early testbed performed all lightmap baking on the CPU, which allowed us to easily integrate packages like <a href="http://eigen.tuxfamily.org/index.php?title=Main_Page">Eigen</a>[15] so that we could use a battle-tested least squares solver. However our actual production baking farm at Ready at Dawn uses a Cuda baker that leverages Nvidia&rsquo;s OptiX library to perform arbitrary ray tracing on a cluster of GPU&rsquo;s.While Cuda does have optimization libraries that could have achieved what we wanted, we faced a bigger problem: memory. Our baker worked by baking many sample points in parallel on the GPU, with a kernel program that would generate and trace the many rays required for monte carlo integration. When we previously used SH and H-basis this approach worked well: both SH and H-basis are built upon orthogonal basis functions, which allows for continuous integration by projecting each sample onto those basis functions. Gathering thousands of samples per-texel is feasible with this setup, since those samples don&rsquo;t need to be explicitly stored in memory. Instead, each new sample is projected onto the in-progress result for the texel and then discarded. This is not the case when performing a solve: the solver needs access to <em>all</em> of the samples, which means keeping them all around in memory. This is a big problem when you have many texels in flight simultaneously, and only a limited amount of GPU memory. Like the intepolation issue it&rsquo;s probably not unsolveable, but we really looking for a less risky approach that would be more of a drop-in replacement for the SH integration.</p>
<p>Ultimately we ended up saying &ldquo;screw it&rdquo;, and projected on the SG lobes as they formed an orthogonal basis (even though they didn&rsquo;t). Since the basis functions weren&rsquo;t orthogonal the results ended up rather blurry compared to a least squares solve, which muddied some of the detail in the source environment for a probe. Here&rsquo;s a comparison to show you what I mean:</p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sg_radiance_solve_comparison.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sg_radiance_solve_comparison.png" alt="SG_Radiance_Solve_Comparison"></a></p>
<center><i>A comparison of different techniques for computing a set of SG lobes that approximate the radiance from an environment map</i></center>
<p>Of the three techniques presented here, the naive projection is the worst at capturing the details from the source map and also the blurriest. As we saw earlier the least squares solve is the best at capturing sharp changes, but achieves this by over-darkening certain areas. NNLS is the &ldquo;just right&rdquo; fit for this particular case, doing a better job of capturing details compared to the projection but without using any negative lobes.</p>
<p>Before we switched to baking SG probes for our grids and lightmaps, we had a fairly standard system for applying pre-convolved cubemap probes that were hand-placed throughout our scenes. Previously these were our only source of environment specular lighting, but once we had SG&rsquo;s working we began to to use the SG specular approximation to compute environment specular directly from lightmaps and probe grids. Obviously our low number of SG&rsquo;s was not sufficient for accurately approximating environment specular for smooth and mirror-like surfaces, so our cubemap system remained relevent. We ended up coming up with a simple scheme to choose between cubemap and lightmap specular per-pixel based on the surface roughness, with a small zone in between where we would blend between the two specular sources. The following images taken from our SIGGRAPH slides use a color-coding to showing the specular source chosen for each pixel from one of our scenes:</p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sg_cubemap_transition_00.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sg_cubemap_transition_00.png" alt="SG_Cubemap_Transition_00"></a></p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sg_cubemap_transition_01.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/sg_cubemap_transition_01.png" alt="SG_Cubemap_Transition_01"></a></p>
<center><i>The top image shows a scene from The Order: 1886. The bottom image shows the same scene with a color coding applied to show the environment specular source for each pixel.</i></center>
<p>To finish off, here&rsquo;s some more comparison images taken from our SIGGRAPH slides:</p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/theorder_sgcomparison_00.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/theorder_sgcomparison_00.png" alt="TheOrder_SGComparison_00"></a></p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/theorder_sgcomparison_01.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/theorder_sgcomparison_01.png" alt="TheOrder_SGComparison_01"></a></p>
<p><a href="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/theorder_sgcomparison_02.png"><img src="/images/converted/sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/theorder_sgcomparison_02.png" alt="TheOrder_SGComparison_02"></a></p>
<center><i>Several comparison images from The Order: 1886 showing scenes with and without environment specular from SG lightmaps</i></center>
<h2 id="future-work">Future Work</h2>
<p>We were quite happy with the improvements that come from the SG baking pipeline we introduced for The Order, but we also feel like we&rsquo;ve barely scratched the surface. Our decision to use fixed lobe directions and sharpness values was probably the right one, but it also limits what we can do. When you take SG&rsquo;s and compare them to a fixed set of basis functions like SH, perhaps the biggest advantage is the fact that you can use an arbitrary combination of lobes to represent a mix of high and low-frequency features. So for instance you can represent a sun and a sky by having one wide lobe with the sky color, and one very narrow and bright lobe oriented towards the sun. We gave up that flexibility when we decided to go with our simpler ad-hoc projection, and it&rsquo;s something we&rsquo;d like to explore further in the future. But until then we can at least enjoy the benefits of having a representation that allows for an environment specular approximation and also avoids ringing artifacts when approximating diffuse lighting.</p>
<p>Aside from the solve, I also think it would be worth taking the time to investigate better approximations for the specular BRDF. In particular I would like to try using something better than just evaluating the cosine, Fresnel, and shadow-masking terms at the center of the warped BRDF lobe. The assumption that those terms are constant over the lobe break down the most when the roughness is high, and in our case we&rsquo;re only ever using SG specular for rough materials! Therefore I think it would be worth the effort to come up with a more accurate representation of those terms.</p>
<h2 id="references">References</h2>
<p>[1] <a href="https://en.wikipedia.org/wiki/Curve_fitting">Curve fitting</a><br>
[2] <a href="https://en.wikipedia.org/wiki/Regression_analysis">Regression analysis</a><br>
[3] <a href="https://en.wikipedia.org/wiki/Least_squares">Least squares</a><br>
[4] <a href="http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html#scipy-optimize-curve-fit">scipy.optimize.curve_fit</a><br>
[5] <a href="https://github.com/kayru/Probulator">Probulator</a><br>
[6] <a href="http://blog.marmakoide.org/?p=1">Spreading points on a disc and on a sphere</a><br>
[7] <a href="https://en.wikipedia.org/wiki/Linear_least_squares_(mathematics)">Linear least squares</a><br>
[8] <a href="https://en.wikipedia.org/wiki/Non-linear_least_squares">Non-linear least squares</a><br>
[9] <a href="https://en.wikipedia.org/wiki/Non-negative_least_squares">Non-negative least squares</a><br>
[10] <a href="http://blog.selfshadow.com/publications/s2015-shading-course/rad/s2015_pbs_rad_slides.pptx">Advanced Lighting R&amp;D at Ready At Dawn Studios</a><br>
[11] <a href="http://blog.selfshadow.com/publications/s2015-shading-course/">SIGGRAPH 2015 Course: Physically Based Shading in Theory and Practice</a><br>
[12] <a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2009/12/sg.pdf">All-Frequency Rendering of Dynamic, Spatially-Varying Reflectance</a><br>
[13] <a href="http://cg.cs.tsinghua.edu.cn/people/~kun/asg/">Anisotropic Spherical Gaussians</a><br>
[14] <a href="http://www.cs.columbia.edu/cg/normalmap/">Frequency Domain Normal Map Filtering</a><br>
[15] <a href="http://eigen.tuxfamily.org/index.php?title=Main_Page">Eigen</a><br></p>
<hr>
<h2 id="comments">Comments:</h2>
<h4 id="heading"></h4>
<p><a href="%22titouchose@yahoo.fr%22">Titouchose</a> - <time datetime="2018-02-01 10:03:18">Feb 4, 2018</time></p>
<p>This is so clearly explained that a simple lighting artist like me can finally understand how SH and SG works. thank you!</p>
<hr />
####
[VisorZ]( "s.pietschmann@campus.tu-berlin.de") - <time datetime="2017-01-29 13:37:59">Jan 0, 2017</time>
<p>Thank you very much for the time you&rsquo;ve put into your explanations. They are a consistent and easy to understand introduction to the usefulness of SHs and SGs.</p>
<hr />
####
[tomgroveblog](http://tomgroveblog.wordpress.com "tomkelgrove@yahoo.co.uk") - <time datetime="2016-10-20 03:54:05">Oct 4, 2016</time>
<p>Excellent series, thanks for posting!</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>SG Series Part 4: Specular Lighting From an SG Light Source</title>
			<link>https://therealmjp.github.io/posts/sg-series-part-4-specular-lighting-from-an-sg-light-source/</link>
			<pubDate>Mon, 10 Oct 2016 07:09:20 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/sg-series-part-4-specular-lighting-from-an-sg-light-source/</guid>
			<description>This is part 4 of a series on Spherical Gaussians and their applications for pre-computed lighting. You can find the other articles here:
Part 1 - A Brief (and Incomplete) History of Baked Lighting Representations
Part 2 - Spherical Gaussians 101
Part 3 - Diffuse Lighting From an SG Light Source
Part 4 - Specular Lighting From an SG Light Source
Part 5 - Approximating Radiance and Irradiance With SG&amp;rsquo;s</description>
			<content:encoded><![CDATA[<p><em>This is part 4 of a series on Spherical Gaussians and their applications for pre-computed lighting. You can find the other articles here:</em></p>
<p>Part 1 - <a href="../sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/">A Brief (and Incomplete) History of Baked Lighting Representations</a><br>
Part 2 - <a href="../sg-series-part-2-spherical-gaussians-101/">Spherical Gaussians 101</a><br>
Part 3 - <a href="../sg-series-part-3-diffuse-lighting-from-an-sg-light-source/">Diffuse Lighting From an SG Light Source</a><br>
Part 4 - <a href="../sg-series-part-4-specular-lighting-from-an-sg-light-source/">Specular Lighting From an SG Light Source</a><br>
Part 5 - <a href="../sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/">Approximating Radiance and Irradiance With SG&rsquo;s</a><br>
Part 6 - <a href="../sg-series-part-6-step-into-the-baking-lab/">Step Into The Baking Lab</a><br></p>
<p>In the <a href="../sg-series-part-3-diffuse-lighting-from-an-sg-light-source/">previous article</a>, we explored a few ways to compute the contribution of an SG light source when using a diffuse BRDF. While this is already useful, it would be nice to be able work with more complex view-dependent BRDF&rsquo;s so that we can also compute a specular contribution. In this article I&rsquo;ll explain a possible approach for approximating the response of a microfacet specular BRDF when applied to an SG light, and also introduce the concept of Anisotropic Spherical Gaussians.</p>
<h2 id="microfacet-specular-recap">Microfacet Specular Recap</h2>
<p>You probably recall from the past 5 years of <a href="http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf">physically based rendering presentations</a>[1] that a standard microfacet BRDF takes the following structure:</p>
<p>$$ f(\mathbf{i}, \mathbf{o}) = \frac{F(\mathbf{o}, \mathbf{h})\,G(\mathbf{i}, \mathbf{o}, \mathbf{h})\,D(\mathbf{h})} {4\,(\mathbf{n} \cdot \mathbf{i})\,(\mathbf{n} \cdot \mathbf{o})} $$</p>
<p>Recall that <em>D</em>(h) is the <em>distribution</em> term, which tells us the percentage of active microfacets for a particular combination of view and light vectors. It&rsquo;s also commonly known as the <em>normal distribution function</em>, or &ldquo;NDF&rdquo; for short. It&rsquo;s generally parameterized on a <em>roughness</em> parameter, which essentially describes the &ldquo;bumpiness&rdquo; of the underlying microgeometry. Lower roughness values lead to sharp mirror-like reflections with a very narrow and intense specular lobe, while higher roughness values lead to more broad reflections with a wider specular lobe. Most modern games (including The Order: 1886) use the GGX (AKA Trowbridge-Reitz) distribution for this purpose.</p>
<p><a href="https://www.desmos.com/calculator/hrybtwxnbi"><img src="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/ggx_distribution_resized_1024.png" alt="GGX_Distribution"></a></p>
<p><a href="https://www.desmos.com/calculator/hrybtwxnbi"><img src="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/ggx_distribution_0_1_resized_1024.png" alt="GGX_Distribution_0_1"></a></p>
<center><i>The top image shows a GGX distribution term with a roughness parameter of 0.5. The bottom image shows the same GGX distribution term with a roughness parameter of 0.1. For both graphs, the X axis represents the angle between the surface normal and the half vector. Click on either image for an interactive graph.</i></center>
<p>Next we have G(i, o, h) which is referred to as the <em>geometry term</em> or the <em>masking-shadow function</em>. Eric Heitz&rsquo;s <a href="http://jcgt.org/published/0003/02/03/">paper on the masking-shadow function</a>[2] has a fantastic explanation of how these terms work and why they&rsquo;re important, so I would strongly recommend reading through it if you haven&rsquo;t already. As Eric explains, the geometry term actually accounts for two different phenomena. The first is the local occlusion of reflections by other neighboring microfacets. Depending on the angle of the incoming lighting and the bumpiness of the microsurface, a certain amount of the lighting will be occluded by the surface itself, and this term attempts to model that. The other phenomenon handled by this term is visibility of the surface from the viewer. A surface that isn&rsquo;t visible naturally can&rsquo;t reflect light towards the viewer, and so the geometry term models this masking effect using the view direction and the roughness of the microsurface.</p>
<p><a href="https://www.desmos.com/calculator/w7wnltroeg"><img src="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/ggx_visibility_0_25_0_0.png" alt="GGX_Visibility_0_25_0_0"></a></p>
<center><i>The Smith visibility term for GGX as a function of the angle between the surface normal and the light direction. The roughness used is 0.25, and the angle between the normal and the view direction is 0. Click on the image for an interactive graph.</i></center>center
<p>Finally we have F(o, h), which is the <em>Fresnel</em> term. This familiar term determines the amount of light that is reflected vs. the amount that is refracted or absorbed, which varies depending on the index of refraction for a particular interface as well as the angle of incidence. For a microfacet BRDF we compute the Fresnel term using the angle between the active microfacet direction (the half vector) and the light or view direction (it is equivalent to use either). This generally causes the reflected intensity  to increase when both the viewing direction and the light direction are grazing with respect to the surface normal. In real-time graphics Schlick&rsquo;s approximation is typically used to represent the Fresnel term, since it&rsquo;s a bit cheaper than evaluating the actual Fresnel equations. It is also common to parameterize the Fresnel term on the reflectance at zero incidence (referred to as &ldquo;F0&rdquo;) instead of directly working with an index of refraction.</p>
<p><a href="https://www.desmos.com/calculator/wcwkzazku0"><img src="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/schlick_fresnel.png" alt="Schlick_Fresnel"></a></p>
<center><i>Schlick's approximation of the Fresnel term as a function of the angle between the half vector and the light direction. The graph uses a value of 0.04 for F0. Click on the image for an interactive graph.</i></center>
<h2 id="specularfrom-an-sg-light">Specular From an SG Light</h2>
<p>Let&rsquo;s now return to our example of a Spherical Gaussian light source. In the previous article we explored how to approximate the purely diffuse contribution from such a light source, but how do we handle the specular response? If we go down this road, we ideally want to do it in a way that uses a microfacet specular model so that we can remain consistent with our lighting from punctual light sources and IBL probes. If we start out by just plugging our BRDF and SG light into the rendering equation we get this monstrosity:</p>
<p>$$ L_{o}(\mathbf{o}, \mathbf{x}) = \int_{\Omega}  \frac{F(\mathbf{o}, \mathbf{h})\,G(\mathbf{i}, \mathbf{o}, \mathbf{h})\,D(\mathbf{h})G_{l}(\mathbf{i};\mathbf{\mu},\lambda,a)cos(\theta_{i})d\Omega} {4cos(\theta_{i})cos(\theta_{o})} $$</p>
<p>$$ L_{o}(\mathbf{o}, \mathbf{x}) = \int_{\Omega}  \frac{F(\mathbf{o}, \mathbf{h})\,G(\mathbf{i}, \mathbf{o}, \mathbf{h})\,D(\mathbf{h})ae^{\lambda(\mathbf{\mu} \cdot \mathbf{i} - 1)}d\Omega} {4cos(\theta_{o})} $$</p>
<p>Unlike diffuse we now have multiple terms inside the integral, many of which are view-dependent. This suggests that we will need to combine several aggressive optimizations in order to get anywhere close to our desired result.</p>
<h3 id="the-distribution-term">The Distribution Term</h3>
<p>Let&rsquo;s start out by taking the distribution term on its own, since it&rsquo;s arguably the most important part of the specular BRDF. The distribution determines the overall shape and intensity of the resulting specular highlight, and has a widely-varying frequency response over the domain of possible roughness values. If we look at the graph of the GGX distribution from the earlier section, the shape does resemble a Gaussian. Unfortunately it&rsquo;s not an exact match: the GGX distribution has the characteristic narrow highlight and long  tails which we can&rsquo;t match using a single SG. We could get a closer fit by summing multiple Gaussians, but for this article we&rsquo;ll keep things simple by sticking with a single lobe. If we go back to <a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2009/12/sg.pdf">Wang et al.</a>&rsquo;s paper[3] that we referenced earlier, we can see that they suggest a very simple fit of an SG to a Cook-Torrance distribution term:</p>
<p>$$ D(\mathbf{h})= e^{-(arccos(\mathbf{h} \cdot \mathbf{n})/m)^{2}} \approx G(\mathbf{h};\mathbf{n},\frac{2}{m^2},1) $$</p>
<p>If we look closely, we can see that they&rsquo;re actually fitting for the Gaussian model mentioned in the <a href="https://graphics.pixar.com/library/ReflectanceModel/paper.pdf">original Cook-Torrance paper</a>[4], which is similar to the one used in the <a href="http://www.graphics.cornell.edu/~westin/pubs/TorranceSparrowJOSA1967.pdf">Torrance-Sparrow model</a>[5]. Note that this should not be confused the with Beckmann distribution that&rsquo;s also mentioned in the Cook-Torrance paper, which is actually a 2D Gaussian in the slope domain (AKA parallel plane domain). However the shape isn&rsquo;t even the biggest problem here, as the variant they&rsquo;re using isn&rsquo;t normalized. This means the peak of the Gaussian will always have a height of 1.0, rather than shrinking when the roughness increases and growing when the roughness decreases. Fortunately this is really easy to fix, since we have a simple analytical formula for computing the integral of an SG. Therefore if we set the amplitude to 1 over the integral, we end up with a normalized distribution:</p>
<p>$$ D_{SG}(\mathbf{h})= G(\mathbf{h};\mathbf{n},\frac{2}{m^2}, \frac{1}{\pi m^2}) $$</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">SG</span> <span class="nf">DistributionTermSG</span><span class="p">(</span><span class="n">in</span> <span class="n">float3</span> <span class="n">direction</span><span class="p">,</span> <span class="n">in</span> <span class="kt">float</span> <span class="n">roughness</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">SG</span> <span class="n">distribution</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">distribution</span><span class="p">.</span><span class="n">Axis</span> <span class="o">=</span> <span class="n">direction</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">m2</span> <span class="o">=</span> <span class="n">roughness</span> <span class="o">*</span> <span class="n">roughness</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">distribution</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">/</span> <span class="n">m2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">distribution</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">=</span> <span class="mf">1.0f</span> <span class="o">/</span> <span class="p">(</span><span class="n">Pi</span> <span class="o">*</span> <span class="n">m2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">distribution</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Let&rsquo;s take a look at a graph of our distribution term, and see how far off it is from our target:</p>
<p><a href="https://www.desmos.com/calculator/yldtkvljsh"><img src="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/sg_distribution_0_25.png" alt="SG_Distribution_0_25"></a></p>
<p><a href="https://www.desmos.com/calculator/yldtkvljsh"><img src="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/sg_distribution_0_5.png" alt="SG_Distribution_0_5"></a></p>
<center><i>Top graph shows a comparison between GGX, Beckmann, normalized Gaussian, and SG distribution terms with a roughness of 0.25. The bottom shows the same comparison with  a roughness of 0.5. Click on either image for an interactive graph.</i></center>
<p>It should come as no surprise that our SG distribution is almost an exact match for a normalized version of a Gaussian distribution. When the roughness is lower it&rsquo;s also a fairly close match for Beckmann, but for higher roughness the difference gets to be quite large. In both cases our distribution isn&rsquo;t a perfect fit for GGX, but it&rsquo;s a workable approximation.</p>
<h3 id="warping-between-domains">Warping Between Domains</h3>
<p>So we now have a normal distribution function in SG format, but unfortunately we&rsquo;re not quite ready to use it as-is. The problem is that we&rsquo;ve defined our distribution in the half-vector domain: the axis of the SG points in the direction of the surface normal, and we use the half-vector as our sampling direction. If we want to use an SG product to compute the result of multiplying our distribution with an SG light source, then we need to ensure that the distribution lobe is in the same domain as our light source. Another way to think about this is that center of our distribution shifts depending on viewing angle, since the half-vector also shifts as the camera moves.</p>
<p>In order to make sure that our distribution lobe is in the correct domain, we need &ldquo;warp&rdquo; our distribution so that it lines up with the current BRDF slice for our viewing direction. If you&rsquo;re wondering what a BRDF slice is, it basically tells you &ldquo;if I pick a particular view direction. what is the value of my  BRDF for a given light direction?&rdquo;. So if you had a mirror BRDF, the slice would just be a single ray pointing in the direction of the view ray reflected off the surface normal. For microfacet specular BRDF&rsquo;s, you get a lobe that&rsquo;s roughly centered around the reflected view direction. Here&rsquo;s what a polar graph of a GGX BRDF slice looks like if we only consider the distribution term:</p>
<p><a href="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/ggx_distribution_brdf_slice.png"><img src="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/ggx_distribution_brdf_slice.png" alt="GGX_Distribution_BRDF_Slice"></a></p>
<center><i>Polar graph of the GGX distribution term from two different viewing angles. The blue line is the view direction, the green line is the surface normal, and the pink line is the reflected view direction. The top image shows the resulting slice when the viewing angle is 0 degrees, and the bottom images shows the resulting slice when the viewing angle is 45 degrees.</i></center>
<p>Wang et al. proposed a simple spherical warp operator that would orient the distribution lobe about the reflected view direction, while also modifying the sharpness to take into account the differential area at the original center of the lobe:</p>
<p>$$ \mu_{w}=2(\mathbf{o} \cdot \mu_{d})\mu_{d} - \mathbf{o} $$</p>
<p>$$ \lambda_{w}=\frac{\lambda_{d}}{4|\mu_{d} \cdot \mathbf{o}|} $$</p>
<p>$$ a_{w} = a_{d} $$</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">SG</span> <span class="nf">WarpDistributionSG</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">ndf</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">view</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">SG</span> <span class="n">warp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">warp</span><span class="p">.</span><span class="n">Axis</span> <span class="o">=</span> <span class="n">reflect</span><span class="p">(</span><span class="o">-</span><span class="n">view</span><span class="p">,</span> <span class="n">ndf</span><span class="p">.</span><span class="n">Axis</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">warp</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">=</span> <span class="n">ndf</span><span class="p">.</span><span class="n">Amplitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">warp</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">=</span> <span class="n">ndf</span><span class="p">.</span><span class="n">Sharpness</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">warp</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">/=</span> <span class="p">(</span><span class="mf">4.0f</span> <span class="o">*</span> <span class="n">max</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">ndf</span><span class="p">.</span><span class="n">Axis</span><span class="p">,</span> <span class="n">view</span><span class="p">),</span> <span class="mf">0.0001f</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">warp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Let&rsquo;s now look at the result of that warp, and compare it to what the actual GGX distribution looks like:</p>
<p><a href="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/sg_ggx_distribution_slices.png"><img src="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/sg_ggx_distribution_slices.png" alt="SG_GGX_Distribution_Slices"></a></p>
<center><i>Result of applying a spherical warp to the SG distribution term (green) compared with the actual GGX distribution (red). The top graph shows a viewing angle of 0 degrees, and the bottom graph shows a viewing angle of 45 degrees.</i></center>
<p>A quick look at the graph shows us that the shape is a bit off, but our warped lobe is ending up in approximately in the right spot. We&rsquo;ll revisit the lobe shape later, but for now let&rsquo;s try combining our distribution with the rest of our BRDF.</p>
<h3 id="approximating-the-remaining-terms">Approximating The Remaining Terms</h3>
<p>In the previous section we figured out how to obtain an SG approximation of our distribution term, and also warp it so that it&rsquo;s in the correct domain for multiplication with our SG light source. Using our SG product operator would allow to us to represent the result of multiplying the distribution with the light source as another SG, which we could then multiply with other terms using SG operators&hellip;or at least we could if we were to represent the remaining terms as SG&rsquo;s. Unfortunately this turns out to be a problem: the geometry and Fresnel terms are nothing at all like a Gaussian, which rules out approximating them as an SG. Wang et al. sidestep this issue by making the somewhat-weak assumption that the values of these terms will be constant across the entire BRDF lobe, which allows them to pull the terms out of the integral and evaluate them only for the axis direction of the BRDF lobe. This allows the resulting BRDF to still capture some of the glancing angle effects, with similar performance cost to evaluating those terms for a punctual light source. The downside is that the error of these terms will increase as the BRDF lobe becomes wider (increasing roughness), since the value of the geometry and Fresnel terms will vary more the further they are from the lobe center. Putting it all together gives the following specular BRDF:</p>
<p>$$ f_{sg}(\mathbf{i}, \mathbf{o}) = \frac{F(\mathbf{o}, \mathbf{h_{w}})\,G(\mathbf{\mu_{w}}, \mathbf{o}, \mathbf{h_{w}})\, \frac{1}{\pi m^2}e^{\lambda_{w}(\mathbf{\mu_{w}} \cdot \mathbf{i} - 1)}} {4\,(\mathbf{n} \cdot \mathbf{\mu_{w}})\,(\mathbf{n} \cdot \mathbf{o})} $$</p>
<p>$$ \mathbf{h_{w}} = \frac{\mathbf{o} + \mathbf{\mu_{w}}}{||\mathbf{o} + \mathbf{\mu_{w}}||} $$</p>
<p>The last thing we need to account for is the cosine term that needs to be multiplied with the BRDF inside of the hemispherical integral. Wang et al. suggest using an SG product to compute an SG representing the result of multiplying the distribution term SG and their SG approximation of a clamped cosine lobe, which can then be multiplied with the lighting lobe using an SG inner product. In order to avoid another expensive SG operation, we will instead use the same approach that we used for geometry and Fresnel terms and evaluate the cosine term using the BRDF lobe axis direction. Implementing it in shader code gives us the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">float</span> <span class="nf">GGX_V1</span><span class="p">(</span><span class="n">in</span> <span class="kt">float</span> <span class="n">m2</span><span class="p">,</span> <span class="n">in</span> <span class="kt">float</span> <span class="n">nDotX</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mf">1.0f</span> <span class="o">/</span> <span class="p">(</span><span class="n">nDotX</span> <span class="o">+</span> <span class="n">sqrt</span><span class="p">(</span><span class="n">m2</span> <span class="o">+</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">m2</span><span class="p">)</span> <span class="o">*</span> <span class="n">nDotX</span> <span class="o">*</span> <span class="n">nDotX</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">SpecularTermSGWarp</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">light</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">normal</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                          <span class="n">in</span> <span class="kt">float</span> <span class="n">roughness</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">view</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                          <span class="n">in</span> <span class="n">float3</span> <span class="n">specAlbedo</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Create an SG that approximates the NDF.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SG</span> <span class="n">ndf</span> <span class="o">=</span> <span class="n">DistributionTermSG</span><span class="p">(</span><span class="n">normal</span><span class="p">,</span> <span class="n">roughness</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Warp the distribution so that our lobe is in the same
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// domain as the lighting lobe
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SG</span> <span class="n">warpedNDF</span> <span class="o">=</span> <span class="n">WarpDistributionSG</span><span class="p">(</span><span class="n">ndf</span><span class="p">,</span> <span class="n">view</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Convolve the NDF with the SG light
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">float3</span> <span class="n">output</span> <span class="o">=</span> <span class="n">SGInnerProduct</span><span class="p">(</span><span class="n">warpedNDF</span><span class="p">,</span> <span class="n">light</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Parameters needed for the visibility
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">float3</span> <span class="n">warpDir</span> <span class="o">=</span> <span class="n">warpedNDF</span><span class="p">.</span><span class="n">Axis</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">m2</span> <span class="o">=</span> <span class="n">roughness</span> <span class="o">*</span> <span class="n">roughness</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">nDotL</span> <span class="o">=</span> <span class="n">saturate</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">normal</span><span class="p">,</span> <span class="n">warpDir</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">nDotV</span> <span class="o">=</span> <span class="n">saturate</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">normal</span><span class="p">,</span> <span class="n">view</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">h</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">warpedNDF</span><span class="p">.</span><span class="n">Axis</span> <span class="o">+</span> <span class="n">view</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Visibility term evaluated at the center of
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// our warped BRDF lobe
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">output</span> <span class="o">*=</span> <span class="n">GGX_V1</span><span class="p">(</span><span class="n">m2</span><span class="p">,</span> <span class="n">nDotL</span><span class="p">)</span> <span class="o">*</span> <span class="n">GGX_V1</span><span class="p">(</span><span class="n">m2</span><span class="p">,</span> <span class="n">nDotV</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Fresnel evaluated at the center of our warped BRDF lobe
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">float</span> <span class="n">powTerm</span> <span class="o">=</span> <span class="n">pow</span><span class="p">((</span><span class="mf">1.0f</span> <span class="o">-</span> <span class="n">saturate</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">warpDir</span><span class="p">,</span> <span class="n">h</span><span class="p">))),</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">output</span> <span class="o">*=</span> <span class="n">specAlbedo</span> <span class="o">+</span> <span class="p">(</span><span class="mf">1.0f</span> <span class="o">-</span> <span class="n">specAlbedo</span><span class="p">)</span> <span class="o">*</span> <span class="n">powTerm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Cosine term evaluated at the center of the BRDF lobe
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">output</span> <span class="o">*=</span> <span class="n">nDotL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">max</span><span class="p">(</span><span class="n">output</span><span class="p">,</span> <span class="mf">0.0f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Let&rsquo;s now (finally) take a look at what our specular approximation looks like for a scene being lit by an SG light source:</p>
<p><a href="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/sglight_specular_sgwarp.png"><img src="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/sglight_specular_sgwarp.png" alt="SGLight_Specular_SGWarp"></a></p>
<center><i>A scene being lit by an SG light source using our diffuse and specular approximations. The scene is using a uniform roughness of 0.128.</i></center>
<h2 id="going-anisotropic">Going Anisotropic</h2>
<p>So if we look at the specular highlights in the above image, you may notice that while the highlights on the red and green walls look pretty good, the highlight on floor seems a bit off. The highlight is rather wide and rounded, and our intuition tells that a highlight viewed at a grazing angle should appear vertically stretched across the floor. To determine why the look is so off, we need to revisit our warp of the distribution term. Previously when we looked at the polar graph of the distribution I noted that the shape of the resulting lobe was a bit off, even though it was oriented in approximately the right direction to line up with the BRDF slice. To get a better idea of what&rsquo;s going on, let&rsquo;s now take a look at a 3D graph of the GGX distribution term:</p>
<p><a href="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/ggx_distribution_stretch.png"><img src="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/ggx_distribution_stretch.png" alt="GGX_Distribution_Stretch"></a></p>
<center><i>3D graph of the GGX distribution term. The left image shows the distribution when the viewing angle is very low, while   shows the distribution when the viewing angle is very steep.</i></center>
<p>Looking at the left image where the angle between the view direction and  the surface normal are very low, the distribution is radially symmetrical just like an SG lobe. However as the viewing angle increases the lobe begins to stretch, looking more and more like the non-symmetrical lobe that we see in the right image. The stretching of the lobe is what causes the stretched highlights that occur when applying the BRDF, and our warped SG is unable to properly represent it since it must remain radially symmetric about its axis.</p>
<p>Luckily for us, there is a better way. In 2013 Xu et al.released a paper titled <a href="http://cg.cs.tsinghua.edu.cn/people/~kun/asg/">Anisotropic Spherical Gaussians</a>[6], where they explain how they extended SG&rsquo;s to support anisotropic lobe width/sharpness. They&rsquo;re defined like this:</p>
<p>$$ G(\mathbf{v};[\mu_x, \mu_y, \mu_z],[\lambda_x, \lambda_y],a) = a \cdot S(\mathbf{v},\mu_z) e^{-\lambda_x(\mathbf{v} \cdot \mu_x) - \lambda_y(\mathbf{v} \cdot \mu_y)} $$</p>
<p>Instead of having a single axis direction, an ASG now has \(  \mu_x &amp;s=1 $, \(  \mu_y \), and \(  \mu_z \), which are three orthogonal vectors forming a complete basis. It&rsquo;s very similar to a tangent frame, where the normal, tangent, and bitangent together make up an orthonormal basis. With an ASG you also now have two separate sharpness parameters, \(  \lambda_x \) and \( \lambda_y \), which control the sharpness with respect to \(  \mu_x \) and \(  \mu_y \). So for example setting \(  \lambda_x \) to 16 and \( \lambda_y \) to 64 will result in stretched Gaussian lobe that&rsquo;s skinnier along the \( \lambda_y \) direction, and with its center located at \(  \mu_z \). Visualizing such an ASG on the surface of a sphere gives you this:</p>
<p><a href="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/asg_sphere.png"><img src="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/asg_sphere_resized_300.png" alt="ASG_Sphere"></a></p>
<center> _An Anisotropic Spherical Gaussian visualized on the surface of a sphere. \\(  \lambda\_x \\) has a value of 16, and \\(  \lambda\_y \\) has a value of 64.</i></center>
<p>Like SG&rsquo;s, the equations lend themselves to simple HLSL implementations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">ASG</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">Amplitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">BasisZ</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">BasisX</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">BasisY</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">SharpnessX</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">SharpnessY</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">EvaluateASG</span><span class="p">(</span><span class="n">in</span> <span class="n">ASG</span> <span class="n">asg</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">dir</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">sTerm</span> <span class="o">=</span> <span class="n">saturate</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">asg</span><span class="p">.</span><span class="n">BasisZ</span><span class="p">,</span> <span class="n">dir</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">lambdaTerm</span> <span class="o">=</span> <span class="n">asg</span><span class="p">.</span><span class="n">SharpnessX</span> <span class="o">*</span> <span class="n">dot</span><span class="p">(</span><span class="n">dir</span><span class="p">,</span> <span class="n">asg</span><span class="p">.</span><span class="n">BasisX</span><span class="p">)</span> <span class="o">*</span> <span class="n">dot</span><span class="p">(</span><span class="n">dir</span><span class="p">,</span> <span class="n">asg</span><span class="p">.</span><span class="n">BasisX</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">muTerm</span> <span class="o">=</span> <span class="n">asg</span><span class="p">.</span><span class="n">SharpnessY</span> <span class="o">*</span> <span class="n">dot</span><span class="p">(</span><span class="n">dir</span><span class="p">,</span> <span class="n">asg</span><span class="p">.</span><span class="n">BasisY</span><span class="p">)</span> <span class="o">*</span> <span class="n">dot</span><span class="p">(</span><span class="n">dir</span><span class="p">,</span> <span class="n">asg</span><span class="p">.</span><span class="n">BasisY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">asg</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">*</span> <span class="n">sTerm</span> <span class="o">*</span> <span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">lambdaTerm</span> <span class="o">-</span> <span class="n">muTerm</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The ASG paper provides us with formulas for two operations that we can use to improve the quality of our specular approximation for SG light sources. The first is a new warping operator that can take an NDF represented as an isotropic SG, and stretch it along the view direction to produce an ASG that better represents the actual BRDF. The other helpful forumula it provides is for convolving an ASG with an SG, which we can use to convolve a anisotropically warped NDF lobe with an SG lighting lobe. Let&rsquo;s take a look at how their improved warp looks when graphing the NDF for a large viewing angle:</p>
<p><a href="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/distribution_anisotropy_3d.png"><img src="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/distribution_anisotropy_3d.png" alt="Distribution_Anisotropy_3D"></a></p>
<center><i>The left image is a 3D graph of the distribution term when using a spherical warp. The middle image is the resulting distribution term when using an anisotropic warp. The right image is the actual GGX distribution term_</i></center>
<p>The anisotropic distribution looks much closer to the actual GGX NDF, since it now has the vertical stretching that we were missing. Let&rsquo;s now implement their formulas in HLSL so we can try the new warp in our test scene:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">ConvolveASG_SG</span><span class="p">(</span><span class="n">in</span> <span class="n">ASG</span> <span class="n">asg</span><span class="p">,</span> <span class="n">in</span> <span class="n">SG</span> <span class="n">sg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// The ASG paper specifes an isotropic SG as
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// exp(2 * nu * (dot(v, axis) - 1)),
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// so we must divide our SG sharpness by 2 in order
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// to get the nup parameter expected by the ASG formula
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">float</span> <span class="n">nu</span> <span class="o">=</span> <span class="n">sg</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">*</span> <span class="mf">0.5f</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">ASG</span> <span class="n">convolveASG</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">convolveASG</span><span class="p">.</span><span class="n">BasisX</span> <span class="o">=</span> <span class="n">asg</span><span class="p">.</span><span class="n">BasisX</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">convolveASG</span><span class="p">.</span><span class="n">BasisY</span> <span class="o">=</span> <span class="n">asg</span><span class="p">.</span><span class="n">BasisY</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">convolveASG</span><span class="p">.</span><span class="n">BasisZ</span> <span class="o">=</span> <span class="n">asg</span><span class="p">.</span><span class="n">BasisZ</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">convolveASG</span><span class="p">.</span><span class="n">SharpnessX</span> <span class="o">=</span> <span class="p">(</span><span class="n">nu</span> <span class="o">*</span> <span class="n">asg</span><span class="p">.</span><span class="n">SharpnessX</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">nu</span> <span class="o">+</span> <span class="n">asg</span><span class="p">.</span><span class="n">SharpnessX</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">convolveASG</span><span class="p">.</span><span class="n">SharpnessY</span> <span class="o">=</span> <span class="p">(</span><span class="n">nu</span> <span class="o">*</span> <span class="n">asg</span><span class="p">.</span><span class="n">SharpnessY</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">nu</span> <span class="o">+</span> <span class="n">asg</span><span class="p">.</span><span class="n">SharpnessY</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">convolveASG</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">=</span> <span class="n">Pi</span> <span class="o">/</span> <span class="n">sqrt</span><span class="p">((</span><span class="n">nu</span> <span class="o">+</span> <span class="n">asg</span><span class="p">.</span><span class="n">SharpnessX</span><span class="p">)</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="n">nu</span> <span class="o">+</span> <span class="n">asg</span><span class="p">.</span><span class="n">SharpnessY</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">asgResult</span> <span class="o">=</span> <span class="n">EvaluateASG</span><span class="p">(</span><span class="n">convolveASG</span><span class="p">,</span> <span class="n">sg</span><span class="p">.</span><span class="n">Axis</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">asgResult</span> <span class="o">*</span> <span class="n">sg</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">*</span> <span class="n">asg</span><span class="p">.</span><span class="n">Amplitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ASG</span> <span class="nf">WarpDistributionASG</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">ndf</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">view</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ASG</span> <span class="n">warp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Generate any orthonormal basis with Z pointing in the
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// direction of the reflected view vector
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">warp</span><span class="p">.</span><span class="n">BasisZ</span> <span class="o">=</span> <span class="n">reflect</span><span class="p">(</span><span class="o">-</span><span class="n">view</span><span class="p">,</span> <span class="n">ndf</span><span class="p">.</span><span class="n">Axis</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">warp</span><span class="p">.</span><span class="n">BasisX</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">cross</span><span class="p">(</span><span class="n">ndf</span><span class="p">.</span><span class="n">Axis</span><span class="p">,</span> <span class="n">warp</span><span class="p">.</span><span class="n">BasisZ</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">warp</span><span class="p">.</span><span class="n">BasisY</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">cross</span><span class="p">(</span><span class="n">warp</span><span class="p">.</span><span class="n">BasisZ</span><span class="p">,</span> <span class="n">warp</span><span class="p">.</span><span class="n">BasisX</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">dotDirO</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">view</span><span class="p">,</span> <span class="n">ndf</span><span class="p">.</span><span class="n">Axis</span><span class="p">),</span> <span class="mf">0.0001f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Second derivative of the sharpness with respect to how
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// far we are from basis Axis direction
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">warp</span><span class="p">.</span><span class="n">SharpnessX</span> <span class="o">=</span> <span class="n">ndf</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">/</span> <span class="p">(</span><span class="mf">8.0f</span> <span class="o">*</span> <span class="n">dotDirO</span> <span class="o">*</span> <span class="n">dotDirO</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">warp</span><span class="p">.</span><span class="n">SharpnessY</span> <span class="o">=</span> <span class="n">ndf</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">/</span> <span class="mf">8.0f</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">warp</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">=</span> <span class="n">ndf</span><span class="p">.</span><span class="n">Amplitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">warp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">SpecularTermASGWarp</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">light</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">normal</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                           <span class="n">in</span> <span class="kt">float</span> <span class="n">roughness</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">view</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                           <span class="n">in</span> <span class="n">float3</span> <span class="n">specAlbedo</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Create an SG that approximates the NDF
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SG</span> <span class="n">ndf</span> <span class="o">=</span> <span class="n">DistributionTermSG</span><span class="p">(</span><span class="n">normal</span><span class="p">,</span> <span class="n">roughness</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Apply a warpring operation that will bring the SG from
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// the half-angle domain the the the lighting domain.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">ASG</span> <span class="n">warpedNDF</span> <span class="o">=</span> <span class="n">WarpDistributionASG</span><span class="p">(</span><span class="n">ndf</span><span class="p">,</span> <span class="n">view</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Convolve the NDF with the light
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">float3</span> <span class="n">output</span> <span class="o">=</span> <span class="n">ConvolveASG_SG</span><span class="p">(</span><span class="n">warpedNDF</span><span class="p">,</span> <span class="n">light</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Parameters needed for evaluating the visibility term
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">float3</span> <span class="n">warpDir</span> <span class="o">=</span> <span class="n">warpedNDF</span><span class="p">.</span><span class="n">BasisZ</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">m2</span> <span class="o">=</span> <span class="n">roughness</span> <span class="o">*</span> <span class="n">roughness</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">nDotL</span> <span class="o">=</span> <span class="n">saturate</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">normal</span><span class="p">,</span> <span class="n">warpDir</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">nDotV</span> <span class="o">=</span> <span class="n">saturate</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">normal</span><span class="p">,</span> <span class="n">view</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">h</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">warpDir</span> <span class="o">+</span> <span class="n">view</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Visibility term
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">output</span> <span class="o">*=</span> <span class="n">GGX_V1</span><span class="p">(</span><span class="n">m2</span><span class="p">,</span> <span class="n">nDotL</span><span class="p">)</span> <span class="o">*</span> <span class="n">GGX_V1</span><span class="p">(</span><span class="n">m2</span><span class="p">,</span> <span class="n">nDotV</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Fresnel
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">float</span> <span class="n">powTerm</span> <span class="o">=</span> <span class="n">pow</span><span class="p">((</span><span class="mf">1.0f</span> <span class="o">-</span> <span class="n">saturate</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">warpDir</span><span class="p">,</span> <span class="n">h</span><span class="p">))),</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">output</span> <span class="o">*=</span> <span class="n">specAlbedo</span> <span class="o">+</span> <span class="p">(</span><span class="mf">1.0f</span> <span class="o">-</span> <span class="n">specAlbedo</span><span class="p">)</span> <span class="o">*</span> <span class="n">powTerm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Cosine term
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">output</span> <span class="o">*=</span> <span class="n">nDotL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">max</span><span class="p">(</span><span class="n">output</span><span class="p">,</span> <span class="mf">0.0f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>If we swap out our old specular approximation for one that uses an anisotropic warp, our test scene now looks much better!</p>
<p><a href="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/sglight_asg_warp.png"><img src="/images/converted/sg-series-part-4-specular-lighting-from-an-sg-light-source/sglight_asg_warp.png" alt="SGLight_ASG_Warp"></a></p>
<center><i>Diffuse and specular approximations applied to an SG light source, using an anisotropic warp to approximate the NDF as an ASG.</i></center>
<h2 id="visually-comparing-the-brdf">Visually Comparing the BRDF</h2>
<p>Many of the images that I used for visually comparing the SG BRDF approximations were generated using Disney&rsquo;s <a href="https://github.com/wdas/brdf">BRDF Explorer</a>. When we were doing our initial research into SG&rsquo;s and figuring out how to implement them, BRDF Explorer was extremely valuable both for understanding the concepts and for experimenting with different variations. If you&rsquo;d like to this yourself, there&rsquo;s a very easy way to do that courtesy of <a href="https://twitter.com/pastasfuture">Nick Brancaccio</a>. Nick was kind of enough to create his own awesome <a href="https://depot.floored.com/brdf_explorer">WebGL version of BRDF Explorer</a>, and it comes pre-loaded with options for comparing an approximate SG specular BRDF with the GGX BRDF. I would recommend checking it out if you&rsquo;d like to play around with the BRDF&rsquo;s and make some pretty 3D graphs!</p>
<p><em>Update 07/04/2023: it appears that the URL for the WebGL BRDF explorer no longer works, and is likely gone forever</em></p>
<h2 id="references">References</h2>
<p>[1] <a href="http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf">Background: Physics and Math of Shading (SIGGRAPH 2013 Course: Physically Based Shading in Theory and Practice)</a><br>
[2] <a href="http://jcgt.org/published/0003/02/03/">Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs</a><br>
[3] <a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2009/12/sg.pdf">All-Frequency Rendering of Dynamic, Spatially-Varying Reflectance</a><br>
[4] <a href="https://graphics.pixar.com/library/ReflectanceModel/paper.pdf">A Reflectance Model for Computer Graphics</a><br>
[5] <a href="http://www.graphics.cornell.edu/~westin/pubs/TorranceSparrowJOSA1967.pdf">Theory for Off-Specular Reflection From Roughened Surfaces</a><br>
[6] <a href="http://cg.cs.tsinghua.edu.cn/people/~kun/asg/">Anisotropic Spherical Gaussians</a><br>
[7] <a href="https://github.com/wdas/brdf">BRDF Explorer</a><br>
[8] <a href="https://depot.floored.com/brdf_explorer">WebGL BRDF Explorer (defunct)</a><br></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://mynameismjp.wordpress.com/" title="mpettineo@gmail.com">MJP</a> - <time datetime="2016-10-17 08:29:19">Oct 1, 2016</time></p>
<p>Thank you Evgenii! I added the missing link. I&rsquo;m also glad that you like the graphs, it took a while to generate all of them. :)</p>
<hr />
####
[Evgenii Golubev]( "zalbard@gmail.com") - <time datetime="2016-10-17 04:01:34">Oct 1, 2016</time>
<p>The first paragraph is missing a link. :-) Great series! I especially like the included graphs.</p>
<hr />
####
[Devsh]( "devsh.graphicsprogramming@gmail.com") - <time datetime="2019-03-02 13:01:17">Mar 6, 2019</time>
<p>Hi MJP, I&rsquo;ve noticed that we could fit more gaussians to an arbitrary function. For example I&rsquo;ve managed to match the Beckmann distribution with 3 gaussians for the roughness value of 1 <a href="https://www.desmos.com/calculator/o1lces1o1l">https://www.desmos.com/calculator/o1lces1o1l</a> My aim is to fit between 2-5 gaussians to common BRDFs and sample from environment maps pre-convolved with SG, then that way I can get a much better approximation to Importance Sampled references than the current IBL methods.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>SG Series Part 3: Diffuse Lighting From an SG Light Source</title>
			<link>https://therealmjp.github.io/posts/sg-series-part-3-diffuse-lighting-from-an-sg-light-source/</link>
			<pubDate>Mon, 10 Oct 2016 07:08:51 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/sg-series-part-3-diffuse-lighting-from-an-sg-light-source/</guid>
			<description>This is part 3 of a series on Spherical Gaussians and their applications for pre-computed lighting. You can find the other articles here:
Part 1 - A Brief (and Incomplete) History of Baked Lighting Representations
Part 2 - Spherical Gaussians 101
Part 3 - Diffuse Lighting From an SG Light Source
Part 4 - Specular Lighting From an SG Light Source
Part 5 - Approximating Radiance and Irradiance With SG&amp;rsquo;s</description>
			<content:encoded><![CDATA[<p><em>This is part 3 of a series on Spherical Gaussians and their applications for pre-computed lighting. You can find the other articles here:</em></p>
<p>Part 1 - <a href="../sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/">A Brief (and Incomplete) History of Baked Lighting Representations</a><br>
Part 2 - <a href="../sg-series-part-2-spherical-gaussians-101/">Spherical Gaussians 101</a><br>
Part 3 - <a href="../sg-series-part-3-diffuse-lighting-from-an-sg-light-source/">Diffuse Lighting From an SG Light Source</a><br>
Part 4 - <a href="../sg-series-part-4-specular-lighting-from-an-sg-light-source/">Specular Lighting From an SG Light Source</a><br>
Part 5 - <a href="../sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/">Approximating Radiance and Irradiance With SG&rsquo;s</a><br>
Part 6 - <a href="../sg-series-part-6-step-into-the-baking-lab/">Step Into The Baking Lab</a><br></p>
<h2 id="a-big-gaussian-in-the-sky">A Big Gaussian In The Sky</h2>
<p>In the <a href="https://mynameismjp.wordpress.com/2016/10/09/sg-series-part-2-spherical-gaussians-101/">previous post</a> we covered a few of the universal properties of SG&rsquo;s. Now that we have a few tools on our utility belt, let&rsquo;s discuss an example of how we can actually use those properties to our advantage in a rendering scenario. Let&rsquo;s say we have a surface point <strong>x</strong> being lit by a light source <strong>L</strong>, with the light source being represented by an SG named <strong>GL</strong>. Recall from the previous article that the equation for computing the outgoing radiance towards the eye for a surface with a Lambertian diffuse BRDF looks like the following:</p>
<p>$$ L_{o}(\mathbf{o}, \mathbf{x}) = \frac{C_{diffuse}}{\pi} \int_{\Omega} L_{i}(\mathbf{i}, \mathbf{x})cos(\theta_{i})d\Omega $$</p>
<p>For punctual light sources that are essentially a scaled delta function, computing this is as easy as N dot L. But we&rsquo;re in trouble if we have an area light source, since we typically don&rsquo;t have a closed form solution to the integral. But let&rsquo;s suppose that we have some strange Gaussian light source, whose angular falloff can be exactly represented by an SG (normally area light sources are considered  to have uniform emission over their surface, but let&rsquo;s imagine we have case where the emission is non-uniform). If we can treat the light as an SG, then we can start to consider some of the handy Gaussian tools that we laid out earlier. In particular the inner product starts to seem really useful: it gives us the result of integrating the product of two SG&rsquo;s, which is basically what we&rsquo;re trying to accomplish in our diffuse lighting equation. The big catch is that we&rsquo;re not integrating the product of two SG&rsquo;s, we&rsquo;re instead integrating the product of an SG with a clamped cosine lobe. Obviously a Gaussian lobe has a different shape compared to a clamped cosine lobe, but perhaps if we squint our eyes from a distance you could substitute one for another. This approach was taken by <a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2009/12/sg.pdf">Wang et al.</a>[1], who suggested fitting a cosine lobe to a single SG with <strong>λ</strong>=2.133 and <strong>a</strong>=1.17. If we follow in their footsteps, the diffuse calculation is straightforward:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">SG</span> <span class="nf">CosineLobeSG</span><span class="p">(</span><span class="n">in</span> <span class="n">float3</span> <span class="n">direction</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">SG</span> <span class="n">cosineLobe</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">cosineLobe</span><span class="p">.</span><span class="n">Axis</span> <span class="o">=</span> <span class="n">direction</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">cosineLobe</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">=</span> <span class="mf">2.133f</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">cosineLobe</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">=</span> <span class="mf">1.17f</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">cosineLobe</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">SGIrradianceInnerProduct</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">lightingLobe</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">normal</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">SG</span> <span class="n">cosineLobe</span> <span class="o">=</span> <span class="n">CosineLobeSG</span><span class="p">(</span><span class="n">normal</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">max</span><span class="p">(</span><span class="n">SGInnerProduct</span><span class="p">(</span><span class="n">lightingLobe</span><span class="p">,</span> <span class="n">cosineLobe</span><span class="p">),</span> <span class="mf">0.0f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">SGDiffuseInnerProduct</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">lightingLobe</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">normal</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">albedo</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">brdf</span> <span class="o">=</span> <span class="n">albedo</span> <span class="o">/</span> <span class="n">Pi</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">SGIrradianceInnerProduct</span><span class="p">(</span><span class="n">lightingLobe</span><span class="p">,</span> <span class="n">normal</span><span class="p">)</span> <span class="o">*</span> <span class="n">brdf</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="error-analysis">Error Analysis</h2>
<p>Not too bad, eh? Of course it&rsquo;s worth taking a closer look at our cosine lobe approximation, since that&rsquo;s definitely going to introduce some error. Perhaps the best way to do this is to look at the graphs of a real cosine lobe and our SG approximation side-by-side:</p>
<p><a href="https://www.desmos.com/calculator/pecunlgzhe"><img src="/images/converted/sg-series-part-3-diffuse-lighting-from-an-sg-light-source/sg_cosine_approximation_alt.png" alt="sg_cosine_approximation_alt"></a></p>
<center><i>Comparison of a clamped cosine cosine lobe (red) with an SG approximation (blue)</i></center>
<p>Just from looking at the graph it&rsquo;s fairly obvious that an SG isn&rsquo;t necessarily a great fit for a cosine lobe. First of all, the amplitude actually goes above 1, which might seem a bit weird at first glance. However it&rsquo;s necessary to ensure that the area under the curve remains somewhat consistent with the cosine lobe, since there would otherwise be a loss of energy. The other weirdness stems from the fact that an SG never actually hits 0 anywhere on the sphere, hence the long &ldquo;tail&rdquo; on the graph of the SG. This essentially means that if the SG were integrated against a punctual light source, the lighting would &ldquo;wrap&rdquo; around the sphere past the point where N dot L is equal to 0. The situation actually isn&rsquo;t all that different from an SH representation of a cosine lobe, which also extends past π/2:</p>
<p><a href="https://www.desmos.com/calculator/dbxrgxb95k"><img src="/images/converted/sg-series-part-3-diffuse-lighting-from-an-sg-light-source/sh_sg_cosine_approximation.png" alt="sh_sg_cosine_approximation"></a></p>
<center><i>L1 (green) and L2 (purple) SH approximation of a clamped cosine lobe compared with an SG approximation (blue) and the actual clamped cosine (red).</i></center>
<p>In the SH case the approximation actually goes negative, which is arguably worse than the long tail of the SG approximation. The L1 approximation is particularly bad in this regard. If at this point you&rsquo;re trying to imagine what these approximations look like on a sphere, let me save you the trouble by providing an image:</p>
<p><a href="/images/converted/sg-series-part-3-diffuse-lighting-from-an-sg-light-source/sg_sh_cosine_sphere.png"><img src="/images/converted/sg-series-part-3-diffuse-lighting-from-an-sg-light-source/sg_sh_cosine_sphere.png" alt="SG_SH_Cosine_Sphere"></a></p>
<center><i>From left to right: actual clamped cosine lobe, SG cosine approximation, L2 SH cosine approximation</i></center>
<p>Now that we&rsquo;ve finished analyzing the approximation of a cosine lobe, we need to take a look at the actual results of computing diffuse lighting from an SG light source. Let&rsquo;s start off by graphing the results of computing irradiance using an SG inner product, and compare it against what we get by using brute-force numerical integration to compute the result of multiplying the SG with an actual clamped cosine (<em>not</em> the approximate SG cosine lobe that we use for the inner product):</p>
<p><a href="https://www.desmos.com/calculator/mxpq29ybam"><img src="/images/converted/sg-series-part-3-diffuse-lighting-from-an-sg-light-source/sglight_irradiance_graph.png" alt="sglight_irradiance_graph"></a></p>
<center><i>The resulting irradiance from an SG light source (with sharpness of 4.0) as a function of the angle between the light source and the surface normal. The red graph is the result of using numerical integration to compute the integral of the SG light source multiplied with a clamped cosine, while the blue graph was computed using an SG inner product of the light source with a cosine lobe approximated as an SG.</i></center>
<p>As you might expect, the inner product approximation has some error when compared with the &ldquo;ground truth&rdquo; provided by numerical integration. It&rsquo;s worth pointing out that this error is purely a consequence of approximating the clamped cosine lobe as an SG: the inner product provides the exact result of the integral, and thus shouldn&rsquo;t introduce any error on its own. Despite this error, the resulting irradiance isn&rsquo;t hugely far off from our ground truth. The biggest difference is for the angles facing away from the light, where the SG inner product version has a stronger tail. Visualizing the resulting diffuse on a sphere gives us the following:</p>
<p><a href="/images/converted/sg-series-part-3-diffuse-lighting-from-an-sg-light-source/sglight_diffuse.png"><img src="/images/converted/sg-series-part-3-diffuse-lighting-from-an-sg-light-source/sglight_diffuse.png" alt="SGLight_Diffuse"></a></p>
<center><i>The left sphere shows the resulting diffuse lighting from an SG light source with a sharpness of 4.0, where the irradiance was computed using monte carlo importance sampling. The right sphere shows the resulting diffuse lighting from computing irradiance using an SG inner product with an approximation of a cosine lobe.</i></center>
<h2 id="acheaperapproximation">A Cheaper Approximation</h2>
<p>As an alternative to representing the cosine lobe with an SG and computing the inner product, we can consider a cheaper approximation. One advantage of working with SG&rsquo;s is that each lobe is always symmetrical about its axis, which is also where its value is the highest. We also discussed earlier how we can compute the integral of an SG over the sphere, which gives us its total energy. This suggests that if we want to be frugal with our shader cycles, we can pull terms out of the integral over the sphere/hemisphere and only evaluate them for the SG axis direction. This obviously introduces error, but that error may be acceptable if the term we pull out is relatively &ldquo;smooth&rdquo;. If we apply this approximation to computing irradiance and diffuse lighting, we get this:</p>
<p>$$ L_{o}(\mathbf{o}, \mathbf{x}) = \frac{C_{diffuse}}{\pi} \int_{\Omega} G_{L}(\mathbf{i};\mathbf{\mu},\lambda,a)cos(\theta_{i})d\Omega $$</p>
<p>$$ L_{o}(\mathbf{o}, \mathbf{x}) \approx cos(\theta_{\mu}) \frac{C_{diffuse}}{\pi} \int_{\Omega} G_{L}(\mathbf{i};\mathbf{\mu},\lambda,a)d\Omega $$</p>
<p>Translating to HLSL, we get the following functions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">SGIrradiancePunctual</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">lightingLobe</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">normal</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">cosineTerm</span> <span class="o">=</span> <span class="n">saturate</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">lightingLobe</span><span class="p">.</span><span class="n">Axis</span><span class="p">,</span> <span class="n">normal</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">cosineTerm</span> <span class="o">*</span> <span class="mf">2.0f</span> <span class="o">*</span> <span class="n">Pi</span> <span class="o">*</span> <span class="p">(</span><span class="n">lightingLobe</span><span class="p">.</span><span class="n">Amplitude</span><span class="p">)</span> <span class="o">/</span>
</span></span><span class="line"><span class="cl">    <span class="n">lightingLobe</span><span class="p">.</span><span class="n">Sharpness</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">SGDiffusePunctual</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">lightingLobe</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">normal</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">albedo</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">brdf</span> <span class="o">=</span> <span class="n">albedo</span> <span class="o">/</span> <span class="n">Pi</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">SGIrradiancePunctual</span><span class="p">(</span><span class="n">lightingLobe</span><span class="p">,</span> <span class="n">normal</span><span class="p">)</span> <span class="o">*</span> <span class="n">brdf</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>If we overlay the graph of our super-cheap irradiance approximation on the graph we were looking at earlier, we get this:</p>
<p><a href="https://www.desmos.com/calculator/mfsufusbqk"><img src="/images/converted/sg-series-part-3-diffuse-lighting-from-an-sg-light-source/sglight_irradiance_approximation2.png" alt="sglight_irradiance_approximation"></a></p>
<center><i>The resulting irradiance from an SG light source (with sharpness of 4.0) as function of the angle between the light source and the surface normal. The red graph was computed using numerical integration, while the blue graph was computed using an SG inner product of the light source with a cosine lobe approximated as an SG. The green graph was computed by pulling the cosine term out of the integral, and multiplying it with the result of integrating the SG light about the sphere.</i></center>
<p>The result shouldn&rsquo;t be a surprise: it&rsquo;s just a scaled version of the standard clamped cosine.It&rsquo;s pretty obvious just by looking that this particular optimization will introduce quite a bit of error, particularly where theta is greater than π/2. But it is cheap, since we&rsquo;ve effectively turned an SG into a point light. This is makes it a useful tool for cases where we may want to approximate the convolution of an SG light source with a BRDF or some other function that isn&rsquo;t easily represented as an SG.</p>
<h2 id="a-more-accurate-approximation">A More Accurate Approximation</h2>
<p>So it&rsquo;s nice to have a cheap option, but what if we want more accuracy than our inner product approximation? Fortunately for us, <a href="http://www.selfshadow.com/">Stephen Hill</a> was able to formulate another alternative approximation that directly fits a curve to the integral of a cosine lobe with an SG. His implementation is actually formulated for a normalized SG (where the integral about the sphere is equal to 1.0), but we can easily account for this by computing the integral and scaling the result by that value:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">SGIrradianceFitted</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">lightingLobe</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">normal</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">float</span> <span class="n">muDotN</span> <span class="o">=</span> <span class="n">dot</span><span class="p">(</span><span class="n">lightingLobe</span><span class="p">.</span><span class="n">Axis</span><span class="p">,</span> <span class="n">normal</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">float</span> <span class="n">lambda</span> <span class="o">=</span> <span class="n">lightingLobe</span><span class="p">.</span><span class="n">Sharpness</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">float</span> <span class="n">c0</span> <span class="o">=</span> <span class="mf">0.36f</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">float</span> <span class="n">c1</span> <span class="o">=</span> <span class="mf">1.0f</span> <span class="o">/</span> <span class="p">(</span><span class="mf">4.0f</span> <span class="o">*</span> <span class="n">c0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">eml</span> <span class="o">=</span> <span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">lambda</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">em2l</span> <span class="o">=</span> <span class="n">eml</span> <span class="o">*</span> <span class="n">eml</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">rl</span> <span class="o">=</span> <span class="n">rcp</span><span class="p">(</span><span class="n">lambda</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">scale</span> <span class="o">=</span> <span class="mf">1.0f</span> <span class="o">+</span> <span class="mf">2.0f</span> <span class="o">*</span> <span class="n">em2l</span> <span class="o">-</span> <span class="n">rl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">bias</span> <span class="o">=</span> <span class="p">(</span><span class="n">eml</span> <span class="o">-</span> <span class="n">em2l</span><span class="p">)</span> <span class="o">*</span> <span class="n">rl</span> <span class="o">-</span> <span class="n">em2l</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">x</span> <span class="o">=</span> <span class="n">sqrt</span><span class="p">(</span><span class="mf">1.0f</span> <span class="o">-</span> <span class="n">scale</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">x0</span> <span class="o">=</span> <span class="n">c0</span> <span class="o">*</span> <span class="n">muDotN</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">x1</span> <span class="o">=</span> <span class="n">c1</span> <span class="o">*</span> <span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">n</span> <span class="o">=</span> <span class="n">x0</span> <span class="o">+</span> <span class="n">x1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">y</span> <span class="o">=</span> <span class="n">saturate</span><span class="p">(</span><span class="n">muDotN</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="n">abs</span><span class="p">(</span><span class="n">x0</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">x1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">y</span> <span class="o">=</span> <span class="n">n</span> <span class="o">*</span> <span class="n">n</span> <span class="o">/</span> <span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">result</span> <span class="o">=</span> <span class="n">scale</span> <span class="o">*</span> <span class="n">y</span> <span class="o">+</span> <span class="n">bias</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">result</span> <span class="o">*</span> <span class="n">ApproximateSGIntegral</span><span class="p">(</span><span class="n">lightingLobe</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The result is very close to the ground truth, which is very cool considering that it might actually be cheaper than our inner product approximation!</p>
<p><a href="https://www.desmos.com/calculator/ke219swxv3"><img src="/images/converted/sg-series-part-3-diffuse-lighting-from-an-sg-light-source/sglight_irradiance_fitted.png" alt="SGLight_Irradiance_Fitted"></a></p>
<center><i>The resulting irradiance from an SG light source (with sharpness of 4.0) as function of the angle between the light source and the surface normal. The red graph was computed using numerical integration, while the blue graph was computed using an SG inner product of the light source with a cosine lobe approximated as an SG. The orange graph was computed using Stephen Hill's fitted curve approximation.</i></center>
<p>If we once again visualize the result on the sphere and compare with our previous results, we get the following:</p>
<p><img src="/images/converted/sg-series-part-3-diffuse-lighting-from-an-sg-light-source/sglight_diffuse_fitted.png" alt="SGLight_Diffuse_Fitted"></p>
<center><i>The left sphere shows the resulting diffuse lighting from an SG light source with a sharpness of 4.0, where the irradiance was computed using an SG inner product with an approximation of a cosine lobe. The middle sphere shows the resulting diffuse lighting from computing irradiance using monte carlo importance sampling. The right sphere shows the resulting diffuse lighting from Stephen Hill's fitted approximation.</i></center>
<h2 id="references">References</h2>
<p>[1] <a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2009/12/sg.pdf">All-Frequency Rendering of Dynamic, Spatially-Varying Reflectance</a></p>
<hr>
<h2 id="comments">Comments:</h2>
<h3 id="heading"></h3>
<p><a href="%22stefan.dyulgerov@gmail.com%22">Stefan Dyulgerov</a> - <time datetime="2017-10-09 07:08:04">Oct 1, 2017</time></p>
<p>Hey, great article and nicely explained. 1. Can you update the links to the wang paper, since john snyders web site is reworked 2. Can you point a link to the SGIrradianceFitted, since the selfshadow is also updated.</p>
<hr />
###
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2017-10-09 14:38:29">Oct 1, 2017</time>
<p>Hi Stefan, I updated the link to Wang et al.&rsquo;s paper. Thank you for pointing that out! As for Stephen Hill&rsquo;s fitted SG irradiance approximation, the link that I currently have in there just points to his home page, which is still the same. He hasn&rsquo;t formally published his approximation anywhere, so I don&rsquo;t have anything else to link to at the moment.</p>
<hr />
###
[Wumpf](http://wumpfblog.wordpress.com "r_andreas2@web.de") - <time datetime="2016-10-13 00:24:24">Oct 4, 2016</time>
<p>In the second code listening &ldquo;SGDiffusePunctual&rdquo; should call &ldquo;SGIrradiancePunctual&rdquo; not &ldquo;ApproximateSGIrradiance&rdquo;, right? :) Great article series! :)</p>
<hr />
###
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-10-13 09:46:50">Oct 4, 2016</time>
<p>Yes, that&rsquo;s right. I had renamed that function in the original source code, and neglected to properly update the code embedded in the article. Thank you for pointing that out, and also for the kind words. :)</p>
<hr />
###
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-10-10 23:00:15">Oct 1, 2016</time>
<p>Hi Matt, I definitely see how that could be confusing, now that you&rsquo;ve pointed it out. I changed the text a bit to explicitly state that the graph represents the numerical integration of the SG light multiplied with the clamped cosine, so hopefully it will be a bit more clear for other readers. Either way I really appreciate the feedback! I should also thank you for writing such an amazing book, and making the complete source code available for reference! Without it I&rsquo;m not sure if I would have gotten my own little path tracer working in the demo. :)</p>
<hr />
###
[Matt Pharr](http://pharr.org/matt "matt.pharr@gmail.com") - <time datetime="2016-10-10 14:39:11">Oct 1, 2016</time>
<p>(Great series!) I was initially confused by your graph comparing numerical integration with the SG inner product&ndash;&ldquo;shouldn&rsquo;t numerical integration give the same result, since the inner product is exact and in closed form?&rdquo; I asked myself; it wasn&rsquo;t clear that you were comparing the integration of two different functions. I eventually figured out that you were numerically integrating the SG light model with the actual clamped cosine and comparing that to the inner product of the SG light and the SG clamped cosine (I think!); it might be nice to clarify the text/caption about that part of it. (On to part 4!)</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>SG Series Part 2: Spherical Gaussians 101</title>
			<link>https://therealmjp.github.io/posts/sg-series-part-2-spherical-gaussians-101/</link>
			<pubDate>Mon, 10 Oct 2016 07:08:02 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/sg-series-part-2-spherical-gaussians-101/</guid>
			<description>This is part 2 of a series on Spherical Gaussians and their applications for pre-computed lighting. You can find the other articles here:
Part 1 - A Brief (and Incomplete) History of Baked Lighting Representations
Part 2 - Spherical Gaussians 101
Part 3 - Diffuse Lighting From an SG Light Source
Part 4 - Specular Lighting From an SG Light Source
Part 5 - Approximating Radiance and Irradiance With SG&amp;rsquo;s</description>
			<content:encoded><![CDATA[<p><em>This is part 2 of a series on Spherical Gaussians and their applications for pre-computed lighting. You can find the other articles here:</em></p>
<p>Part 1 - <a href="../sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/">A Brief (and Incomplete) History of Baked Lighting Representations</a><br>
Part 2 - <a href="../sg-series-part-2-spherical-gaussians-101/">Spherical Gaussians 101</a><br>
Part 3 - <a href="../sg-series-part-3-diffuse-lighting-from-an-sg-light-source/">Diffuse Lighting From an SG Light Source</a><br>
Part 4 - <a href="../sg-series-part-4-specular-lighting-from-an-sg-light-source/">Specular Lighting From an SG Light Source</a><br>
Part 5 - <a href="../sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/">Approximating Radiance and Irradiance With SG&rsquo;s</a><br>
Part 6 - <a href="../sg-series-part-6-step-into-the-baking-lab/">Step Into The Baking Lab</a><br></p>
<p>In the <a href="../sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/">previous article</a>, I gave a quick rundown of some of the available techniques for representing a pre-computed distribution of radiance or irradiance for each lightmap texel or probe location. In this article, I&rsquo;m going cover the basics of Spherical Gaussians, which are a type of spherical radial basis function (SRBF for short). The concepts introduced here will serve as the core set of tools for working with Spherical Gaussians, and in later articles I&rsquo;ll demonstrate how you can use those tools to form an alternative for approximating incoming radiance in pre-computed lightmaps or probes.</p>
<p>I should point out that this article is still going to be somewhat high-level, in that it won&rsquo;t provide full derivations and background details for all formulas and operations. However it is my hope that the material here will be sufficient to gain a basic understanding of SG&rsquo;s, and also use them in practical scenarios.</p>
<h2 id="whats-a-spherical-gaussian">What&rsquo;s a Spherical Gaussian?</h2>
<p>A Spherical Gaussian, or &ldquo;SG&rdquo; for short, is essentially a <a href="https://en.wikipedia.org/wiki/Gaussian_function">Gaussian function</a>[1] that&rsquo;s defined on the surface of a sphere. If you&rsquo;re reading this, then you&rsquo;re probably already familar with how a Gaussian function works in 1D: you compute the distance from the center of the Gaussian, and use this distance as part of a base-e exponential. This produces the characteristic &ldquo;hump&rdquo; that you see when you graph it:</p>
<p><a href="/images/converted/sg-series-part-2-spherical-gaussians-101/gaussian_1d.png"><img src="/images/converted/sg-series-part-2-spherical-gaussians-101/gaussian_1d_resized_300.png" alt="Gaussian_1D"></a></p>
<center><i>A Gaussian in 1D centered at x=0, with a height of 3</i></center>
<p>You&rsquo;re probably also familiar with how it looks in 2D, since it&rsquo;s very commonly used in image processing as a filter kernel. It ends up looking like what you would get if you took the above graph and revolved it around its axis</p>
<p><a href="/images/converted/sg-series-part-2-spherical-gaussians-101/gaussian_2d.png"><img src="/images/converted/sg-series-part-2-spherical-gaussians-101/gaussian_2d.png" alt="Gaussian_2D"></a></p>
<center><i>A Gaussian filter applied to a 2D image of a white dot, showing that the impulse response is effectively a Gaussian function in 2D</i></center>
<p>A Spherical Gaussian still works the same way, except that it now lives on the surface of a sphere instead of on a line or a flat plane. If you&rsquo;re having trouble visualizing that, imagine if you took the above image and wrapped it around a sphere like wrapping paper. It ends up looking like this:</p>
<p><a href="/images/converted/sg-series-part-2-spherical-gaussians-101/sg_sphere.png"><img src="/images/converted/sg-series-part-2-spherical-gaussians-101/sg_sphere_resized_300.png" alt="SG_Sphere"></a></p>
<center><i>A Spherical Gaussian visualized on the surface of a sphere</i></center>
<p>Since an SG is defined on a sphere rather than a line or plane, it&rsquo;s parameterized differently than a normal Gaussian. A 1D Gaussian function always has the following form:</p>
<p>$$ ae^{\frac{-(x - b)^{2}}{2c^{2}}} $$</p>
<p>The part that we need to change in order to define the function on a sphere is the &ldquo;(x - b)&rdquo; term. This part of the function essentially makes the Gaussian a function of the cartesian distance between a given point and the center of the Gaussian, which can be trivially extended into 2D using the standard distance formula. To make this work on a sphere, we must instead make our Gaussian a function of the angle between two unit direction vectors. In practice we do this by making an SG a function of the <em>cosine</em> of the angle between two vectors, which can be efficiently computed using a dot product like so:</p>
<p>$$ G(\mathbf{v};\mathbf{\mu},\lambda,a) = ae^{\lambda(\mathbf{\mu} \cdot \mathbf{v} - 1)} $$</p>
<p>Just like a normal Gaussian, we have a few parameters that control the shape and location of the resulting lobe. First we have μ, which is the <em>axis</em>, or <em>direction</em> of the lobe. It effectively controls where the lobe is located on the sphere, and always points towards the exact center of the lobe. Next we have λ, which is the <em>sharpness</em> of the lobe. As this value increases, the lobe will get &ldquo;skinnier&rdquo;, meaning that the result will fall off more quickly as you get further from the lobe axis. Finally we have <em>a</em>, which is the <em>amplitude</em> or <em>intensity</em> of the lobe. If you were to look at a <a href="https://www.desmos.com/calculator/rvtqpze0g7">polar graph of an SG</a>, it would correspond to the height of the lobe at its peak. The amplitude can be a scalar value, or for graphics applications we may choose to make it an RGB triplet in order to support varying intensities for different color channels. This all lends itself to a simple HLSL code definition:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">SG</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">Amplitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">Axis</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">Sharpness</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>Evaluating an SG is also easily expressible in HLSL. All we need is a normalized direction vector representing the point on the sphere where we&rsquo;d like to compute the value of the SG:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">EvaluateSG</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">sg</span><span class="p">,</span> <span class="n">in</span> <span class="n">float3</span> <span class="n">dir</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">cosAngle</span> <span class="o">=</span> <span class="n">dot</span><span class="p">(</span><span class="n">dir</span><span class="p">,</span> <span class="n">sg</span><span class="p">.</span><span class="n">Axis</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">sg</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">*</span> <span class="n">exp</span><span class="p">(</span><span class="n">sg</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">*</span> <span class="p">(</span><span class="n">cosAngle</span> <span class="o">-</span> <span class="mf">1.0f</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="why-spherical-gaussians">Why Spherical Gaussians?</h2>
<p>Now that we know what a Spherical Gaussian is, what&rsquo;s so useful about them anyway? One pontential benefit is that they&rsquo;re fairly intuitive: it&rsquo;s not terribly hard to understand how the 3 parameters work, and how each parameter affects the resulting lobe. The other main draw is that they inherit a lot of useful properties of &ldquo;regular&rdquo; Gaussians, which makes them useful for graphics and other related applications. These properties have been explored and utilized in several research papers that were primarily aimed at achieving pre-computed radiance transfer (PRT) with both diffuse and specular material response. In particular,  the paper entitled &ldquo;<a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2009/12/sg.pdf">All-Frequency Rendering of Dynamic, Spatially-Varying Reflectance</a>[2]&rdquo; by Wang et al. was our main inspiration for pursuing SG&rsquo;s at RAD.</p>
<h2 id="products">Products</h2>
<p>So what are these useful Gaussian properties that we can exploit? For starters, taking the product of 2 Gaussians functions produces another Gaussian. For an SG, this is equivalent to visiting every point on the sphere, evaluating 2 different SG&rsquo;s, and multiplying the two results. Since it&rsquo;s an operation that takes 2 SG&rsquo;s and produces another SG, it is sometimes referred to as a &ldquo;vector&rdquo; product. It&rsquo;s defined as the following:</p>
<p>$$ G_{1}(\mathbf{v})G_{2}(\mathbf{v}) = G(\mathbf{v};\frac{\mu_{m}}{||\mu_{m}||},a_{1}a_{2}e^{\lambda_{m}(||\mu_{m}|| - 1)}) $$</p>
<p>$$ \lambda_{m} = \lambda_{1} + \lambda_{2} $$</p>
<p>$$ \mu_{m} = \frac{\lambda_{1}\mu_{1} + \lambda_{2}\mu_{2}}{\lambda_{1} + \lambda_{2}} $$</p>
<p>In HLSL code, it looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">SG</span> <span class="nf">SGProduct</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">x</span><span class="p">,</span> <span class="n">in</span> <span class="n">SG</span> <span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">um</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">*</span> <span class="n">x</span><span class="p">.</span><span class="n">Axis</span> <span class="o">+</span> <span class="n">y</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">*</span> <span class="n">y</span><span class="p">.</span><span class="n">Axis</span><span class="p">)</span> <span class="o">/</span>
</span></span><span class="line"><span class="cl">                <span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">+</span> <span class="n">y</span><span class="p">.</span><span class="n">Sharpness</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">umLength</span> <span class="o">=</span> <span class="n">length</span><span class="p">(</span><span class="n">um</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">lm</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">+</span> <span class="n">y</span><span class="p">.</span><span class="n">Sharpness</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">SG</span> <span class="n">res</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">res</span><span class="p">.</span><span class="n">Axis</span> <span class="o">=</span> <span class="n">um</span> <span class="o">*</span> <span class="p">(</span><span class="mf">1.0f</span> <span class="o">/</span> <span class="n">umLength</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">res</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">=</span> <span class="n">lm</span> <span class="o">*</span> <span class="n">umLength</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">res</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">*</span> <span class="n">y</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl">    <span class="n">exp</span><span class="p">(</span><span class="n">lm</span> <span class="o">*</span> <span class="p">(</span><span class="n">umLength</span> <span class="o">-</span> <span class="mf">1.0f</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">res</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="integrals">Integrals</h2>
<p>Gaussians have another really nice property in that their integrals have a closed-form solution, which is known as the <a href="https://en.wikipedia.org/wiki/Error_function">error function</a>[3]. The property also extends to SG&rsquo;s, where we can compute the integral of an SG over the entire sphere:</p>
<p>$$ \int_{\Omega} G(\mathbf{v})d\mathbf{v} = 2\pi\frac{a}{\lambda}(1 - e^{-2\lambda}) $$</p>
<p>Computing an integral will essentially tell us the total &ldquo;energy&rdquo; of an SG, which can be useful for lighting calculations. It can also be useful for <em>normalizing</em> an SG, which produces an SG that integrates to 1. Such a normalized SG is suitable for representing a probability distribution, such as an NDF. In fact, a normalized SG is actually equivalent to a <a href="https://en.wikipedia.org/wiki/Von_Mises%E2%80%93Fisher_distribution">von Mises-Fisher distribution</a>[4] in 3D!</p>
<p>An SG integral is actually very cheap to compute&hellip;or at least it would be if we removed the exponential term. It turns out that the \( (1 - e^{-2\lambda}) \) term actually approaches 1 very quickly as the SG&rsquo;s sharpness increases, which means we can potentially drop it with little error as long as we know that the sharpness is high enough. Here&rsquo;s what a graph of \( (1 - e^{-2\lambda}) \) looks like for increasing sharpness:</p>
<p><a href="https://www.desmos.com/calculator/qqfeojmknn"><img src="/images/converted/sg-series-part-2-spherical-gaussians-101/sg_integral_expterm_resized_300.png" alt="SG_Integral_ExpTerm"></a></p>
<center><i>A graph of the exponential term in computing the integral of an SG over a sphere, which approaches 1 as the sharpness increases. The X-axis is sharpness, and the Y-axis is the value of \\( (1 - e^{-2\lambda}) \\).</i></center>
<p>This all lends itself naturally to HLSL implementations for accurate and approximate versions of an SG integral:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">SGIntegral</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">sg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">expTerm</span> <span class="o">=</span> <span class="mf">1.0f</span> <span class="o">-</span> <span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="mf">2.0f</span> <span class="o">*</span> <span class="n">sg</span><span class="p">.</span><span class="n">Sharpness</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">Pi</span> <span class="o">*</span> <span class="p">(</span><span class="n">sg</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">/</span> <span class="n">sg</span><span class="p">.</span><span class="n">Sharpness</span><span class="p">)</span> <span class="o">*</span> <span class="n">expTerm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">ApproximateSGIntegral</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">sg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">Pi</span> <span class="o">*</span> <span class="p">(</span><span class="n">sg</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">/</span> <span class="n">sg</span><span class="p">.</span><span class="n">Sharpness</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="inner-product">Inner Product</h2>
<p>If we were to use our SG integral formula to compute the integral of the product of two SG&rsquo;s, we can compute what&rsquo;s known as the <em>inner product</em>, or <em>dot product</em> of those SG&rsquo;s. The operation is usually defined like this:</p>
<p>$$ \int_{\Omega} G_{1}(\mathbf{v}) G_{2}(\mathbf{v}) d\mathbf{v} = \frac{4 \pi a_{0} a_{1}}{e^{\lambda_{m}}} \frac{sinh(d_{m})}{d_{m}} $$</p>
<p>$$ d_{m} = || \lambda_{1}\mu_{1} + \lambda_{2}\mu_{2} || $$</p>
<p>However we can avoid numerical precision issues by using an alternate arrangement:</p>
<p>$$ \int_{\Omega} G_{1}(\mathbf{v}) G_{2}(\mathbf{v}) d\mathbf{v} = 2 \pi a_{0} a_{1}\frac{e^{d_{m} - \lambda_{m}} - e^{-d_{m} - \lambda_{m}}}{d_{m}} $$</p>
<p>&hellip;which looks like this in HLSL:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">float3</span> <span class="nf">SGInnerProduct</span><span class="p">(</span><span class="n">in</span> <span class="n">SG</span> <span class="n">x</span><span class="p">,</span> <span class="n">in</span> <span class="n">SG</span> <span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">dm</span> <span class="o">=</span> <span class="n">length</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">*</span> <span class="n">x</span><span class="p">.</span><span class="n">Axis</span> <span class="o">+</span> <span class="n">y</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">*</span> <span class="n">y</span><span class="p">.</span><span class="n">Axis</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">float3</span> <span class="n">expo</span> <span class="o">=</span> <span class="n">exp</span><span class="p">(</span><span class="n">dm</span> <span class="o">-</span> <span class="n">x</span><span class="p">.</span><span class="n">Sharpness</span> <span class="o">-</span> <span class="n">y</span><span class="p">.</span><span class="n">Sharpness</span><span class="p">)</span> <span class="o">*</span> <span class="n">x</span><span class="p">.</span><span class="n">Amplitude</span> <span class="o">*</span> <span class="n">y</span><span class="p">.</span><span class="n">Amplitude</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">other</span> <span class="o">=</span> <span class="mf">1.0f</span> <span class="o">-</span> <span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="mf">2.0f</span> <span class="o">*</span> <span class="n">dm</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="mf">2.0f</span> <span class="o">*</span> <span class="n">Pi</span> <span class="o">*</span> <span class="n">expo</span> <span class="o">*</span> <span class="n">other</span><span class="p">)</span> <span class="o">/</span> <span class="n">dm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="treshold">Treshold</h2>
<p>SG&rsquo;s have what&rsquo;s known as &ldquo;compact-ε&rdquo; support, which means that it&rsquo;s possible to determine an angle θ such that all points within θ radians of the SG&rsquo;s axis will have a value greater than ε. This property is potentially more useful if we flip it around so that we calculate a sharpness λ that results in a given θ for a particular value of ε:</p>
<p>$$ ae^{\lambda(cos\theta - 1)} = \epsilon $$
$$ \lambda = \frac{ln(\epsilon) - ln(a)}{cos\theta - 1} $$</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">float</span> <span class="nf">SGSharpnessFromThreshold</span><span class="p">(</span><span class="n">in</span> <span class="kt">float</span> <span class="n">amplitude</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                               <span class="n">in</span> <span class="kt">float</span> <span class="n">epsilon</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                               <span class="n">in</span> <span class="kt">float</span> <span class="n">cosTheta</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">log</span><span class="p">(</span><span class="n">epsilon</span><span class="p">)</span> <span class="o">-</span> <span class="n">log</span><span class="p">(</span><span class="n">amplitude</span><span class="p">))</span> <span class="o">/</span> <span class="p">(</span><span class="n">cosTheta</span> <span class="o">-</span> <span class="mf">1.0f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="rotation">Rotation</h2>
<p>One last operation I&rsquo;ll discuss is rotation. Rotating an SG is trivial: all you need to do is apply your rotation transform to the SG&rsquo;s axis vector and you have a rotated SG! You can apply the transform using a matrix, a quaternion, or any other means you might have for rotating a vector. This is a welcome change from SH, which requires a very complex transform once you go above L1.</p>
<p>[1] <a href="https://en.wikipedia.org/wiki/Gaussian_function">Gaussian Function</a><br>
[2] <a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2009/12/sg.pdf">All-Frequency Rendering of Dynamic, Spatially-Varying Reflectance</a><br>
[3] <a href="https://en.wikipedia.org/wiki/Error_function">Error Function</a><br>
[4] <a href="https://en.wikipedia.org/wiki/Von_Mises%E2%80%93Fisher_distribution">von-Mises Fisher Distribtion</a><br></p>
<hr>
<h2 id="comments">Comments:</h2>
<h3 id="heading"></h3>
<p><a href="http://gravatar.com/mriegger" title="mikeriegger@gmail.com">Mike R.</a> - <time datetime="2017-11-13 23:30:48">Nov 1, 2017</time></p>
<p>Just wanted say thanks for the awesome explanations, you make everything so clear and easy to understand.</p>
<hr />
###
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-10-12 21:55:16">Oct 3, 2016</time>
<p>Yes indeed, that was an error on my part. Thank you for pointing that out!</p>
<hr />
###
[ChenA]( "chena.invalid@gmail.com") - <time datetime="2016-10-11 08:04:42">Oct 2, 2016</time>
<p>\( G_{1}(\mathbf{v})G_{2}(\mathbf{v}) = G(\mathbf{v};\frac{\mu_{m}}{||\mu_{m}||},a_{1}a_{2}e^{\lambda_{m}(||\mu_{m}|| - 1)}) \) is it lost the sharpness λm∥pm∥? and <code>SG SGProduct(in SG x, in SG y) { float3 um = (x.Sharpness * x.Axis + y.Sharpness + y.Axis) / (x.Sharpness + y.Sharpness);</code> is this should be: <code>float3 um = (x.Sharpness * x.Axis + y.Sharpness * y.Axis) / (x.Sharpness + y.Sharpness);</code></p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>SG Series Part 1: A Brief (and Incomplete) History of Baked Lighting Representations</title>
			<link>https://therealmjp.github.io/posts/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/</link>
			<pubDate>Mon, 10 Oct 2016 07:05:49 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/</guid>
			<description>This is part 1 of a series on Spherical Gaussians and their applications for pre-computed lighting. You can find the other articles here:
Part 1 - A Brief (and Incomplete) History of Baked Lighting Representations
Part 2 - Spherical Gaussians 101
Part 3 - Diffuse Lighting From an SG Light Source
Part 4 - Specular Lighting From an SG Light Source
Part 5 - Approximating Radiance and Irradiance With SG&amp;rsquo;s</description>
			<content:encoded><![CDATA[<p><em>This is part 1 of a series on Spherical Gaussians and their applications for pre-computed lighting. You can find the other articles here:</em></p>
<p>Part 1 - <a href="../sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/">A Brief (and Incomplete) History of Baked Lighting Representations</a><br>
Part 2 - <a href="../sg-series-part-2-spherical-gaussians-101/">Spherical Gaussians 101</a><br>
Part 3 - <a href="../sg-series-part-3-diffuse-lighting-from-an-sg-light-source/">Diffuse Lighting From an SG Light Source</a><br>
Part 4 - <a href="../sg-series-part-4-specular-lighting-from-an-sg-light-source/">Specular Lighting From an SG Light Source</a><br>
Part 5 - <a href="../sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/">Approximating Radiance and Irradiance With SG&rsquo;s</a><br>
Part 6 - <a href="../sg-series-part-6-step-into-the-baking-lab/">Step Into The Baking Lab</a><br></p>
<p>For part 1 of this series, I&rsquo;m going to provide some background material for our research into Spherical Gaussians. The main purpose is cover some of the alternatives to the approach we used for The Order: 1886, and also to help you understand why we decided to persue Spherical Gaussians. The main empahasis is going to be on discussing what exactly we store in pre-baked lightmaps and probes, and how that data is used to compute diffuse or specular lighting. If you&rsquo;re already familiar with the concepts of pre-computing radiance or irradiance and approximating them using basis functions like the HL2 basis or Spherical Harmonics, then you will probably want to skip to the next article.</p>
<p>Before we get started, here&rsquo;s a quick glossary of the terms I use the formulas:</p>
<ul>
<li>\( L_{o} \) the outgoing radiance (lighting) towards the viewer</li>
<li>\( L_{i} \) the incoming radiance (lighting) hitting the surface</li>
<li>\( \mathbf{o} \) the direction pointing towards the viewer (often denoted as &ldquo;V&rdquo; in shader code dealing with lighting)</li>
<li>\( \mathbf{i} \) the direction pointing towards the incoming radiance hitting the surface (often denoted as &ldquo;L&rdquo; in shader code dealing with lighting)</li>
<li>\( \mathbf{n} \) the direction of the surface normal</li>
<li>\( \mathbf{x} \) the 3D location of the surface point</li>
<li>\( \int_{\Omega} \) integral about the hemisphere</li>
<li>\( \theta_{i} \) the angle between the surface normal and the incoming radiance direction</li>
<li>\( \theta_{o} \) the angle between the surface normal and the outgoing direction towards the viewer</li>
<li>\( f() \) the BRDF of the surface</li>
</ul>
<h2 id="the-olden-days---storing-irradiance">The Olden Days - Storing Irradiance</h2>
<p>Games have used pre-computed lightmaps for almost as long as they have been using shaded 3D graphics, and they&rsquo;re still quite popular in 2016. The idea is simple: pre-compute a lighting value for every texel, then sample those lighting values at runtime to determine the final appearance of a surface. It&rsquo;s a simple concept to grasp, but there are some details you might not think about if you&rsquo;re just learning how they work. For instance, what exactly does it mean to store &ldquo;lighting&rdquo; in a texture? What exact value are we computing, anyway? In the early days the value fetched from the lightmap was simply multiplied with the material&rsquo;s diffuse albedo color (typically done with fixed-function texture stages), and then directly output to the screen. Ignoring the issue of gamma correction and sRGB transfer functions for the moment, we can work backwards from this simple description to describe this old-school approach in terms of the rendering equation. This might seem like a bit of a pointless exercise, but I think it helps build a solid base that we can use to discuss more advanced techniques.</p>
<p>So we know that our lightmap contains a single fixed color per-texel, and we apply it the same way regardless of the viewing direction for a given pixel. This implies that we&rsquo;re using a simple Lambertian diffuse BRDF, since it lacks any sort of view-dependence. Recall that we compute the outgoing radiance for a single point using the following integral:</p>
<p>$$ L_{o}(\mathbf{o}, \mathbf{x}) = \int_{\Omega}f(\mathbf{i}, \mathbf{o}, \mathbf{x}) \cdot L_{i}(\mathbf{i}, \mathbf{x}) \cdot cos(\theta_{i}) \cdot d\Omega $$</p>
<p>If we substitute the standard diffuse BRDF of \( \frac{C_{diffuse}}{\pi} \) for our BRDF (where Cdiffuse is the diffuse albedo of the surface), then we get the following:</p>
<p>$$ L_{o}(\mathbf{o}, \mathbf{x}) = \int_{\Omega} \frac{C_{diffuse}}{\pi} \cdot L_{i}(\mathbf{i}, \mathbf{x}) \cdot cos(\theta_{i}) \cdot d\Omega  $$</p>
<p>$$ = \frac{C_{diffuse}}{\pi} \int_{\Omega} L_{i}(\mathbf{i}, \mathbf{x}) \cdot cos(\theta_{i}) \cdot d\Omega $$</p>
<p>On the right side we see that we can pull the constant terms out the integral (the constant term is actually the entire BRDF!), and what we&rsquo;re left with lines up nicely with how we handle lightmaps: the expensive integral part is pre-computed per-texel, and then the constant term is applied at runtime per-pixel. The &ldquo;integral part&rdquo; is actually computing the incident irradiance, which lets us finally identify the quantity being stored in the lightmap: it&rsquo;s irradiance! In practice however most games would not apply the 1 / π term at runtime, since it would have been impractical to do so. Instead, let&rsquo;s assume that the 1 / π was &ldquo;baked&rdquo; into the lightmap, since it&rsquo;s constant for all surfaces (unlike the diffuse albedo, which we consider to be <em>spatially varying</em>). In that case, we&rsquo;re actually storing a reflectance value that takes the BRDF into account. So if we wanted to be precise, we would say that it contains &ldquo;the diffuse reflectance of a surface with Cdiffuse = 1.0&rdquo;, AKA the maximum possible outgoing radiance for a surface with a diffuse BRDF.</p>
<h2 id="light-map-meet-normal-map">Light Map: Meet Normal Map</h2>
<p>One of the key concepts of lightmapping is the idea of reconstructing the final surface appearance using data that&rsquo;s stored at different rates in the spatial domain. Or in simpler words, we store lightmaps using one texel density while combining it with albedo maps that have a different (usually higher) density. This lets us retain the appearance of high-frequency details without actually computing irradiance integrals per-pixel. But what if we want to take this concept a step further? What it we also want the irradiance itself to vary in response to texture maps, and not just the diffuse albedo? By the early 2000&rsquo;s normal maps were starting to see common use for this purpose, however they were generally only used when computing the contribution from punctual light sources. Normal maps were no help with light maps that only stored a single (scaled) irradiance value, which meant that that pure ambient lighting would look very flat compared to areas using dynamic lighting:</p>
<p><a href="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/flat_lightmaps.png"><img src="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/flat_lightmaps_resized_1024.png" alt="Flat_Lightmaps"></a></p>
<center><i>Areas in direct lighting (on the right) have a varying appearance due to a normal map, but areas in shadow (on the left) have no variation due to being lit by a baked lightmap containing only a single irradiance value.</i></center>
<p>To make  lightmaps work with normal mapping, we need to stop storing a single value and instead somehow store a <em>distribution</em> of irradiance values for every texel. Normal maps contain a range of normal directions, where those directions are generally restricted to the hemisphere around a point&rsquo;s surface normal. So if we want our lightmap to store irradiance values for all possible normal map values, then it must contain a distribution of irradiance that&rsquo;s defined for that same hemisphere. One of the earliest and simplest examples of such a distribution was used by <a href="https://advances.realtimerendering.com/s2006/Mitchell-ShadingInValvesSourceEngine.pdf">Half-Life 2</a>[1], and was referred to as <a href="http://www2.ati.com/developer/gdc/D3DTutorial10_Half-Life2_Shading.pdf">Radiosity Normal Mapping</a>[2]:</p>
<p><img src="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/hl2_basis.png" alt="HL2_Basis"></p>
<center><i>Image from "Shading in Valve’s Source Engine ", SIGGRAPH 2006</i></center>
<p>Valve essentially modified their lightmap baker to compute 3 values instead of 1, with each value computed by projecting the irradiance signal onto one of the corresponding orthogonal <a href="https://en.wikipedia.org/wiki/Basis_(linear_algebra)">basis vectors</a> in the above image. At runtime, the irradiance value used for shading would be computed by blending the 3 lightmap values based on the cosine of the angle between the normal map direction and the 3 basis directions (which is cheaply computed using a dot product). This allowed them to effectively vary the irradiance based on the normal map direction, thus avoiding the &ldquo;flat ambient&rdquo; problem described above.</p>
<p>While this worked for their static geometry, there still remained the issue of applying pre-computed lighting to dynamic objects and characters. Some early games (such as the original Quake) used tricks like sampling the lightmap value at a character&rsquo;s feet, and using that value to compute ambient lighting for the entire mesh. Other games didn&rsquo;t even do that much, and would just apply dynamic lights combined with a global ambient term. Valve decided to take a more sophisticated approach that extended their hemispherical lightmap basis into a full spherical basis formed by 6 orthogonal basis vectors:</p>
<p><a href="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/ambientcube.png"><img src="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/ambientcube.png" alt="ambientcube"></a></p>
<center><i>Image from "Shading in Valve’s Source Engine ", SIGGRAPH 2006</i></center>
<p>The basis vectors coincided with the 6 face directions of a unit cube, which led Valve to call this basis the &ldquo;Ambient Cube&rdquo;. By projecting irradiance in all directions around a point in space (instead of a hemisphere surrounding a surface normal) onto their basis functions, a dynamic mesh could sample irradiance for any normal direction and use it to compute diffuse lighting. This type of representation is often referred to as a <em>lighting probe</em>, or often just &ldquo;probe&rdquo; for short.</p>
<h2 id="going-specular">Going Specular</h2>
<p>With Valve&rsquo;s basis we can combine normal maps and light maps to get diffuse lighting that can vary in response to high-frequency normal maps. So what&rsquo;s next? For added realism we would ideally like to support more complex BRDF&rsquo;s, including view-dependent specular BRDF&rsquo;s. Half-Life 2 handled environment specular by pre-generating cubemaps at hand-placed probe locations, which is still a common approach used by modern games (albeit with the addition of <a href="http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf">pre-filtering</a>[3] used to approximate the response from a microfacet BRDF). However the large memory footprint of cubemaps limits the practical density of specular probes, which can naturally lead to issues caused by incorrect parallax or disocclusion.</p>
<p><a href="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/envmap_disocclusion.png"><img src="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/envmap_disocclusion_resized_756.png" alt="EnvMap_Disocclusion"></a></p>
<center><i>A combination of incorrect parallax and disocclusion when using a pre-filtered environment as a source for environment specular. Notice the bright edges on the sphere, which are actually caused by the sphere reflecting itself!</i></center>
<p>With that in mind it would nice to be able to get some sort of specular response out of our lightmaps, even if only for a subset of materials. But if that is our goal, then our approach of storing an irradiance distribution starts to become a hinderance. Recall from earlier that with a diffuse BRDF we were able to completely pull the BRDF out of the irradiance integral, since the Lambertian diffuse BRDF is just a constant term. This is no longer the case even with a simple specular BRDF, whose value varies depending on both the viewing direction as well as the incident lighting direction.</p>
<p>If you&rsquo;re working with the Half-Life 2 basis (or something similar), a tempting option might be to compute a specular term as if the 3 basis directions were directional lights. If you think about what this means, it&rsquo;s basically what you get if you decide to say &ldquo;screw it&rdquo; and pull the specular BRDF out of the irradiance integral. So instead of Integrate(BRDF * Lighting * cos(theta)), you&rsquo;re doing BRDF * Integrate(Lighting * cos(theta)). This will definitely give you <em>something,</em> and it&rsquo;s perhaps a lot better than nothing. But you&rsquo;ll also effectively lose out on a ton of your specular response, since you&rsquo;ll only get specular when your viewing direction appropriately lines up with your basis directions according the the BRDF slice. To show you what I mean by this, here&rsquo;s a comparison:</p>
<p><a href="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/irradiance_specular_comparison1.png"><img src="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/irradiance_specular_comparison1_resized_542.png" alt="irradiance_specular_comparison"></a></p>
<center><i>The top image shows a path-traced rendering of a green wall being lit by direct sun lighting. The middle image shows the indirect specular component of the top image, with exposure increased by 4x. The bottom image shows the resulting specular from treating the HL2 basis directions as directional lights.</i></center>
<p>Hopefully these images clearly show the problem that I&rsquo;m describing. In the bottom image, you get specular reflections that look just like they came from a few point lights, since that&rsquo;s effectively what you&rsquo;re simulating. Meanwhile in the middle image with proper environment reflections, you can see that the the entire green wall effectively acts as an area light, and you get a very broad specular reflections across the entire floor. In general the problem tends to be less noticeable though as roughness increases, since higher roughness naturally results in broader, less-defined reflections that are harder to notice.</p>
<h2 id="lets-tryspherical-harmonics">Let&rsquo;s Try Spherical Harmonics</h2>
<p>If we want to do better, we must instead find a way to store a radiance distribution and then efficiently integrate it against our BRDF. It&rsquo;s at this point that we turn to spherical harmonics. Spherical harmonics (SH for short) have become a popular tool for real-time graphics, typically as a way to store an approximation of indirect lighting at discrete probe locations. I&rsquo;m not going to go into the full specifics of SH since that could easily fill an <a href="http://www.research.scea.com/gdc2003/spherical-harmonic-lighting.pdf">entire article</a>[4] on its own. If you have no experience with SH, the key thing to know about them is that they basically let you approximate a function defined on a sphere using a handful of coefficients (typically either 4 or 9 floats per RGB channel). It&rsquo;s sort-of as if you had a compact cubemap, where you can take a direction vector and get back a value associated with that direction. The big catch is that you can only represent very low-frequency (fuzzy) signals with lower-order SH, which can limit what sort of things you can do with it. You can project detailed, high-frequency signals onto SH if you want to, but the resulting projection will be very blurry. Here&rsquo;s an example showing what an HDR environment map looks like projected onto L2 SH, which requires 27 coefficients for RGB:</p>
<p><a href="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/wells_radiance.png"><img src="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/wells_radiance.png" alt="Wells_Radiance"></a></p>
<p><a href="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/wells_radiance_sh.png"><img src="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/wells_radiance_sh.png" alt="Wells_Radiance_SH"></a></p>
<center><i>The top image is an HDR environment map containing incoming radiance values about a sphere, while the bottom image shows the result of projecting that environment onto L2 spherical harmonics.</i></center>
<p>In the case of irradiance, SH can work pretty well since it&rsquo;s naturally low-frequency. The integration of incoming radiance against the cosine term effectively acts as a low-pass filter, which makes it a suitable candidate for approximation with SH. So if we project irradiance onto SH for every probe location or lightmap texel, we can now do an SH &ldquo;lookup&rdquo; (which is basically a few computations followed by a dot product with the coefficients) to get the irradiance in any direction on the sphere. This means we can get spatial variation from albedo and normal maps just like with the HL2 basis!</p>
<p>It also turns out that SH is pretty useful for <em>computing</em> irradiance from input radiance, since we can do it really cheaply. In fact it can do it so cheaply, it can be done at runtime by folding it into the SH lookup process. The reason it&rsquo;s so cheap is because SH is effectively a frequency-domain representation of the signal, and when you&rsquo;re in the frequency domain convolutions can be done with simple multiplication. In the spatial domain, convolution with a cubemap is an N^2 operation involving many samples from an input radiance cubemap. If you&rsquo;re interested in the full details, the process was described in Ravi Ramamoorthi&rsquo;s <a href="https://cseweb.ucsd.edu/~ravir/papers/envmap/">seminal paper</a>[5] from 2001, with derivations provided in <a href="https://cseweb.ucsd.edu/~ravir/papers/invlamb/">another article</a>[6].</p>
<p><a href="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/sh_diffuse.png"><img src="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/sh_diffuse_resized_300.png" alt="SH_Diffuse"></a></p>
<center><i>The Stanford Bunny model being lit with diffuse lighting from an L2 spherical harmonics probe</i></center>
<p>So we&rsquo;ve established that SH works for approximating irradiance, and that we can convert from radiance to irradiance at runtime. But what does this have to do with specular? By storing an approximation of radiance instead of irradiance in our probes or lightmaps (albeit, a very blurry version of radiance), we now have the signal that we need to integrate our specular BRDF against in order to produce specular reflections. All we need is an SH representation of our BRDF, and we&rsquo;re a dot product away from environment specular! The only problem we have to solve is how to actually <em>get</em> an SH representation of our BRDF.</p>
<p>Unfortunately a microfacet specular BRDF  is quite a bit more complicated than a Lambertian diffuse BRDF, which makes our lives more difficult. For diffuse lighting we only needed to worry about the cosine lobe, which has the same shape regardless of the material or viewing direction. However a specular lobe will vary in shape and intensity depending on the viewing direction, material roughness, and the fresnel term at zero incidence (AKA F0). If all else fails, we can always use monte-carlo techniques to pre-compute the coefficients and store the result in a lookup texture. At first it may seem like we need at parameterize our lookup table on 4 terms, since the viewing direction is two-dimensional. However we can drop a dimension if we follow in the <a href="https://www.gdcvault.com/play/253/Lighting-and-Material-of-HALO">footsteps</a>[7] of the intrepid engineers at Bungie, who used a neat trick for their <a href="https://www.chrisoat.com/papers/Siggraph2008-AdvancesInRealTimeRendering-CourseNotes.pdf">SH specular implementation in Halo 3</a>[8]. The key insight that they shared was that the specular lobe shape doesn&rsquo;t actually change as the viewer rotates around the local Z axis of the shading point (AKA the surface normal). It actually only changes based on the <em>viewing angle</em>, which is the angle between the view vector and the local Z axis of the surface. If we exploit this knowledge, we can pre-compute the coefficients for the set of possible viewing directions that are aligned with the local X axis. Then at runtime, we can rotate the coefficients so that the resulting lobe lines up with the actual viewing direction. Here&rsquo;s an image to show you what I mean:</p>
<p><a href="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/sh_rotation.png"><img src="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/sh_rotation_resized_296.png" alt="SH_Rotation"></a></p>
<center><i>Rotating a specular lobe from the X axis to its actual location based on the viewing direction, which is helpful for pre-computing the SH coefficients into a lookup texture</i></center>
<p>So in this image the checkerboard is the surface being shaded, and the red, green and blue arrows are the local X, Y, and Z axes of the surface. The transparent lobe represents the specular lobe that we precomputed for a viewpoint that&rsquo;s aligned with the X axis, but has the same viewing angle. The blue arrow shows how we can rotate the specular lobe from its original position to the actual position of the lobe based on the current viewing position, giving us the desired specular response. Here&rsquo;s a comparison showing what it looks like it in action:</p>
<p><a href="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/sh_specular_comparison.png"><img src="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/sh_specular_comparison_resized_481.png" alt="sh_specular_comparison"></a></p>
<center><i>The top image is a scene rendered with a path tracer. The middle image shows the indirect specular as rendered by a path tracer, with exposure increased 4x. The bottom image shows the indirect specular term computing an L2 SH lightmap, also with exposure increased by 4x.</i></center>
<p>Not too bad, eh? Or at least&hellip;not too bad as long as we&rsquo;re willing to store 27 coefficients per lightmap texel, and we&rsquo;re only concerned with rough materials. The comparison image used a GGX α parameter of 0.39, which is fairly rough.</p>
<p>One common issue with issue with SH is a phenomenon known as &ldquo;ringing&rdquo;, which is described in Peter-Pike Sloan&rsquo;s <a href="http://www.ppsloan.org/publications/StupidSH36.pdf">Stupid Spherical Harmonics Tricks</a>[9]. Ringing artifacts tends to show up when you have a very intense light source one side of the sphere. When this happens, the SH projection will naturally result in negative lobes on the opposite side of the sphere, whi<br>
h an result very low (or even negative!) values when evaluated. It&rsquo;s generally not too much of an issue for 2D lightmaps, since lightmaps are only concerned with the incoming radiance for a hemisphere surrounding the surface normal. However they often show up in probes, which store radiance or irradiance about the entire sphere. The solution suggested by Peter-Pike Sloan is to apply a windowing function to the SH coefficients, which will filter out the ringing artifacts. However the windowing will also introduce additional blurring, which may remove high-frequency components from the original signal being projected. The following image shows how ringing artifacts manifest when using SH to compute irradiance from an environment with a bright area light, and also shows how windowing affects the final result:</p>
<p><img src="/images/converted/sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/sh_ringing_comparison.png" alt="sh_ringing_comparison"></p>
<center><i>A sphere with a Lambertian diffuse BRDF being lit by a lighting environment with a strong area light source. The left image shows the ground-truth result of using monte-carlo integration. The middle image shows the result of projecting radiance onto L2 SH, and then computing irradiance. The right image shows the result of applying a windowing function to the L2 SH coefficients before computing irradiance.</i></center>
<p>Update 8/6/2019: For L1 (2-band SH), Graham Hazel developed a <a href="https://grahamhazel.com/blog/2017/12/22/converting-sh-radiance-to-irradiance/">technique</a> for Geomerics that reconstructions irradiance from radiance with less ringing and better contrast compared with the process originally developed by Ravi Ramamoorthi. The slides from his presentation have disappeared from the Geomerics website, but you can find the archived version <a href="https://web.archive.org/web/20160313132301/http://www.geomerics.com/wp-content/uploads/2015/08/CEDEC_Geomerics_ReconstructingDiffuseLighting1.pdf">here</a>. You can also find a reference implementation in Yuriy O&rsquo;Donnell&rsquo;s <a href="https://github.com/kayru/Probulator/blob/master/Source/Probulator/SphericalHarmonics.h#L136">Probulator code</a>.</p>
<h2 id="references">References</h2>
<p>[1] <a href="https://advances.realtimerendering.com/s2006/Mitchell-ShadingInValvesSourceEngine.pdf">Shading in Valve&rsquo;s Source Engine (SIGGRAPH 2006)</a><br>
[2] <a href="http://www2.ati.com/developer/gdc/D3DTutorial10_Half-Life2_Shading.pdf">Half Life 2 / Valve Source Shading</a><br>
[3] <a href="https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf">Real Shading in Unreal Engine 4</a><br>
[4] <a href="https://basesandframes.files.wordpress.com/2016/05/spherical_harmonic_lighting_gritty_details_green_2003.pdf">Spherical Harmonic Lighting: The Gritty Details</a><br>
[5] <a href="https://cseweb.ucsd.edu/~ravir/papers/envmap/">An Efficient Representation for Irradiance Environment Maps</a><br>
[6] <a href="https://cseweb.ucsd.edu/~ravir/papers/invlamb/">On the Relationship between Radiance and Irradiance: Determining the illumination from images of a convex Lambertian object</a><br>
[7] <a href="https://www.gdcvault.com/play/253/Lighting-and-Material-of-HALO">The Lighting and Material of Halo 3 (Slides)</a><br>
[8] <a href="https://www.chrisoat.com/papers/Siggraph2008-AdvancesInRealTimeRendering-CourseNotes.pdf">The Lighting and Material of Halo 3 (Course Notes)</a><br>
[9] <a href="http://www.ppsloan.org/publications/StupidSH36.pdf">Stupid Spherical Harmonics Tricks</a></p>
<hr>
<h2 id="comments">Comments:</h2>
<h3 id="heading"></h3>
<p><a href="%22fangsunjian2@gmail.com%22">fang</a> - <time datetime="2017-02-06 01:38:22">Feb 1, 2017</time></p>
<p>Hello! Thank you for sharing such great article. I have a question here. How do you think about vertex baking and texel baking? Why nowadays people tend to use texel baking instead of vertex baking?</p>
<hr />
###
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-11-01 12:58:58">Nov 2, 2016</time>
<p>Hey Tyler, for the bunny image I used the &ldquo;wells&rdquo; environment in probulator, which doesn&rsquo;t have as high of a dynamic range as the &ldquo;ennis&rdquo; probe. With the &ldquo;wells&rdquo; probe the ringing isn&rsquo;t particularly noticeable, so I used the standard L2 SH approach with no windowing.</p>
<hr />
###
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2017-02-14 01:16:27">Feb 2, 2017</time>
<p>There are pros and cons to either approach. Vertex baking tends to be easier to start with since you already have the structure set up for you, and there&rsquo;s no need to generate a unique 2D parameterization of your entire scene. However the major downside is that your sample density is effectively tied to your vertex density and topology. This means that meshes may need to be tessellated in order to capture high-frequency lighting changes, which can increase vertex, geometry, and pixel costs. With lightmaps the texel density can be adjusted independently of the underlying geometry, which makes it much easier spot fix problem areas. In our studio this was actually very important, since it removed a dependency between the lighting artists and environment artists. The structured 2D layout of textures also tends to be much better for interpolation and compression. On recent hardware you can use block compression formats like BC6H to drastically reduce the memory footprint, which is of course a huge win if you&rsquo;re memory constrained. Personally I keep a close eye on papers that presentations that look into alternative forms of storing baked sample points for a scene. For instance there are some presentations that have discussed using a sparse 3D grid to store data without needing vertices or 2D maps, and others that have used basis functions to &ldquo;splat&rdquo; the contribution of arbitrary points onto a scene.</p>
<hr />
###
[Tayfun K. (@notCamelCase)](http://twitter.com/notCamelCase "notCamelCase@twitter.example.com") - <time datetime="2016-10-10 02:04:23">Oct 1, 2016</time>
<p>Very informative posts, thank you! Shouldn&rsquo;t this be the &lsquo;middle image&rsquo; below in comparison of indirect speculars? &ldquo;Meanwhile in the bottom image with proper environment reflections, you can see that the the entire green wall effectively acts as an area light &hellip;&rdquo;</p>
<hr />
###
[Tyler]( "tylerrobertsondeveloper@gmail.com") - <time datetime="2016-11-01 10:01:22">Nov 2, 2016</time>
<p>It&rsquo;&rsquo;s difficult to tell, is the source IBL bright enough for the ringing artifact visible on the test with the bunny? Or did you use the windowed L2 SH there?</p>
<hr />
###
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-10-10 17:12:18">Oct 1, 2016</time>
<p>Indeed, that&rsquo;s exactly right: your normal maps are typically going to be much higher density than your light maps, since normal maps can be tiled and light maps will be completely unique over all surfaces (plus the lightmaps may have a larger per-texel footprint if they are HDR). So technically you *could* sample the normal maps when baking the lightmap, but since the texel density is much lower it would be as if you used a blurrier, downscaled version of the normal map.</p>
<hr />
###
[atrix256]( "alan.wolfe@gmail.com") - <time datetime="2016-10-10 14:31:41">Oct 1, 2016</time>
<p>Apologies, I get it now. The normals can&rsquo;t be taken into consideration because the light map is much lower resolution than the details needed to support features at the normal map level. Thanks for writing these, they are a great read (:</p>
<hr />
###
[atrix256]( "alan.wolfe@gmail.com") - <time datetime="2016-10-10 09:54:10">Oct 1, 2016</time>
<p>Hello! For the case of ambient lighting looking flat even in presence of a normal map, near the top of the article, why wasn&rsquo;t the normal map considered when calculating the baked lighting? It seems like that would also solve the problem, unless there is some reason that is undesirable?</p>
<hr />
###
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-10-10 09:50:45">Oct 1, 2016</time>
<p>Yes you&rsquo;re 100% correct: that sentence is referring to the middle image, not the bottom image. It&rsquo;s now been corrected. Thank you for pointing out the mistake!</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>New Blog Series: Lightmap Baking and Spherical Gaussians</title>
			<link>https://therealmjp.github.io/posts/new-blog-series-lightmap-baking-and-spherical-gaussians/</link>
			<pubDate>Mon, 10 Oct 2016 07:05:10 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/new-blog-series-lightmap-baking-and-spherical-gaussians/</guid>
			<description>So nearly year and a half ago myself and Dave Neubelt gave a presentation at SIGGRAPH where we described the approach that we developed for approximating incoming radiance using Spherical Gaussians in both our lightmaps and 3D probe grids. We had planned on releasing a source code demo as well as course notes that would serve as a full set of implementation details, but unfortunately those efforts were sidetracked by other responsibilities.</description>
			<content:encoded><![CDATA[<p><img src="/images/converted/new-blog-series-lightmap-baking-and-spherical-gaussians/white_room_small.png" alt="white_room_small"></p>
<p>So nearly year and a half ago myself and Dave Neubelt gave a <a href="http://blog.selfshadow.com/publications/s2015-shading-course/rad/s2015_pbs_rad_slides.pdf">presentation at SIGGRAPH</a> where we described the approach that we developed for approximating incoming radiance using Spherical Gaussians in both our lightmaps and 3D probe grids. We had planned on releasing a source code demo as well as course notes that would serve as a full set of implementation details, but unfortunately those efforts were sidetracked by other responsibilities. We had actually gotten pretty far on a demo, and it started to seem pretty silly to let a whole year go by without releasing it. So I&rsquo;ve cleaned it up, and put it on GitHub for all to learn from and/or make fun of. So if you&rsquo;re just interested in seeing the code or running the demo, then go ahead and download it:</p>
<p><a href="https://github.com/TheRealMJP/BakingLab">https://github.com/TheRealMJP/BakingLab</a></p>
<p>For those looking for some in-depth written explanation, I&rsquo;ve also decided to write a series of blog posts that should hopefully shed some light on the basics of using SG&rsquo;s in rendering. The first post provides background material by explaining  common approaches to storing pre-computing lighting data in lightmaps and/or probes. The second post focuses on explaining the basics of Spherical Gaussians, and demonstrating some of their more useful properties. The third post explains how the various SG properties can be used to compute diffuse lighting from an SG light source. The fourth post goes even deeper and covers methods for approximating the specular contribution from an SG light source. The fifth post explores some approaches for using SG&rsquo;s to create a compact approximation of a lighting environment, and compares the results with spherical harmonics. Finally, the sixth posts discusses features present in the the lightmap baking demo that we&rsquo;ve released on GitHub.</p>
<p>Part 1 - <a href="../sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/">A Brief (and Incomplete) History of Baked Lighting Representations</a><br>
Part 2 - <a href="../sg-series-part-2-spherical-gaussians-101/">Spherical Gaussians 101</a><br>
Part 3 - <a href="../sg-series-part-3-diffuse-lighting-from-an-sg-light-source/">Diffuse Lighting From an SG Light Source</a><br>
Part 4 - <a href="../sg-series-part-4-specular-lighting-from-an-sg-light-source/">Specular Lighting From an SG Light Source</a><br>
Part 5 - <a href="../sg-series-part-5-approximating-radiance-and-irradiance-with-sgs/">Approximating Radiance and Irradiance With SG&rsquo;s</a><br>
Part 6 - <a href="../sg-series-part-6-step-into-the-baking-lab/">Step Into The Baking Lab</a><br></p>
<p>Enjoy, and please leave a comment if you have questions, concerns, or corrections!</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="https://icedmaster.wordpress.com/2017/11/22/6697/" title="">Randomly generated stuff</a> - <time datetime="2017-11-22 18:28:50">Nov 3, 2017</time></p>
<p>[…] <a href="https://mynameismjp.wordpress.com/2016/10/09/new-blog-series-lightmap-baking-and-spherical-gaussians">https://mynameismjp.wordpress.com/2016/10/09/new-blog-series-lightmap-baking-and-spherical-gaussians</a>… […]</p>
<hr />
####
[Doug Chism]( "dougchism@hotmail.com") - <time datetime="2016-10-11 05:31:40">Oct 2, 2016</time>
<p>Wow, was planning to research this in the near future due to our indirect specular being rather lousy for higher roughness values using env-probes. This is going to be sure excellent reading this week, thank you immensely.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Bindless Texturing for Deferred Rendering and Decals</title>
			<link>https://therealmjp.github.io/posts/bindless-texturing-for-deferred-rendering-and-decals/</link>
			<pubDate>Fri, 25 Mar 2016 08:39:36 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/bindless-texturing-for-deferred-rendering-and-decals/</guid>
			<description>https://github.com/TheRealMJP/DeferredTexturing
https://github.com/TheRealMJP/DeferredTexturing/releases (Precompiled Binaries)
To Bind, or Not To Bind Unless you&amp;rsquo;ve been in a coma for the past year, you&amp;rsquo;ve probably noticed that there&amp;rsquo;s a lot of buzz and excitement around the new graphics API&amp;rsquo;s that are available for PC and mobile. One of the biggest changes brought by both D3D12 and Vulkan is that they&amp;rsquo;ve ditched the old slot-based system for binding resources that&amp;rsquo;s been in use since&amp;hellip;forever.</description>
			<content:encoded><![CDATA[<p><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/title.png" alt="Title Image"></p>
<p><a href="https://github.com/TheRealMJP/DeferredTexturing">https://github.com/TheRealMJP/DeferredTexturing</a></p>
<p><a href="https://github.com/TheRealMJP/DeferredTexturing/releases">https://github.com/TheRealMJP/DeferredTexturing/releases</a> (Precompiled Binaries)</p>
<h2 id="to-bind-or-not-to-bind">To Bind, or Not To Bind</h2>
<p>Unless you&rsquo;ve been in a coma for the past year, you&rsquo;ve probably noticed that there&rsquo;s a lot of buzz and excitement around the new graphics API&rsquo;s that are available for PC and mobile. One of the biggest changes brought by both D3D12 and Vulkan is that they&rsquo;ve ditched the old slot-based system for binding resources that&rsquo;s been in use since&hellip;forever. In place of the old system, both API&rsquo;s have a adopted a <a href="https://software.intel.com/en-us/articles/introduction-to-resource-binding-in-microsoft-directx-12">new model</a>[1] based around placing opaque resource descriptors in contiguous ranges of GPU-accessible memory. The new model has the potential to be more efficient, since a lot of hardware (most notably AMD&rsquo;s GCN-based GPU&rsquo;s) can read their descriptors straight from memory instead of having to keep them in physical registers. So instead of having the driver take a slot-based model and do behind-the-scenes gymnastics to put the appropriate descriptors into tables that the shader can use, the app can just put the descriptors in a layout that works right from the start.</p>
<p>The new style of providing resources to the GPU is often referred to as &ldquo;bindless&rdquo;, since you&rsquo;re no longer restricted to explicitly binding textures or buffers through dedicated API functions. The term &ldquo;bindless&rdquo; originally comes from Nvidia, who were the first to <a href="http://developer.download.nvidia.com/opengl/tutorials/bindless_graphics.pdf">introduce the concept</a>[2] through their <a href="http://developer.download.nvidia.com/opengl/specs/GL_NV_bindless_texture.txt">NV_bindless_texture</a>[3] extension for OpenGL. Their material shows some serious reductions in CPU overhead by skipping standard resource binding, and instead letting the app place 64-bit descriptor handles (most likely they&rsquo;re actually pointers to descriptors) inside of uniform buffers. One major difference between Nvidia bindless and D3D12/Vulkan bindless is that the new APIs don&rsquo;t allow you to simply put descriptor handles inside of constant/uniform buffers. Instead, they require you to manually specify (through a root signature) how you&rsquo;ll organize your tables of descriptors for a shader. It might seem more complicated the Nvidia extension, but doing it this way has a big advantage: it lets D3D12 still support hardware that has no support (or limited support) for pulling descriptors from memory. It also still allows you to go full-on Nvidia-style bindless via support for unbounded texture arrays in HLSL. With unbounded arrays you can potentially put all of your descriptors in one giant table, and then index into that array using values from root constants/constant buffers/structured buffers/etc. This basically lets you treat an integer the same as a &ldquo;handle&rdquo; in Nvidia&rsquo;s approach, with the added benefit that you don&rsquo;t need to actually store a full 64-bit integer. Not only can this be really efficient, but it also opens the door to new rendering techniques that use GPU-generated values to determine which textures to fetch from.</p>
<h2 id="deferred-texturing">Deferred Texturing</h2>
<p>One such use case for bindless textures is deferred texturing. The concept is pretty straightforward: instead of writing out a G-Buffer containing all of the material parameters required for shading, you instead write out your interpolated UV&rsquo;s as well as a material ID. Then during your deferred pass you can use your material ID to figure out which textures you need to sample, and use the UV&rsquo;s from your G-Buffer to actually sample them. The main benefit is that you can ensure that your textures are only sampled for visible pixels, without worrying about overdraw or quad packing. Depending on your approach, you may also be able to save on some G-Buffer space by virtue of not having to cram every material parameter in there. In practice you actually need more than just UV and material ID. For normal mapping you need your full tangent frame, which at minimum requires a quaternion. For mipmaps and anisotropic filtering we also need the screen-space derivatives of our UV&rsquo;s. These can computed in the pixel shader and then explicitly stored in the G-Buffer, or you can compute them from G-Buffer as long as you&rsquo;re willing to live with occasional artifacts. Nathan Reed has a <a href="http://www.reedbeta.com/blog/2014/03/25/deferred-texturing/">nice write-up</a>[4] on his blog discussing the various possibilities for G-Buffer layouts, so I would suggest reading through his article for some more details.</p>
<p>The place where bindless helps is in the actual sampling of the material textures during the deferred lighting phase. By putting all of your texture descriptors into one big array, the lighting pass can index into that array in order to sample the textures for any given material. All you need is a simple mapping of material ID -&gt; texture indices, which you can do by indexing into a structured buffer. Of course it&rsquo;s not really <em>required</em> to have bindless in order to pull this off. If you&rsquo;re willing to stuff all of your textures into a big texture array, then you could achieve the same thing on D3D10-level hardware. Or if you use virtual mapping, then it&rsquo;s pretty trivial to implement since everything&rsquo;s already coming from big texture atlases. In fact, the virtual mapping approach has already been used in a shipping game, and was <a href="http://advances.realtimerendering.com/s2015/aaltonenhaar_siggraph2015_combined_final_footer_220dpi.pdf">described at SIGGRAPH last year</a>[5][6]. That said, the bindless approach is probably the easiest to get running and also places the least constraints on existing pipelines and assets.</p>
<h2 id="go-go-gadget-d3d12">Go Go Gadget D3D12!</h2>
<p>As a way to test out the brand-new D3D12 version of my sample framework, I decided to have a go at writing a simple implementation of a deferred texturing renderer. It seemed like a good way to get familiar with some of the new features offered by D3D12, while also making sure that the new version of my sample framework was ready for prime-time. I also hoped that I could gain a better understanding of some of the practical issues involved in implementing deferred texturing, and use the experience to judge whether or not it might be a appealing choice for future projects.</p>
<p>Here&rsquo;s a quick breakdown of how I ultimately set up my renderer:</p>
<ul>
<li>Bin lights and decals into clusters
<ul>
<li>16x16 tiles aligned to screen space, 16 linearly-partitioned depth tiles</li>
</ul>
</li>
<li>Render sun and spotlight shadows
<ul>
<li>4 2048x2048 cascaded shadow maps for the sun</li>
<li>1024x1024 standard shadow maps for each spotlight</li>
</ul>
</li>
<li>Render scene to the G-Buffer
<ul>
<li>Depth (32bpp)</li>
<li>Tangent frame as a <a href="http://bitsquid.blogspot.com/2009/11/bitsquid-low-level-animation-system.html">packed quaternion</a>[7]  (32bpp)</li>
<li>Texture coordinates (32bpp)</li>
<li>Depth gradients (32bpp)</li>
<li>(Optional) UV gradients (64bpp)</li>
<li>Material ID  (8bpp)
<ul>
<li>7-bit Material index</li>
<li>1-bit for tangent frame handedness</li>
</ul>
</li>
</ul>
</li>
<li>If MSAA is not enabled:
<ul>
<li>Run a deferred compute shader that perfoms deferred shading for all pixels
<ul>
<li>Read attributes from G-Buffer</li>
<li>Use material ID to get texture indices, and use those indices to index into descriptor tables</li>
<li>Blend decals into material properties</li>
</ul>
</li>
<li>Render the sky to the output texture, using the depth buffer for occlusion</li>
</ul>
</li>
<li>Otherwise if MSAA is enabled
<ul>
<li>Detect &ldquo;edge&rdquo; pixels requiring per-sample shading using Material ID</li>
<li>Classify 8x8 tiles as having either having edges or having no edges</li>
<li>Render the sky to an MSAA render target texture, using the depth buffer for occlusion</li>
<li>Run a deferred compute shader for non-edge tiles, shading only 1 subsample per pixel</li>
<li>Run a deferred compute shader for edge tiles, shading 1 subsample for non-edge pixels and all subsamples for edge pixels</li>
<li>Resolve MSAA subsamples to a non-MSAA texture</li>
</ul>
</li>
<li>Post-processing</li>
<li>Present</li>
</ul>
<p>In order to have another reference point for evaluating quality and performance, I also decided to implement a <a href="http://www.humus.name/Articles/PracticalClusteredShading.pdf">clustered forward</a>[8] path side-by-side with the deferred renderer:</p>
<ul>
<li>Bin lights into clusters (16x16 tiles aligned to screen space, 16 linearly-partitioned depth tiles)</li>
<li>Render sun and spotlight shadows</li>
<li>(optional) Render scene depth prepass</li>
<li>Render scene with full shading
<ul>
<li>Read from material textures</li>
<li>Blend decals into material properties</li>
<li>Apply sunlight and spotlights</li>
</ul>
</li>
<li>if MSAA is enabled
<ul>
<li>Resolve MSAA subsamples to a non-MSAA texture</li>
</ul>
</li>
<li>Post-processing</li>
<li>Present</li>
</ul>
<h2 id="light-clusteringbinning">Light Clustering/Binning</h2>
<p>As you may have already noticed, I used the same clustered approach to binning lights for both the deferred and forward paths. I did this because it was simpler than having two different approaches to light selection, and it also seemed more fair to take that aspect out of the equation when comparing performance. However you could obviously use whatever approach you&rsquo;d like for binning lights when using deferred texturing, such as classic light bounds rasterization/blending or <a href="https://software.intel.com/en-us/articles/deferred-rendering-for-current-and-future-rendering-pipelines">tile-based subfrustum culling</a>[9].</p>
<p>To implement the actual binning, I used a similar setup to what was described in <a href="http://www.humus.name/Articles/PracticalClusteredShading.pdf">Emil Persson&rsquo;s excellent presentation</a>[8] from SIGGRAPH 2013. If you&rsquo;re not familiar, the basic idea is that you chop up your view frustum into a bunch subfrusta, both in screen-space XY as well as along the Z axis. This essentially looks like a voxel grid, except warped to fit inside the frustum shape of a perspective projection. This is actually rather similar to the approach used in tiled deferred or <a href="http://www.slideshare.net/takahiroharada/forward-34779335">Forward+</a>[10] rendering, except that you also bucket along Z instead of fitting each subfrustum to the depth buffer. This can be really nice for forward rendering, since it lets you avoid over-including lights for tiles with a large depth range.</p>
<p>In the Avalanche engine they decided to perform their light binning on the CPU, which is feasible since the binning doesn&rsquo;t rely on a depth buffer. Binning on the CPU can make a lot of sense, since a typical GPU approach to culling will often have many redundant calculations across threads and thread groups.It&rsquo;s also possible make it pretty fast through CPU parallelization, as demonstrated by <a href="https://software.intel.com/en-us/articles/forward-clustered-shading">Intel&rsquo;s Clustered Forward Shading sample</a>[11].</p>
<p>For my own implementation I decided that I wanted to stick with the GPU, and so I went with a different approach. Having shipped a game that used a Forward+-style renderer, I&rsquo;m pretty disappointed with the results of using a typical subfrustum plane-based culling scheme in a compute shader. The <a href="http://www.iquilezles.org/www/articles/frustumcorrect/frustumcorrect.htm">dirty secret</a>[12] of using plane/volume tests for frustum culling is that they&rsquo;re actually quite prone to false positives. The &ldquo;standard&rdquo; test can only exclude when your bounding volume is completely on the wrong side of one or more of your frustum planes. Unfortunately this means that it will fail for cases where the bounding volume intersects multiple planes at points outside the frustum. Even more unfortunate is that this particular case becomes more likely as your bounding volumes become large relative to your frustum, which is typically the case for testing lights against subfrusta. Spotlights are especially bad in this regard, since the wide portion will often intersect the planes of subfrusta that are actually just outside the narrower tip:</p>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/bad_culling.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/bad_culling.png" alt="Bad_Culling"></a></p>
<p>The top-right image shows a 3D perspective view, where you can clearly see that the cone in this situation doesn&rsquo;t actually intersect with the frustum. However if you look at the orthographic views, you can also see that the cone manages to be on both sides of the frustum&rsquo;s right plane. In practice you end up getting results like the following (tiles colored in green are &ldquo;intersecting&rdquo; with the spotlight):</p>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/spotlight_tile_culling_planes.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/spotlight_tile_culling_planes.png" alt="Spotlight_Tile_Culling_Planes"></a></p>
<p>As you can see, you can end up with a ton of false positives. In fact towards the right we&rsquo;re basically just filling in the screen-aligned AABB of the bounding cone, which turns into a whole lot of wasted work for those pixels.</p>
<p>This drove me nuts on The Order, since our lighting artists liked to pack our levels full of large, shadow-casting spotlights. On the CPU side I would resort to expensive frustum-frustum tests for shadow-casting spotlights, which makes sense when you consider that you can save a lot of CPU and GPU time by culling out an entire shadow map. Unfortunately frustum/frustum wasn&rsquo;t a realistic option for the tiled subfrustum culling that was performed on the GPU, and so for &ldquo;important&rdquo; lights I augmented the intersection test with a 2D mask generated via rasterization of the bounding cone. The results were quite a bit better:</p>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/spotlight_tile_culling_rasterized.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/spotlight_tile_culling_rasterized.png" alt="Spotlight_Tile_Culling_Rasterized"></a></p>
<p>For this demo, I decided to take  what I had done on The Order and move it into 3D by binning into Z buckets as well. Binning in Z is a bit tricky, since you essentially want the equivalent of solid voxelization except in the projected space of your the frustum. Working in projected space rules out some of the common voxelization tricks, and so I ended up going with a simple 2-pass approach. The first pass renders the backfaces of a light&rsquo;s bounding geometry, and marks the light&rsquo;s bit (each cluster stores a bitfield of active lights) in the furthest Z bucket intersected by the current triangle within a given XY bucket. To conservatively estimate the furthest Z bucket, I use pixel shader derivatives to get the depth gradients, and then compute the maximum depth at the corner of the pixel. This generally works OK, but when the depth gradient is large it&rsquo;s possible to extrapolate off the triangle. To minimize the damage in these cases, I compute a view-space AABB of the light on the CPU, and clamp the extrapolated depth to this AABB. After the backfacing pass, the frontfaces are then rendered. This time, the pixel shader computes the minimum depth bucket, and then walks forward along view ray until encountering the bucket that was marked by the backface pass. Here&rsquo;s a visualization of the binning for a single light in my demo:</p>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/light_binning_single.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/light_binning_single.png" alt="Light_Binning_Single"></a></p>
<p>The pixels marked in red are the ones that belong to a cluster where the light is active. The triangle-shaped UI in the bottom right is a visualization that shows the active clusters for the XZ plane located at y=0. This helps you to see how well the clustering is working in Z, which is where the most errors occur in my implementation. Here&rsquo;s another image showing the scene with all (20) lights enabled:</p>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/light_binning_all.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/light_binning_all.png" alt="Light_Binning_All"></a></p>
<p>To do this robustly, you really want to use <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn903791(v=vs.85).aspx">conservative rasterization</a>[13]. I have this option in my demo, but unfortunately there are still no AMD GPU&rsquo;s that support the feature. As a fallback, I also support forcing 4x or 8x MSAA modes to reduce the chance that the pixel shader won&rsquo;t be executed for a covered tile. For The Order I used 8x MSAA, and it was never an issue in practice. It would really only be an issue if the light was <em>very</em> small on-screen, in which case you could probably just rasterize a bounding box instead. I should also point out that in my implementation the depth buffer is not used to accelerate the binning process, or to produce more optimally-distributed Z buckets. I implemented it this way so that there would not be additional performance differences when choosing whether or not to enable a Z prepass for the forward rendering path.</p>
<h2 id="the-g-buffer">The G-Buffer</h2>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/g-buffer2.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/g-buffer2.png" alt="G-Buffer"></a></p>
<p>For rendering we&rsquo;re going to need a G-Buffer in which we can store whatever information comes from our vertex data, as well as a material ID that we can use to look up the appropriate textures during the deferred pass. In terms of vertex information, we need both the tangent frame and the UV&rsquo;s in order to sample textures and perform normal mapping. If we assume our tangent frame is an orthonormal basis, we can store it pretty compactly by using a quaternion. <code>R16G16B16A16_SNORM</code> is perfect for this use case, since it covers the expected range and provides great precision. However we can <a href="http://bitsquid.blogspot.com/2009/11/bitsquid-low-level-animation-system.html">crunch it down</a>[7] to 4 bytes per texel if we really want to keep it small (and we do!). The UV&rsquo;s are stored in a 16-bit UNORM format, which gives us plenty of precision as long as we store frac(UV) to keep things between 0 and 1. In the same texture as the UV&rsquo;s I also store screen-space depth gradients in the Z and W components.  After that is an optional 64bpp texture for storing the screen-space UV gradients, which I&rsquo;ll discuss in the next section. Finally, the G-Buffer also has an 8-bit texture for storing a material ID. The MSB of each textel is the handedness bit for the tangent frame, which is used to flip the direction of the bitangent once it&rsquo;s reconstructed from a quaternion. This brings us to a total of 25 bytes per sample when storing UV gradients, and 17 when computing them instead.</p>
<h2 id="uv-and-position-gradients">UV and Position Gradients</h2>
<p>One issue with deferred texturing is that you can&rsquo;t rely on automatic mip selection via screen-space UV gradients when sampling the material textures. The gradients computed in a pixel shader will be wrong for quads that span multiple triangles, and in a compute shader they&rsquo;re not available at all. The simplest way to solve this is to obtain the gradients in the pixel shader (using ddx/ddy) when rendering the scene to the G-Buffer, and then store those gradients in a texture. Unfortunately this means storing 4 separate values, which requires an additional 8 bytes of data per pixel when using 16-bit precision. It also doesn&rsquo;t help you at all if you require positional gradients during your deferred pass, which can be useful for things like gobos, decals, filterable shadows, or receiver plane shadow bias factors. Storing the full gradients of world or view-space position would be silly, but fortunately we can store depth gradients and use those to reconstruct position gradients. Depth gradients only need 2 values instead of 6, and we can use a 16-bit fixed-point format instead of floating-point. They also have the nice property of being constant across the surface of a plane, which makes them useful for detecting triangle edges.</p>
<p>Both the UV and depth gradients can be computed in the deferred pass by sampling values from neighboring pixels, but in practice I&rsquo;ve found it&rsquo;s actually somewhat tricky to get right. You have to be careful not to &ldquo;walk off the triangle&rdquo;, otherwise you might end up reading UV&rsquo;s from a totally unrelated mesh. Unless of course your triangle is so small that none of your neighbors came from the same triangle, in which case walking off might be your only option. You also have to take care around UV seams, including any you might have created yourself by using frac()!</p>
<p>In my implementation, I decided to always store depth gradients (where &ldquo;depth&rdquo; in this case is the post-projection z/w value stored in the depth buffer)  while supporting an option to either store or compute UV gradients. Doing it this way allowed me to utilize the depth gradients when trying to find suitable neighbor pixels for computing UV gradients, and also ensured that I always had high-quality positional gradients. The material ID was also useful here: by checking that a neighboring pixel used the same material and was also had the same depth gradients, I could be relatively certain that the neighbor was either from the same triangle, or from a coplanar triangle. The shader code for this step can be found <a href="https://github.com/TheRealMJP/DeferredTexturing/blob/master/BindlessDeferred/Deferred.hlsl#L132">here</a>, if you&rsquo;re interested.</p>
<p>To assess the quality, let&rsquo;s look at a visualization showing what the UV gradients look like when using ddx/ddy during forward rendering:</p>
<p><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/uvgradients_stored.png" alt="UVGradients_Stored"></p>
<p>And here&rsquo;s an image showing what computed UV gradients look like:</p>
<p><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/uvgradients_computed.png" alt="UVGradients_Computed"></p>
<p>As you can see there&rsquo;s quite a few places where my algorithm fails to detect neighbors (these pixels are dark), and other places where a neighboring pixel shouldn&rsquo;t have been used at all (these pixels are bright). I&rsquo;m sure the results could be improved with more time and cleverness, but you need to be careful that the amount of work done is enough to offset the cost of just writing out the gradients in the first place. On my GTX 970 it&rsquo;s actually faster to store the gradients than it is to compute them when MSAA is disabled, but then it switches to the other way around once MSAA is turned on.</p>
<p>It&rsquo;s worth noting that <a href="http://advances.realtimerendering.com/s2015/aaltonenhaar_siggraph2015_combined_final_footer_220dpi.pdf">Sebastian&rsquo;s presentation</a>[5] mentions that they reconstruct UV gradients in their implementation (see page 45),  although you can definitely see some artifacts around triangle edges in their comparison image. They also mention that they use &ldquo;UV distance&rdquo; to detect neighbors, which makes sense considering that they have unique UVs for their virtual texturing.</p>
<h2 id="non-msaa-shading">Non-MSAA Shading</h2>
<p>For non-MSAA rendering, the deferred pass is pretty straightforward. First, the G-Buffer attributes are read from their textures and used to compute the original pixel position and gradients. UV gradients are then read from the G-Buffer if present, or otherwise computed from neighboring pixels. The material ID from the G-Buffer is then used to index into a structured buffer that contains one element per material, where each element contains the descriptor indices for the material textures (albedo, normal, roughness, and metallic). These indices are used to index into a large descriptor table containing descriptors for every texture in the scene, so that the appropriate textures can be sampled using the pixel&rsquo;s UV coordinates and derivatives.</p>
<p>Once all of the surface and material parameters are read, they are passed into a function that performs the actual shading. This function will loop over all lights that were binned into the pixel&rsquo;s XYZ cluster, and compute the reflectance for each light source. This requires evaluating the surface BRDF, and also applying a visibility term that comes from the light&rsquo;s 1024x1024 shadow map. The shadow map is sampled with a 7x7 PCF kernel that&rsquo;s implemented as an optimized, unrolled loop that makes use of GatherCmp instructions. This helps to make the actual workload somewhat representative of what you might have in actual game, instead of biasing things too much towards ALU work. My scene also has a directional light from the sun, which uses 4 2048x2048 cascaded shadow maps for visibility. Finally, an ambient term is applied by means of a set SH coefficients representing the radiance from the skydome.</p>
<h2 id="msaa-edge-detection-and-tile-classification">MSAA Edge Detection and Tile Classification</h2>
<p>Just like any other kind of deferred rendering, MSAA needs special care. In particular we need to determine which pixels contain multiple unique subsamples, so that we can shade each subsample individually during the deferred pass. The key issue here is scheduling: the &ldquo;edge&rdquo; pixels that require subsample shading will typically be rather sparse in screen space, which makes dynamic branching a poor fit. The &ldquo;old-school&rdquo; way of scheduling is to create a stencil mask from the edge pixels, and then render in two pixel shader passes: one pass for per-pixel shading, another for per-sample shading that runs the shader at per-sample frequency. This can work better than a branch, but the hardware still may not be able to schedule it particularly well due to the sparseness of the edge pixels. It will also still need to make sure that the shader runs with 2x2 quads, which can result in a lot of needless helper executions.</p>
<p>The &ldquo;newer&rdquo; way to schedule edge pixels (and by &ldquo;newer&rdquo;, I mean 6 years old) is to use a compute shader that re-schedules threads within a thread group. Basically you detect edge pixels, append their location to a list in thread group shared memory, and then have the entire group iterate over that list once it finishes shading the first subsample. This effectively compacts the sparse list of edge pixels within a tile, allowing for coherent looping and branching. The downside is that you need to use shared memory, which can decrease your maximum occupancy if you use too much of it.</p>
<p>In my sample, I use the compute shader approach but with a new twist. Instead of performing the edge detection in the deferred pass, I run an earlier pass that checks for edges and builds a mask. This pass uses append buffers to build a list of 8x8 tiles that contain edge pixels, as well as a separate list containing tiles that have no edges at all. The append counts are used as indirect arguments for ExecuteIndirect, so that the edge and non-edge tiles can processed with two separate dispatches using two different shader permutations. This helps minimize overhead from shared memory usage, since the non-edge version of the compute shader doesn&rsquo;t touch shared memory at all.</p>
<p>As for the actual edge detection, my sample supports two different approaches. The first approach only checks the material ID, and flags pixels that contain multiple material ID values. This is a very conservative approach, since it will only flag pixels where meshes with different materials overlap. The second approach is more aggressive, and additionally flags pixels with varying depth gradients. A varying depth gradient means that we have multiple triangles that are not coplanar, which means that we avoid tagging edges for the case of a tessellated flat plane. Here&rsquo;s what the edge detection looks like using only the material ID:</p>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/msaa_mask_materialid.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/msaa_mask_materialid.png" alt="MSAA_Mask_MaterialID"></a></p>
<p>&hellip;and with the the more aggressive depth gradient check:</p>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/msaa_mask_zgradients1.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/msaa_mask_zgradients1.png" alt="MSAA_Mask_ZGradients"></a></p>
<h2 id="decals">Decals</h2>
<p>One of the big advantages of traditional deferred shading is that you can modify your G-Buffer before computing your lighting. Lots of games take advantage of this by rendering <a href="http://www.slideshare.net/blindrenderer/screen-space-decals-in-warhammer-40000-space-marine-14699854">deferred decals</a>[14] into the scene for things like graffiti, blood splatter, debris, and posters. It&rsquo;s much nicer than traditional forward-rendered decals, since you only need to light once per pixel even when accumulating multiple decals. The typical approach is to apply these decals in a deferred pass prior to lighting, where bounding volumes are rasterized representing the area of influence for each decal. In the pixel shader, the depth buffer is used to compute a surface position, which is then projected into 2D in order to compute a UV value. The projected UV can then be used to sample textures containing the decal&rsquo;s surface properties, which are then written out to be blended into the G-Buffer. The end result is cheap decals that can work on complex scene geometry.</p>
<p>To use a typical deferred decal system you need two things: a depth buffer, and a G-Buffer. The depth buffer is needed for the projection part, and the G-Buffer is needed so that you can blend in your properties before shading. For forward rendering you can get a depth buffer through a depth prepass, but you&rsquo;re out of luck for the G-Buffer part. We&rsquo;re in a similar position with deferred texturing: we have depth, but our G-Buffer lacks the parameters that we&rsquo;d typically want to modify from a decal. For The Order I worked around this by making a very specialized decal system. We would essentially accumulate values into a render target, with each channel of the texture corresponding to specific hard-coded decal type: bullet damage/cratering, scorching, and blood. The forward shaders would then read in those values, and would use them to modify material parameters before performing the lighting phase of the shader. It worked, but it obviously was super-specific to our game and wasn&rsquo;t at all generic. Although we did get two nice things from this approach: materials could customize how they reacted to decals (materials could actually allocate a fully-composited layer for the damaged areas inside of bullet decals), and decals would properly accumulate on top of one another.</p>
<p>The good news is that deferred projectors is definitely not the only way to do decals. You can actually remove the need for a both a depth buffer <em>and</em> a G-Buffer by switching to the same clustered approach that you can use for lights, which is an idea I&rsquo;ve been kicking around for a year or two now. You just need to build a per-cluster list of decals, iterate over the list in your shading pass, and apply the decal according to its projection. The catch is that our shading pass now needs access to the textures for every possible decal in the scene, and it needs to be able to access the appropriate texture based on a decal index. In D3D11 this would have meant using texture arrays or atlases, but with D3D12 we can potentially avoid these headaches thanks to power of bindless.</p>
<p>So does a clustered approach to decals actually work? Why, yes it does! I implemented them in my app, and got them to work with both the clustered forward and deferred texturing rendering paths. I even made a picker so that you can splat decals wherever you want in the scene, which is lots of fun! The decals themselves are just a set of sci-fi color and normal maps, and were <a href="http://nobiax.deviantart.com/art/Free-Decals-02-Sci-Fi-329262452">generously provided for free by Nobiax</a> from DeviantArt[15]. They end up looking like this:</p>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/decals.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/decals.png" alt="Decals"></a></p>
<p>In my demo the decal color and normal are just blended with the surface parameters based on the alpha channel from the color texture. However one advantage of applying decals in this way is that you&rsquo;re not restricted to framebuffer blending operations. So for instance, you can accumulate surface normals by <a href="http://blog.selfshadow.com/publications/blending-in-detail/">reorienting the decal normals</a>[19] to make them relative to the normals of the underlying surface.</p>
<p>As for their performance, I unfortunately don&rsquo;t have another decal implementation to compare against. However if I set up test case where most of the screen is covered in around 11 decals, the cost of the deferred pass (no MSAA) goes from 1.50ms to 2.00ms on my GTX 970. If I branch over the decals entirely (including reading from the cluster bitfield buffer), then the cost drops to 1.33ms. For the forward path it costs about 1.55ms with no decals, 2.20ms with decals, and 1.38 branching over the entire decal step.</p>
<h2 id="performance">Performance</h2>
<p>To measure performance, I captured GPU timing information via timestamp queries. All measurements were taken from the default camera position, at 1920x1080 resolution. The test scene is the CryTek Sponza (we really need a new test scene!) with 20 hand-placed spotlights, each casting a 1024x1024 shadow map. There&rsquo;s also a directional light for the sun that uses 4 2048x2048 shadow cascades. The scene uses normal, albedo, roughness, and metallic maps courtesy of <a href="http://www.alexandre-pestana.com/pbr-textures-sponza/">Alexandre Pestana</a>[16]. Here&rsquo;s what the frame breakdown looks like for the deferred texturing path, with no MSAA:</p>
<pre tabindex="0"><code>Render Total: 7.40ms (7.47ms max)
Cluster Update: 0.08ms (0.08ms max)
Sun Shadow Map Rendering: 1.30ms (1.34ms max)
Spot Light Shadow Map Rendering: 1.04ms (1.04ms max)
G-Buffer Rendering: 0.67ms (0.67ms max)
Deferred Rendering: 3.54ms (3.58ms max)
Post Processing: 0.59ms (0.60ms max)
</code></pre><p>&hellip;and here&rsquo;s what it looks like with 4x MSAA:</p>
<pre tabindex="0"><code>Render Total: 9.86ms (9.97ms max)
Cluster Update: 0.08ms (0.08ms max)
Sun Shadow Map Rendering: 1.30ms (1.34ms max)
Spot Light Shadow Map Rendering: 1.04ms (1.05ms max)
G-Buffer Rendering: 1.48ms (1.49ms max)
Deferred Rendering: 4.64ms (4.67ms max)
Post Processing: 0.56ms (0.57ms max)
MSAA Mask: 0.16ms (0.16ms max)
MSAA Resolve: 0.21ms (0.21ms max)
</code></pre><p>The first timing number is an average over the past 64 frames, while the second number is the maximum time from the past 64 frames.</p>
<p>The following chart shows how some of the various configurations scale with increasing MSAA levels:</p>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/gtx970-timings-7x7-pcf1.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/gtx970-timings-7x7-pcf1.png" alt="GTX970 Timings - 7x7 PCF"></a></p>
<p>So there&rsquo;s a few observations that we can make from this data. First of all, the deferred path generally does very well compared to the forward path. The forward path without a depth prepass is basically useless, which means we&rsquo;re clearly suffering from overdraw problems. I do actually sort by depth when rendering the main forward pass, but  my test scene doesn&rsquo;t have sufficient granularity to achieve good front-to-back ordering. Enabling the depth prepass improves things considerably, but not enough to match the deferred performance. Once we enable MSAA things go a little differently, as the forward paths scale up better compared to the deferred rendering path. At 4x forward and deferred are nearly tied, but that is only when the deferred path uses the conservative material ID check for detecting edge pixels. The conservative path skips many edges in the test scene, and so the AA quality is inferior to the forward path. Using the aggressive depth gradient edge test brings the quality more in line with the forward path, but it&rsquo;s also quite a bit more expensive. However I would also expect the forward path to scale more poorly with scene complexity, since pixel shader efficiency will only decrease as the triangle count increases. One other interesting observation we can make is that writing out the UV gradients doesn&rsquo;t seem to be an issue for our test scene when running on my 970. With no MSAA it&rsquo;s actually slightly faster (7.47ms vs 7.50ms) to just write out the gradients instead of computing them, but that changes by the time we get to 4x MSAA (9.97ms vs. 9.83ms).</p>
<p>I should point out that all of these timings were captured while using a heavy-duty, &ldquo;thrash your cache&rdquo; 7x7 PCF kernel that&rsquo;s implemented as an unrolled loop using GatherCmp. It undoubtedly causes a large increase in memory traffic, and it probably causes an increase in register pressure as well. I would imagine that this is especially bad for the forward path, since everything is done in one pixel shader. As an alternative, I also have an option to revert back to a simple 2x2 PCF kernel that only uses a single GatherCmp (you can toggle it yourself by changing the &ldquo;UseGatherPCF_&rdquo; flag at the top of Shading.hlsl). This path is probably a better representation for games that use filterable shadow maps, and possibly games that use aggressive PCF sampling optimizations. Here&rsquo;s what the data looks like:</p>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/gtx970-timings-2x2-pcf1.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/gtx970-timings-2x2-pcf1.png" alt="GTX970 Timings - 2x2 PCF"></a></p>
<p>Some of these results are quite different compared to the 7x7 case. The forward paths do much better than they did previously, especially at 4xMSAA. The deferred paths scale the same as they did before, with the aggressive edge detection again causing longer frame times.</p>
<p>At home I only have access to a GTX 970, and so that&rsquo;s what I used for almost all of my testing, profiling, and optimization. However I was able to verify that the demo works on an an AMD R9 280, as well as a GTX 980. I&rsquo;ve posted a summary of all of the performance data in table form below (all timings in milliseconds):</p>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/gtx-970-2x2-pcf.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/gtx-970-2x2-pcf.png" alt="GTX 970 - 2x2 PCF"></a></p>
<center><i>GTX 970 - 2x2 PCF</i></center>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/gtx-970-7x7-pcf.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/gtx-970-7x7-pcf.png" alt="GTX 970 - 7x7 PCF"></a></p>
<center><i>GTX 970 - 7x7 PCF</i></center>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/gtx-980-2x2-pcf.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/gtx-980-2x2-pcf.png" alt="GTX 980 - 2x2 PCF"></a></p>
<center><i>GTX 980 - 2x2 PCF</i></center>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/gtx-980-7x7-pcf.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/gtx-980-7x7-pcf.png" alt="GTX 980 - 7x7 PCF"></a></p>
<center><i>GTX 980 - 7x7 PCF</i></center>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/r9-380-2x2-pcf.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/r9-380-2x2-pcf.png" alt="R9 380 - 2x2 PCF"></a></p>
<center><i>R9 280 - 2x2 PCF</i></center>
<p><a href="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/r9-380-7x7-pcf.png"><img src="/images/converted/bindless-texturing-for-deferred-rendering-and-decals/r9-380-7x7-pcf.png" alt="R9 380 - 7x7 PCF"></a></p>
<center><i>R9 280 - 7x7 PCF</i></center>
<p>If you&rsquo;re interested in seeing the raw performance data that includes the per-frame breakdowns, I&rsquo;ve uploaded them here: 
<a href="https://mynameismjp.files.wordpress.com/2016/03/deferred-texturing-timings.zip">https://mynameismjp.files.wordpress.com/2016/03/deferred-texturing-timings.zip</a>. You can also access the timing data in the app by clicking on the &ldquo;Timings&rdquo; button in the top-left corner of the screen.</p>
<h2 id="conclusions-and-future-work">Conclusions and Future Work</h2>
<p>So what kind of conclusions can we draw from this experiment? Personally I&rsquo;m wary of extrapolating too much from such an artificial testing scenario with only one basis for comparison, but I think it&rsquo;s safe to say that the bindless deferred texturing approach doesn&rsquo;t have any sort of inherent performance issue that would render it useless for games. Or at least, it doesn&rsquo;t on the limited hardware that I&rsquo;ve used for testing. I was initially worried that the combination of SampleGrad and divergent descriptor indices would lead to to suboptimal performance, but in the end it didn&rsquo;t seem to be an issue for my particular setup. Although to be completely fair, the texture sampling ended up being a relatively small part of the shading process. It&rsquo;s certainly possible that the situation could change if the number of textures were to increase, or if the indexing became more divergent due to increased material density in screen-space.  But at the same time those situations could also lead to reduced performance during a forward pass or during a traditional G-Buffer pass, so it might end up being a wash anyway.</p>
<p>At least in my demo, the biggest performance issue for the deferred path seems to be MSAA. This shouldn&rsquo;t really be a surprise, considering how hard it is to implement affordable MSAA with any deferred renderer. My hope would be that a deferred texturing approach does a bit better than a traditional deferred renderer with a very large G-Buffer, but unfortunately I don&rsquo;t have data to prove that out. Ultimately it probably doesn&rsquo;t even matter, since hardly anyone even bothers with MSAA these days. :(</p>
<p>What about limitations? Multiple UV sets are at best a headache, and at worst a non-option. I think you&rsquo;d have to really like your artists to store another UV set in your G-Buffer, and also eat the cost of storing or computing another set of UV gradients. Not having a full G-Buffer might be an issue for certain screen-space techniques, like SSAO or screen-space reflections. I&rsquo;ve shown that it&rsquo;s possible to have decals even without a full G-Buffer, but it&rsquo;s more complex and possibly more expensive than traditional deferred decals. But on the upside, it&rsquo;s really nice to have a cheap geometry pass that doesn&rsquo;t need to sample any textures! It&rsquo;s also very friendly to GPU-driven batching techniques, which was demonstrated in the RedLynx presentation from SIGGRAPH.</p>
<p>There is one final reason why you might not want to do this (or at least not right now): it was a real pain in the ass to get this demo working in DX12. Driver bugs, shader compilation bugs, long compile times, validation bugs, driver crashes, blue screens of death: if it was annoying, I ran into it. Dynamic indexing support seems to be rather immature in both the shader compiler and the drivers, so tread carefully. The final code has a few work-arounds implemented, but I&rsquo;ve noted them with a comment.</p>
<p><em>Update 8/4/2019: The situation has improved substantially since I wrote this article. Our engine at RAD now makes full use of bindless resources throughout its various rendering passes. Drivers have become much more robust, and the open source shader compiler (DXC) is much faster and more robust compared with FXC (which allowed me to remove some of my workarounds). I&rsquo;ve also used wave-level intrinsics from SM6.0 to implement scalarization techniques for loading the light/decal data, which forces wave coherency and can substantially reduce VGPR usage on AMD hardware. There is also now a GPU-based validation layer that can validate resources accessed through descriptor indexing, and PIX can now do a similar analysis to show which resources were actually used within a draw or dispatch.</em></p>
<p>If I were to spend more time on this, I think it would be interesting to explore some of the more extreme variants of deferred texturing. In particular there&rsquo;s Intel&rsquo;s paper on <a href="http://jcgt.org/published/0002/02/04/">Visibility Buffers</a>[17], where the authors completely forego storing any surface data except for triangle ID and instance ID. All surface data is instead reconstructed by transforming vertices during the deferred pass, and performing a ray-triangle intersection to compute barycentrics for interpolation. There&rsquo;s also Tomasz Stachowiak&rsquo;s presentation about <a href="https://onedrive.live.com/view.aspx?resid=EBE7DEDA70D06DA0!115&amp;app=PowerPoint&amp;authkey=!AP-pDh4IMUug6vs">deferred material rendering</a>[18], where barycentric coordinates are stored in the G-Buffer instead of being reconstructed (which he does by tricking the driver into accepting his hand-written GCN assembly!!!). He has some neat ideas about using tile classification to execute different shader paths based on the material, which is something that could be integrated with the MSAA tile classification that&rsquo;s performed in my demo. Finally in the RedLynx presentation they use a neat trick where they render with MSAA at half resolution, and then reconstruct full-resolution surface samples during the deferred pass. It makes the deferred shader more complicated, but reduces the pixel shader cost of rasterizing the G-Buffer. These are all things I would love to implement in my demo if I had infinite time, but at some point I actually need to sleep. :)</p>
<h2 id="thats-all-folks">That&rsquo;s All, Folks!</h2>
<p>If you&rsquo;ve made it this far, thank you for hanging in there! This one might be my longest so far! I considering making it a series of articles, but I didn&rsquo;t want it to turn into one of those blog series where the author just never finishes it.</p>
<p>If you want to look at the code or run the sample, everything is available on GitHub:</p>
<p><a href="https://github.com/TheRealMJP/DeferredTexturing">https://github.com/TheRealMJP/DeferredTexturing</a></p>
<p><a href="https://github.com/TheRealMJP/DeferredTexturing/releases">https://github.com/TheRealMJP/DeferredTexturing/releases</a> (Precompiled Binaries)</p>
<p>If you find any bugs or have any suggestions, please let me know via comments, email, GitHub issue, or twitter!</p>
<h2 id="references">References</h2>
<p>[1] <a href="https://software.intel.com/en-us/articles/introduction-to-resource-binding-in-microsoft-directx-12">Introduction To Resource Binding In D3D12 (Intel)</a><br>
[2] <a href="http://developer.download.nvidia.com/opengl/tutorials/bindless_graphics.pdf">OpenGL Bindless Extensions (Nvidia)</a><br>
[3] <a href="http://developer.download.nvidia.com/opengl/specs/GL_NV_bindless_texture.txt">GL_NV_bindless_texture (OpenGL Extension Registry)</a><br>
[4] <a href="http://www.reedbeta.com/blog/2014/03/25/deferred-texturing/">Deferred Texturing (Nathan Reed)</a><br>
[5] <a href="http://advances.realtimerendering.com/s2015/aaltonenhaar_siggraph2015_combined_final_footer_220dpi.pdf">GPU-Driven Rendering Pipelines (Haar and Aaltonen, SIGGRAPH 2015)</a><br>
[6] <a href="https://forum.beyond3d.com/threads/modern-textureless-deferred-rendering-techniques.57611/">Modern Textureless Deferred Rendering Techniques (Beyond3D)</a><br>
[7] <a href="http://bitsquid.blogspot.com/2009/11/bitsquid-low-level-animation-system.html">The Bitsquid Low-Level Animation System</a><br>
[8] <a href="http://www.humus.name/Articles/PracticalClusteredShading.pdf">Practical Clustered Shading (Emil Persson)</a><br>
[9] <a href="https://software.intel.com/en-us/articles/deferred-rendering-for-current-and-future-rendering-pipelines">Deferred Rendering for Current and Future Rendering Pipelines (Intel, Andrew Lauritzen)</a><br>
[10] <a href="http://www.slideshare.net/takahiroharada/forward-34779335">Forward+: Bringing Deferred Lighting To The Next Level (AMD, Takahiro Harada)</a><br>
[11] <a href="https://software.intel.com/en-us/articles/forward-clustered-shading">Forward Clustered Shading (Intel)</a><br>
[12] <a href="http://www.iquilezles.org/www/articles/frustumcorrect/frustumcorrect.htm">Correct Frustum Culling (Íñigo Quílez)</a><br>
[13] <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn903791(v=vs.85).aspx">Conservative Rasterization (MSDN)</a><br>
[14] <a href="http://www.slideshare.net/blindrenderer/screen-space-decals-in-warhammer-40000-space-marine-14699854">Screen Space Decals in Warhammer 40K: Space Marine (Pope Kim)</a><br>
[15] <a href="http://nobiax.deviantart.com/art/Free-Decals-02-Sci-Fi-329262452">Free Sci-Fi Decals 2 (Nobiax, DeviantArt)</a><br>
[16] <a href="http://www.alexandre-pestana.com/pbr-textures-sponza/">Base color, Roughness and Metallic textures for Sponza (Alexandre Pestana)</a><br>
[17] <a href="http://jcgt.org/published/0002/02/04/">The Visibility Buffer: A Cache-Friendly Approach to Deferred Shading (Burns and Hunt, JCGT)</a><br>
[18] <a href="https://onedrive.live.com/view.aspx?resid=EBE7DEDA70D06DA0!115&amp;app=PowerPoint&amp;authkey=!AP-pDh4IMUug6vs">A Deferred Material Rendering System (Tomasz Stachowiak)</a><br>
[19] <a href="http://blog.selfshadow.com/publications/blending-in-detail/">Blending In Detail (Barré-Brisebois and Hill)</a><br></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://gravatar.com/jeroenstout" title="jeroenstout@gmail.com">Jeroen D Stout</a> - <time datetime="2016-03-25 07:46:18">Mar 5, 2016</time></p>
<p>Thank you, as always, for sharing your ideas and tests so liberally, it has been very inspiring on many occasions. One thing this all made me think about is the recent interest in distance fields. The idea of using a scene approximation distance field for reflections is enticing to me, as I am mostly interested in depicting relatively small scenes as realistically as possible. Would you happen to have any ideas on rendering via a distance field, finding the intersection and using that (eventually) to fetch textures, materials, shaders, &amp;c? The deferred material rendering article made me think that is not as crazy as it may sound; and it might play nice (seeing all this) with reflections.</p>
<hr />
####
[Daniel Wright (@EpicShaders)](http://twitter.com/EpicShaders "EpicShaders@twitter.example.com") - <time datetime="2016-03-25 14:31:08">Mar 5, 2016</time>
<p>Jeroen, a distance field intersection would only give you world position, so you&rsquo;d have to use a data structure that can evaluate a material entirely from that, not an easy thing to do efficiently! Plus you really need prefiltered results to solve the cone queries you are doing.</p>
<hr />
####
[Jeroen D Stout](http://gravatar.com/jeroenstout "jeroenstout@gmail.com") - <time datetime="2016-03-25 14:46:53">Mar 5, 2016</time>
<p>Daniel, Hmm, to clarify, my thought was hypothetically you could store the &rsquo;nearest&rsquo; Mat-ID in the field (if it is a 3d texture) along with a U and a V (admittedly making it a much larger texture). That can get you a world position + UV + mat-ID and come closer to the data for &lsquo;deferred material rendering&rsquo;. It&rsquo;s perhaps not very practical.</p>
<hr />
####
[Ingramb]( "ingramb@gmail.com") - <time datetime="2016-03-26 19:52:57">Mar 6, 2016</time>
<p>Any reason why you use buffers rather than 3D textures for cluster storage? Stylistic choice, or are there practical considerations? Really nice article and sample, thanks for sharing.</p>
<hr />
####
[zbethel](http://zachbethel.wordpress.com "zach.bethel@gmail.com") - <time datetime="2016-03-29 12:42:09">Mar 2, 2016</time>
<p>Do you ever sleep? :) This is great work, thanks for sharing. I&rsquo;m particularly interested in the clustered decal approach, as forward pipelines have traditionally suffered from decal support. It seems like the magic bullet these days is tiled compute approaches! The textureless deferred stuff is interesting, but I&rsquo;m not sure I&rsquo;m sold. For one, it still suffers from high memory bandwidth, which means you still end up needing a Z-Prepass. More importantly though, high bandwidth deferred pipelines don&rsquo;t scale to higher resolutions. We&rsquo;ve hit a wall with our deferred pipeline in that we simply can&rsquo;t scale above 1080p and maintain any scene complexity. I&rsquo;d be interested to run this at a higher resolution and see how the performance compares.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-03-29 22:56:38">Mar 2, 2016</time>
<p>@ingramb: I mainly used a flat buffer so that I could easily vary the maximum number of lights and decals per cluster without having to worry about texture formats. But to be honest I didn&rsquo;t give it a whole lot of thought. @zbethel: the G-Buffer is definitely still fatter than I&rsquo;d like it to be. However I&rsquo;m not sure that you need a depth prepass. I used to have an option to turn on depth prepass for the deferred path, but I disabled it because it was never faster. That might not be the case on hardware with less bandwidth, but either way the G-Buffer pixel shader is *very* cheap. If you want to try it yourself you can re-enable it by uncommenting the code on line 1578 of BindlessDeferred.cpp. I think for lower-end desktop or mobile hardware that&rsquo;s more bandwidth-constrained, approaches like the Visibility Buffer start to look more appealing due to the ultra-slim G-Buffer. It&rsquo;s also nice for MSAA, for the same reason. If I run maximized on my 2560x1440 monitor on my GTX 970 I get 10.73ms for deferred, vs. 11.63ms for forward. At 4xMSAA this changes to 14.20ms for deferred (with computing UV gradients enabled), vs. 14.00ms for forward. Using the aggressive edge detection pushes the deferred path up to about 16.00ms.</p>
<hr />
####
[]( "") - <time datetime="2016-12-16 03:28:14">Dec 5, 2016</time>
<p>I&rsquo;d love to use this technique. But I ran into a troubling thought. In a normal deferred shading pipeline, any object that requires some unique shaders to do some fancy work to it&rsquo;s materials can just output that data to the correct GBuffer. However, how would you do that for deferred texturing? I don&rsquo;t think you can rebind your programs dynamically from the GPU.</p>
<hr />
####
[Jason]( "jasonhuang1988@gmail.com") - <time datetime="2016-09-03 11:58:06">Sep 6, 2016</time>
<p>Thx a lot, it is this most inspriing article I have ever seen. Btw, would you like to try radiance hints (GI) some day?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-06-19 16:40:09">Jun 0, 2016</time>
<p>Hey Sean, glad you liked the post. I just tried compiling and running the latest code on my GTX 970, and I don&rsquo;t see either of those issues. Are you on the latest driver? I&rsquo;m currently running on driver 368.39, which is the latest available.</p>
<hr />
####
[Sean Dunn](https://www.facebook.com/app_scoped_user_id/10153555138757301/ "seanedunn@yahoo.com") - <time datetime="2016-06-19 08:49:48">Jun 0, 2016</time>
<p>This is great stuff, and a very educational post. One problem, though. I&rsquo;m testing out your demo, and there are some issues I&rsquo;m having, and wondering if they&rsquo;re known: 1. Lights flicker terribly in the Clustered Forward render mode, 2. In Deferred Texturing mode, there are many places where the UVs stretch across multiple polygons. These did not show up in your screenshots above, but I&rsquo;m running on a GTX 970 as well, so I wouldn&rsquo;t expect to see this. Ex: <a href="http://imgur.com/WoXtMjV">http://imgur.com/WoXtMjV</a></p>
<hr />
####
[Michael]( "segura.mikael@orange.fr") - <time datetime="2016-06-03 15:04:51">Jun 5, 2016</time>
<p>Since the forward path is faster using 4xMSAA, is it still better to keep the deferred path ?</p>
<hr />
####
[Sean Dunn](https://www.facebook.com/app_scoped_user_id/10153555138757301/ "seanedunn@yahoo.com") - <time datetime="2016-06-20 19:52:29">Jun 1, 2016</time>
<p>Doh. Turns out this demo is the reason I upgraded to Windows 10, and in my rush, I forgot that my graphics driver would have to be manually updated. Works great now, thanks.</p>
<hr />
####
[Cull that cone! Improved cone/spotlight visibility tests for tiled and clustered lighting | Bart Wronski](http://bartwronski.com/2017/04/13/cull-that-cone/ "") - <time datetime="2017-04-13 19:52:40">Apr 4, 2017</time>
<p>[…] are few solutions to it. I will mention first a heavy / complicated, but best solution, suggested by Matt Pettineo – rasterizing the cone in tested space and then either simply checking against this […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Update For My Shadow Sample Update</title>
			<link>https://therealmjp.github.io/posts/update-for-my-shadow-sample-update/</link>
			<pubDate>Mon, 25 Jan 2016 00:24:56 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/update-for-my-shadow-sample-update/</guid>
			<description>Recently I was contacted by Christoph Peters (one of the authors of Moment Shadow Mapping) regarding a blog post where I compared EVSM to MSM using my sample app. He noticed that I was incorrectly clamping the maximum exponential warp to 10.0 for the 16-bit variant of EVSM, which can result in values that are greater than what can be stored in a 16-bit floating point texture. Doing this has 2 effects: it causes incorrect results during filtering (clamping the moments can lead to negative variance, causing a reconstruction that resembles a step function), and it reduces the amount of light bleeding.</description>
			<content:encoded><![CDATA[<p>Recently I was contacted by Christoph Peters (one of the authors of <a href="http://cg.cs.uni-bonn.de/en/publications/paper-details/peters-2015-msm/">Moment Shadow Mapping</a>) regarding a <a href="../shadow-sample-update/">blog post</a> where I compared EVSM to MSM using my sample app. He noticed that I was incorrectly clamping the maximum exponential warp to 10.0 for the 16-bit variant of EVSM, which can result in values that are greater than what can be stored in a 16-bit floating point texture. Doing this has 2 effects: it causes incorrect results during filtering (clamping the moments can lead to negative variance, causing a reconstruction that resembles a step function), and it reduces the amount of light bleeding. Unfortunately all of my comparisons were done without any additional prefiltering, and so I didn&rsquo;t notice my error. Even more unfortunate was that it misrepresented the extent of EVSM16&rsquo;s light bleeding issues in my comparison images, making the test unfairly skewed in favor of EVSM.</p>
<p>To help make things right, I&rsquo;ve uploaded a <a href="https://github.com/TheRealMJP/Shadows">new version of the sample app</a> to GitHub that correctly clamps the exponential warp factor to 5.54. The <a href="https://github.com/TheRealMJP/Shadows/releases">releases page</a> has a zip file containing code and a compiled executable. I&rsquo;ve also updated the <a href="../shadow-sample-update/">original blog post in question</a> with new comparison images, as well as new commentary on those images. You&rsquo;ll notice that my thoughts on MSM have become quite a bit more positive now that I&rsquo;ve taken a second look, and performed a more fair analysis. In fact, it now seems that MSM16 is hands-down the better option if you&rsquo;re looking to stay in an 64bpp footprint for the shadow map.</p>
<p>Finally, I&rsquo;d like to apologize to Christoph and Reinhard for my unfair comparison. The work they&rsquo;ve presented (including some <a href="http://cg.cs.uni-bonn.de/en/publications/paper-details/peters-2016-msm-applications/">new research</a> being presented at I3D 2016) is very promising, and it&rsquo;s great to see research that&rsquo;s immediately usable and practical for games. I also need to call out that they release sample code alongside their work, which is extremely helpful for evaluating and understanding their techniques. Hopefully we see more great things from them in the future!</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://gravatar.com/happysde" title="fn1186@gmail.com">happysde</a> - <time datetime="2016-02-26 06:14:08">Feb 5, 2016</time></p>
<p>I suggest a buildfix for VS2015: In Shadows.cpp (119-120), add &ldquo;.AsEnum()&rdquo; in both strings. So the result should be like Float4x4 meshWorld = Float4x4::ScaleMatrix(MeshScales[AppSettings::CurrentScene.AsEnum()]); meshRenderer.SetSceneMesh(context, &amp;models[AppSettings::CurrentScene.AsEnum()], meshWorld);</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Stairway To (Programmable Sample Point) Heaven</title>
			<link>https://therealmjp.github.io/posts/programmable-sample-points/</link>
			<pubDate>Mon, 14 Sep 2015 03:31:37 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/programmable-sample-points/</guid>
			<description>What Is and Should Never Be Historically, the sub-pixel location of MSAA sample points was totally out of your control as a programmer. Typically the hardware used rotated grid patterns such as this one, which were fixed for every pixel in your render target. For FEATURE_LEVEL_10_1, D3D added the concept of standard sample patterns that were required to be supported by the hardware. These were nice, in that you could specify the appropriate quality level and know exactly where the samples would be located.</description>
			<content:encoded><![CDATA[<h2 id="what-is-and-should-never-be">What Is and Should Never Be</h2>
<p>Historically, the sub-pixel location of MSAA sample points was totally out of your control as a programmer. Typically the hardware used rotated grid patterns such as <a href="/images/converted/programmable-sample-points/msaa_4xpattern.png">this one</a>, which were fixed for every pixel in your render target. For <code>FEATURE_LEVEL_10_1</code>, D3D added the concept of <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/Ff476218(v=VS.85).aspx">standard sample patterns</a> that were required to be supported by the hardware. These were nice, in that you could specify the appropriate quality level and know exactly where the samples would be located. Without that, you either had to use <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb943997(v=vs.85).aspx">shader intrinsics</a> or resort to <a href="../msaa-sample-pattern-detector/">wackier methods</a> for figuring out the sample positions. In theory it also gives you a very limited ability to control where sample points are located, but in practice the GPU vendors just made the standard multisample patterns the only patterns that they supported. So if you specified quality level 0, you got the same pattern as if you specified <code>D3D11_STANDARD_MULTISAMPLE_PATTERN</code>.</p>
<p>Fairly recently, we&rsquo;ve finally seen the idea of programmable sample points getting some major attention. This has primarily came from Nvidia, who just added this functionality to their second-generation Maxwell architecture. You&rsquo;ve probably seen <a href="http://www.geforce.com/hardware/technology/mfaa/technology">some</a> of the <a href="http://www.extremetech.com/gaming/194629-analyzing-nvidias-new-aa-can-mfaa-improve-image-quality-without-a-frame-rate-hit">articles</a> about their new &ldquo;Multi-Frame Sampled Anti-Aliasing&rdquo; (MFAA), which exploits the new hardware capabilities to jitter MSAA sample positions across alternating frames[1]<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. The idea is that you can achieve a doubled effective sampling rate, as long as your resolve intelligently retrieves the subsamples from your previous frame. They also incorporate ideas from <a href="http://www.cs.ubc.ca/~heidrich/Papers/RW.01_2.pdf">interleaved sampling</a>, by varying their sample locations across a 2x2 quad instead of using the same pattern for all pixels. While the idea of yet-another control panel AA setting probably won&rsquo;t do more than elicit some collective groans from the graphics community, it should at least get us thinking about what we might do if provided with full access to this new functionality for ourselves. And now that Nvidia has added  an <a href="https://developer.nvidia.com/sites/default/files/akamai/opengl/specs/GL_NV_sample_locations.txt">OpenGL extension</a> as well as a corresponding D3D11 extension to their proprietary <a href="https://developer.nvidia.com/nvapi">NVAPI</a>, we can finally try out our own ideas (unless you work on consoles, in which case you <a href="http://advances.realtimerendering.com/s2015/aaltonenhaar_siggraph2015_combined_final_footer_220dpi.pptx">may have</a> been <a href="http://advances.realtimerendering.com/s2014/drobot/HRAA_notes_final.pdf">experimenting</a> with them already!).</p>
<p>As for AMD, they&rsquo;ve actually supported some form of programmable sample points since as far back as <a href="http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/10/R6xx_3D_Registers.pdf">R600</a>, at least if the command buffer documentation is accurate (look, for PA_SC_AA_SAMPLE_LOCS). Either way, <a href="http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/10/SI_3D_registers.pdf">Southern Islands</a> certainly has support for varying sample locations across a 2x2 quad of pixels, which puts it on par with the functionality present in Maxwell 2.0. It&rsquo;s a little strange to that AMD hasn&rsquo;t done much to promote this functionality in the way that Nvidia has, considering they&rsquo;ve had it for so long. Currently there&rsquo;s no way to access this feature through D3D, but they have had an <a href="https://www.opengl.org/registry/specs/AMD/sample_positions.txt">OpenGL extension</a> for a long time now (thanks to <a href="https://twitter.com/NIV_Anteru">Matthäus Chajdas</a> and <a href="https://twitter.com/grahamsellers">Graham Sellers</a> for pointing out the extension!).</p>
<p>I&rsquo;m not particularly knowledgeable about Intel GPU&rsquo;s, and some quick searching didn&rsquo;t return anything to indicate that they might be able to specify custom sample points. If anyone knows otherwise, then please let me know!</p>
<p><em>Update: Andrew Lauritzen has informed me via <a href="https://twitter.com/AndrewLauritzen/status/643458084195581952">Twitter</a> that Intel GPU&rsquo;s do in fact support custom sample locations! Thank you again, Andrew!</em></p>
<h2 id="how-does-it-work">How Does It Work?</h2>
<p>Before we get into use cases, let&rsquo;s quickly go over how to actually work with programmable sample points. Since I usually only use D3D when working on PC&rsquo;s, I&rsquo;m going to focus on the extensions for Maxwell GPU&rsquo;s that were exposed in NVAPI. If you look in the NVAPI headers, you&rsquo;ll find a function for creating an extended rasterizer state, with a corresponding description structure that has new members:</p>
<pre tabindex="0"><code>NvU32 ForcedSampleCount;
bool ProgrammableSamplePositionsEnable;
bool InterleavedSamplingEnable;
NvU8 SampleCount;
NvU8 SamplePositionsX[16];
NvU8 SamplePositionsY[16];
bool ConservativeRasterEnable;
NVAPI_QUAD_FILLMODE QuadFillMode;
bool PostZCoverageEnable;
bool CoverageToColorEnable;
NvU8 CoverageToColorRTIndex;
NvU32 reserved[16];
</code></pre><p>There&rsquo;s a few other goodies in there (like conservative rasterization, and Post-Z coverage!), but the members that we&rsquo;re concerned with are <strong>ProgrammableSamplePositionsEnable</strong>, <strong>InterleavedSamplingEnable</strong>, <strong>SampleCount</strong>, <strong>SamplePositionsX</strong>, and <strong>SamplePositionsY</strong>. <strong>ProgrammableSamplePositionsEnable</strong> is self-explanatory: it enables the functionality. <strong>SampleCount</strong> is also pretty obvious: it&rsquo;s the MSAA sample count that we&rsquo;re using for rendering. <strong>SamplePositionsX</strong> and <strong>SamplePositionsY</strong> are pretty clear in terms of what they&rsquo;re used for: they&rsquo;re for specifying the X and Y coordinates of our MSAA sample points. What&rsquo;s not clear at all is how the API interprets those values. My initial guess was that they should contain 8-bit fixed point numbers where (0,0) is the top left of the pixel, and (255,255) is the bottom right. This was close, but not quite: they&rsquo;re actually 4-bit fixed-point values that correspond to points in the D3D Sample Coordinate System. If you&rsquo;re not familiar with this particular coordinate system (and you&rsquo;re probably not), there&rsquo;s a nice diagram in the documentation for the <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/Ff476218(v=VS.85).aspx">D3D11_STANDARD_MULTISAMPLE_QUALITY_LEVELS enumeration</a>[2]<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>:</p>
<p><a href="/images/converted/programmable-sample-points/d3d-sample-coordinate-system.png"><img src="/images/converted/programmable-sample-points/d3d-sample-coordinate-system.png" alt="D3D Sample Coordinate System"></a></p>
<p>There&rsquo;s also a bit more information in the documentation for <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ff471396(v=vs.85).aspx">EvaluateAttributeSnapped</a>:</p>
<pre tabindex="0"><code>Only the least significant 4 bits of the first two components (U, V) of the pixel offset are used.
The conversion from the 4-bit fixed point to float is as follows (MSB...LSB),
where the MSB is both a part of the fraction and determines the sign:

1000 = -0.5f (-8 / 16)
1001 = -0.4375f (-7 / 16)
1010 = -0.375f (-6 / 16)
1011 = -0.3125f (-5 / 16)
1100 = -0.25f (-4 / 16)
1101 = -0.1875f (-3 / 16)
1110 = -0.125f (-2 / 16)
1111 = -0.0625f (-1 / 16)
0000 = 0.0f ( 0 / 16)
0001 = 0.0625f ( 1 / 16)
0010 = 0.125f ( 2 / 16)
0011 = 0.1875f ( 3 / 16)
0100 = 0.25f ( 4 / 16)
0101 = 0.3125f ( 5 / 16)
0110 = 0.375f ( 6 / 16)
0111 = 0.4375f ( 7 / 16)
</code></pre><p>So basically we have 16 buckets work with in X and Y, with the ability to have sample points sit on the top or left edges, but not on the bottom or right edges. Now as for the NVAPI extension, it uses this same coordinate system, except that all values or positive. This means that there is no sign bit, as in the values passed to EvaluateAttributeSnapped. Instead you can picture the coordinate system as ranging from 0 to 15, with 8 (0.5) corresponding to the center of the pixel:</p>
<p><a href="/images/converted/programmable-sample-points/nvapi-sample-grid1.png"><img src="/images/converted/programmable-sample-points/nvapi-sample-grid1_resized_620.png" alt="NVAPI Sample Grid"></a></p>
<p>Personally I like this coordinate system better, since having the pixel center at 0.5 is consistent with the coordinate system used for pixel shader positions and UV coordinates. It also means that going from a [0, 1] float to the fixed point representation is pretty simple:</p>
<pre tabindex="0"><code>rsDesc.SamplePositionsX[i] = uint8(Clamp(SamplePositions1x[i].x * 16.0f, 0.0f, 15.0f));
</code></pre><p>Now, what about that <strong>InterleavedSamplingEnable</strong> member that&rsquo;s also part of the struct? This is a somewhat poorly-named parameter that controls whether you&rsquo;re specifying the same sample positions for all 4 pixels in a 2x2 quad, or whether you&rsquo;re specifying separate sample positions for each quad pixel. The API works such that the first N points correspond to the top left pixel, the next N correspond to the top right pixel, then the bottom left, followed by the bottom right. This means that for 2xMSAA you need to specify 8 coordinates, and for 4xMSAA you need  to specify 16 coordinates. For 8xMSAA we would need to specify 32 coordinates, but unfortunately NVAPI only lets us specify 16 values. In this case, the 16 values correspond to the sample points in the top left and bottom left pixels of the quad.</p>
<h2 id="case-study-low-resolution-rendering">Case Study: Low-Resolution Rendering</h2>
<p>So now that we have programmable sample points, what exactly should we do with them? MFAA-style temporal jittering is a pretty obvious use case, but surely there&rsquo;s other ways to make use of this functionality. One idea I&rsquo;ve been kicking around has been to use MSAA as way to implement half-resolution rendering. Rendering particles at half resolution is really common in games, since blending many layers of particles can result in high costs from pixel shading and framebuffer operations. It can be an easy win in many cases, as long as the content you&rsquo;re rendering won&rsquo;t suffer too much from being shaded at half-rate and upsampled to full resolution. The main issue of course is that you can&rsquo;t just naively upsample the result, since the half-resolution rasterization and depth testing will cause noticeably incorrect results around areas where the transparent geometry was occluded by opaque geometry. Here&rsquo;s a comparison between half-resolution particle rendering with a naive bilinear upscale (left), and full-resolution rendering (right):</p>
<p><a href="/images/converted/programmable-sample-points/lowres_naive_comparison.png"><img src="/images/converted/programmable-sample-points/lowres_naive_comparison_resized_620.png" alt="LowRes_Naive_Comparison"></a></p>
<p>Notice how in some areas the low-resolution buffer has false occlusion, causing no particles to be visible for those pixels, while in other places the particles bleed onto the foreground geometry even though they should have been occluded. Perhaps the most obvious and common way to reduce these artifacts is to use some variation of a bilateral filter during the upscale, where the filtering weights are adjusted for pixels based on the difference between the low-resolution depth buffer and the full-resolution depth buffer. The idea behind doing this is that you&rsquo;re going to have under or over-occlusion artifacts in places where the low-resolution depth is a poor representation of your full-resolution depth, and so you should reject low-resolution samples where the depth values are divergent. For The Order: 1886, we used a variant of this approach called nearest-depth upsampling, which was originally presented in an Nvidia SDK sample called <a href="http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/OpacityMappingSDKWhitePaper.pdf">OpacityMapping</a>. This particular algorithm requires that you sample 4 low-resolution depth samples from a 2x2 bilinear footprint, and compare each one with the full-resolution depth value. If the depth values are all within a user-defined threshold, the low-resolution color target is bilinearly upsampled with no special weighting. However if any of the sample comparisons are greater than the treshold value, then the algorithm falls back to using only the low-resolution sample where the depth value was closest to the full-resolution depth value (hence the name &ldquo;nearest-depth&rdquo;). Overall the technique works pretty well, and in most cases it can avoid usual occlusion artifacts that are typical of low-resolution rendering. It can even be used in conjunction with MSAA, as long as you&rsquo;re willing to run the filter for every subsample in the depth buffer. However it does have a few issues that we ran into over the course of development, and most of them stem from using depth buffer comparisons as a heuristic for occlusion changes in the transparent rendering. Before I list them, here&rsquo;s an image that you can use to follow along (click for full resolution):</p>
<p><a href="/images/converted/programmable-sample-points/nearestdepth_artifacts.png"><img src="/images/converted/programmable-sample-points/nearestdepth_artifacts_resized_620.png" alt="NearestDepth_Artifacts"></a></p>
<p>The first issue, illustrated in the top left corner, occurs when there are rapid changes in the depth buffer but no actual changes in the particle occlusion. In that image all of the particles are actually in front of the opaque geometry, and yet the nearest-depth algorithm still switches to point sampling due to depth buffer changes. It&rsquo;s very noticeable in the pixels that overlap with the blue wall to the left. In this area there&rsquo;s not even a discontinuity in the depth buffer, it&rsquo;s just that the depth is changing quickly due to the oblique angle at which the wall is being viewed. The second issue, shown in the top right corner, is that the depth buffer contains no information about the actual edges of the triangles that were rasterized into the low-resolution render target. In this image I turned off the particle texture and disabled billboarding the quad towards the camera, and as a result you can clearly see the a blurry, half-resolution stair-step pattern at the quad edges. The third issue, shown in the bottom left, occurs when alpha-to-coverage is used to get sub-pixel transparency. In this image 2xMSAA is used, and the grid mesh (the grey parallel lines) is only being rendered to the first subsample, which was done by outputting a custom coverage mask from the pixel shader. This interacts poorly with the nearest-depth upsampling, since 1 of the 2 high-res samples won&rsquo;t have the depth from the grid mesh. This causes blocky artifacts to occur for the second subsample. Finally, in the bottom right we have what was the most noticable issue for me personally: if the geometry is too small to be captured by the low-resolution depth buffer, then the upsample just fails completely. In practice this manifests as entire portions of the thin mesh &ldquo;disappearing&rdquo;, since they don&rsquo;t correctly occlude the low-resolution transparents.</p>
<h2 id="ramble-on">Ramble On</h2>
<p>By now I hope you get the point: bilateral upsampling has issues for low resolution rendering. So how can we use MSAA and custom sampling points to do a better job? If you think about it, MSAA is actually perfect for what we want to accomplish: it causes the rasterizer and the depth test to run at a higher resolution than the render target(s), but the pixel shader still executes once per pixel. So if we use a half-resolution render target combined with 4x MSAA, then we should get exactly what we want: full-resolution coverage and depth testing, but half-resolution shading! This actually isn&rsquo;t a new idea: Capcom tried a variant of this approach for the original Lost Planet on Xbox 360. For their implementation, which is outlined <a href="http://game.watch.impress.co.jp/docs/20070131/3dlp.htm">here</a> in Japanese (English translation <a href="https://forum.beyond3d.com/threads/capcoms-framework-game-engine.31090/page-6#post-815108">here</a>), they were actually able to just alias their full-resolution render targets as a half-resolution 4xMSAA render targets while they were still in eDRAM. This worked for them, although at the time it was specific to the Xbox 360 hardware and also had no filtering applied to the low-resolution result (due to there being no explicit upscaling step). However we now have much more flexible GPU&rsquo;s that give us generalized access to MSAA data, and my hunch was that we could improve upon this by using a more traditional &ldquo;render in low-res and then upscale&rdquo; approach. So I went ahead and implemented a sample program that demonstrates the technique, and so far it seems to actually work! I did have to work my way past a few issues, and so I&rsquo;d like to go over the details in this article.</p>
<p>Here&rsquo;s how the rendering steps were laid out every frame:</p>
<ol>
<li>Render Shadows</li>
<li>Render Opaques (full res)</li>
<li>Run pixel shader to convert from full-res depth to 4xMSAA depth</li>
<li>Render particles at low-res with 4xMSAA, using ordered grid sample points</li>
<li>Resolve low-res MSAA render target</li>
<li>Upscale and composite low-res render target onto the full-res render target</li>
</ol>
<p>The first two steps are completely normal, and don&rsquo;t need explaining. For step #3, I used a full-screen pixel shader that only output SV_Depth, and executed at per-sample frequency. Its job was to look at the current subsample being shaded (provided by SV_SampleIndex), and use that to figure out which full-res depth pixel to sample from. The idea here is that 4 full-res depth samples need to be crammed into the 4 subsamples of the low-resolution depth buffer, so that all of the full-resolution depth values are fully represented in the low-res depth buffer:</p>
<p><a href="/images/converted/programmable-sample-points/fullres_halfres_samplepoints.png"><img src="/images/converted/programmable-sample-points/fullres_halfres_samplepoints_resized_620.png" alt="FullRes_HalfRes_SamplePoints"></a></p>
<p>If you look at the image, you&rsquo;ll notice that the MSAA sample positions in the low-resolution render target are spaced out in a uniform grid, instead of the typical rotated grid pattern. This is where programmable sample points come in: by manually specifying the sample points, we can make sure that  the low-resolution MSAA samples correspond to the exact same location as they would be in the full-resolution render target. This is important if you want your low-resolution triangle edges to look the same as if they would if rasterized at full resolution, and also ensures correct depth testing at intersections with opaque geometry.</p>
<p>For step #4, we now render our transparent geometry to the half-resolution 4xMSAA render target. This is fairly straightforward, and in my implementation I just render a bunch of lit, billboarded particles with alpha blending. This is also where I apply the NVAPI rasterizer state containing the custom sample points, since this step is where rasterization and depth testing occurs. In my sample app you can toggle the programmable sample points on and off to see the effect (or rather, you can if you&rsquo;re running on a GPU that supports it), although you probably wouldn&rsquo;t notice anything with the default rendering settings. The issues are most noticable with albedo maps and billboarding disabled, which lets you see the triangle edges very clearly. If you look at pair of the images below, the image to left shows what happens when rasterizing with the 4x rotated grid pattern. The image on the right shows what it looks like when using our custom sample points, which are in a sub-pixel ordered grid pattern.</p>
<p><a href="/images/converted/programmable-sample-points/edgesamplingcomparison.png"><img src="/images/converted/programmable-sample-points/edgesamplingcomparison_resized_620.png" alt="EdgeSamplingComparison"></a></p>
<p>For step #5, I use a custom resolve shader instead of the standard hardware resolve. I did this so that I can look at the 4 subsamples, and find cases where the subsamples are not all the same. When this happens, this means that there was a sub-pixel edge during rasterization, that was either caused by a transparent triangle edge or by the depth test failing. For these pixels I output a special sentinel value of -FP16Max, so that the upscale/composite step has knowledge of the sub-pixel edge.</p>
<p>In the last step, I run another full-screen pixel shader that samples the low-resolution render target and blends it on top of the full-resolution render target. The important part of this step is choosing how exactly to filter when upsampling. If we just use plain bilinear filtering, the results will be smooth but the transparents will incorrectly bleed over onto occluding opaque pixels. If we instead use the MSAA render target and just load the single subsample that corresponds to the pixel being shaded, the results will look &ldquo;blocky&rdquo; due to the point sampling. So we must choose between filtering and point sampling, just as we did with nearest-depth upsampling. In the resolve step we already detected sub-pixel edges, and so we can already use that to determine when to switch to point sampling. This gets us most of the way there, but not all of the way. For bilinear filtering we&rsquo;re going to sample a 2x2 neighborhood from the low-resolution texture, but our resolve step only detected edges within a single pixel. This means that if there&rsquo;s an edge between two adjacent low-resolution pixels, then we&rsquo;ll get a bleeding artifact if we sample across that edge. To show you what I mean, here&rsquo;s an image showing low-resolution MSAA on the left, and full-resolution rendering on the right:</p>
<p><a href="/images/converted/programmable-sample-points/msaa_edgebleed.png"><img src="/images/converted/programmable-sample-points/msaa_edgebleed_resized_620.png" alt="MSAA_EdgeBleed"></a></p>
<p>To detect this particular case, I decided to use an alpha comparison as a heuristic for changes in occlusion:</p>
<pre tabindex="0"><code>float4 msaaResult = LowResTextureMSAA.Load(lowResPos, lowResSampleIdx);
float4 filteredResult = LowResTexture.SampleLevel(LinearSampler, UV, 0.0f);
float4 result = filteredResult;
if(msaaResult.a - filteredResult.a &gt; CompositeSubPixelThreshold)
    result = msaaResult;
</code></pre><p>This worked out pretty nicely, since it essentially let me roll the two tests into one: if the resolve sub-pixel test failed then it would output -FP16Max, which automatically results in a very large difference during the edge test in the composite step. Here&rsquo;s an image where the left side shows low-resolution MSAA rendering with the improved edge detection, and the right side shows the &ldquo;edge&rdquo; pixels by coloring them bright green:</p>
<p><a href="/images/converted/programmable-sample-points/msaaedges.png"><img src="/images/converted/programmable-sample-points/msaaedges_resized_620.png" alt="MSAAEdges"></a></p>
<p>Before I show some results and comparisons, I&rsquo;d like to touch on integration with MSAA for full-resolution rendering. With nearest-depth upsampling, MSAA is typically handled by running the upsample algorithm for every MSAA sub-sample. This is pretty trivial to implement by running your pixel shader at per-sample frequency, which automatically happens if you use SV_SampleIndex as an input to your pixel shader. This works well for most cases, although it still doesn&rsquo;t help situations where sub-pixel features are completely missing in the low-resolution depth buffer. For the low-resolution MSAA technique that I&rsquo;m proposing it&rsquo;s also fairly trivial to integrate with 2x MSAA: you simply need to use 8xMSAA for your low-resolution rendering, and adjust your sample points so that you have 4 sets of 2x rotated grid patterns. Then during the upsample you can execute the pixel shader at per-sample frequency, and run the alpha comparison for each full-resolution subsample. Unfortunately we can&rsquo;t handle 4x so easily, since 16xMSAA is not available on any hardware that I know of. I haven&rsquo;t given it a lot of thought yet, but I think it should be doable with some quality loss by performing a bit of bilateral upsampling during the compositing step. On consoles it should also be possible to use EQAA with 16x coverage samples to get a little more information during the upsampling.</p>
<h2 id="results">Results</h2>
<p>So now that we know that our MSAA trick can work for low-resolution rendering, how does it stack up against against techniques? In my sample app I also implemented full-resolution rendering as well as half-resolution with nearest-depth upsampling, and so we&rsquo;ll use those as a basis for comparison. Full resolution is useful as a &ldquo;ground truth&rdquo; for quality comparisons, while nearest-depth upsampling is a good baseline for performance. So without further adieu, here are some links to comparison images:</p>
<h3 id="normal">Normal</h3>
<p><a href="/images/converted/programmable-sample-points/standard_fullres.png">Full Res</a></p>
<p><a href="/images/converted/programmable-sample-points/standard_nearestdepth.png">Low Res - Nearest Depth</a></p>
<p><a href="/images/converted/programmable-sample-points/standard_msaa.png">Low Res - MSAA</a></p>
<h3 id="sub-pixel-geo">Sub-Pixel Geo</h3>
<p><a href="/images/converted/programmable-sample-points/subpixelgeo_fullres.png">Full Res</a></p>
<p><a href="/images/converted/programmable-sample-points/subpixelgeo_nearestdepth.png">Low Res - Nearest Depth</a></p>
<p><a href="/images/converted/programmable-sample-points/subpixelgeo_msaa.png">Low Res - MSAA</a></p>
<h3 id="sub-pixel-geo--2x-msaa">Sub-Pixel Geo – 2x MSAA</h3>
<p><a href="/images/converted/programmable-sample-points/subpixelgeo_2x_fullres.png">Full Res</a></p>
<p><a href="/images/converted/programmable-sample-points/subpixelgeo_2x_nearestdepth.png">Low Res - Nearest Depth</a></p>
<p><a href="/images/converted/programmable-sample-points/subpixelgeo_2x_msaa.png">Low Res - MSAA</a></p>
<h3 id="triangle-edges">Triangle Edges</h3>
<p><a href="/images/converted/programmable-sample-points/triangleedges_fullres.png">Full Res</a></p>
<p><a href="/images/converted/programmable-sample-points/triangleedges_nearestdepth.png">Low Res - Nearest Depth</a></p>
<p><a href="/images/converted/programmable-sample-points/triangleedges_msaa.png">Low Res - MSAA</a></p>
<h3 id="high-depth-gradient">High Depth Gradient</h3>
<p><a href="/images/converted/programmable-sample-points/depthgradient_fullres.png">Full Res</a></p>
<p><a href="/images/converted/programmable-sample-points/depthgradient_nearestdepth.png">Low Res - Nearest Depth</a></p>
<p><a href="/images/converted/programmable-sample-points/depthgradient_msaa.png">Low Res - MSAA</a></p>
<h3 id="alpha-to-coverage">Alpha To Coverage</h3>
<p><a href="/images/converted/programmable-sample-points/a2c_fullres.png">Full Res</a></p>
<p><a href="/images/converted/programmable-sample-points/a2c_nearestdepth.png">Low Res - Nearest Depth</a></p>
<p><a href="/images/converted/programmable-sample-points/a2c_msaa.png">Low Res - MSAA</a></p>
<p>In my opinion, the quality is a consistent improvement over standard low-res rendering with a nearest-depth upscale. The low-resolution MSAA technique holds up in all of the failure cases that I mentioned earlier, and is still capable of providing filtered results in areas where the particles aren&rsquo;t being occluded (unlike the Lost Planet approach, which essentially upsampled with point filtering).</p>
<p>To evaluate performance, I gathered some GPU timings for each rendering step on my GeForce GTX 970. The GPU time was measured using timestamp queries, and I recorded the maximum time interval from the past 64 frames. These were all gathered while rendering at 1920x1080 resolution (which means 960x540 for half-resolution rendering) with 16 * 1024 particles, and no MSAA:</p>
<table>
<thead>
<tr>
<th>Render Mode</th>
<th>Depth Downscale</th>
<th>Particle Rendering</th>
<th>Resolve</th>
<th>Composite</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr>
<td>Full Res</td>
<td>N/A</td>
<td>4.59ms</td>
<td>N/A</td>
<td>N/A</td>
<td>4.59ms</td>
</tr>
<tr>
<td>Half Res – Nearest Depth</td>
<td>0.04ms</td>
<td>1.37ms</td>
<td>N/A</td>
<td>0.31ms</td>
<td>1.71ms</td>
</tr>
<tr>
<td>Half Res - MSAA</td>
<td>0.13ms</td>
<td>1.48ms</td>
<td>0.23ms</td>
<td>0.08ms</td>
<td>1.92ms</td>
</tr>
</tbody>
</table>
<p>On my hardware, the low-resolution MSAA technique holds up fairly well against the baseline of half-res with nearest depth upsampling. The resolve adds a bit of time, although that particular timestamp was rather erratic and so I&rsquo;m not 100% certain of its accuracy. One nice thing is that the composite step is now cheaper, since the shader is quite a bit simpler. In the interest of making sure that all of my cards are on the table, one thing that I should mention is that the particles in this sample don&rsquo;t sample the depth buffer in the pixel shader. This is common for implementing soft particles as well as for computing volumetric effects by marching along the view ray. In these cases the pixel shader performance would likely suffer if it had to point sample an MSAA depth buffer, and so it would probably make sense to prepare a 1x depth buffer during the downscale phase. This would add some additional cost to the low-resolution MSAA technique, and so I should probably consider adding it to the sample at some point.</p>
<p>These numbers were gathered with 2xMSAA used for full-resolution rendering:</p>
<table>
<thead>
<tr>
<th>Render Mode</th>
<th>Depth Downscale</th>
<th>Particle Rendering</th>
<th>Resolve</th>
<th>Composite</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr>
<td>Full Res</td>
<td>N/A</td>
<td>4.69ms</td>
<td>N/A</td>
<td>N/A</td>
<td>4.69ms</td>
</tr>
<tr>
<td>Half Res – Nearest Depth</td>
<td>0.06ms</td>
<td>1.40ms</td>
<td>N/A</td>
<td>0.38ms</td>
<td>1.81ms</td>
</tr>
<tr>
<td>Half Res - MSAA</td>
<td>0.25ms</td>
<td>2.11ms</td>
<td>0.24ms</td>
<td>0.17ms</td>
<td>1.74ms</td>
</tr>
</tbody>
</table>
<p>Unfortunately the low-resolution MSAA technique doesn&rsquo;t hold up quite as well in this case. The particle rendering gets quite a bit more expensive, and the cost of downscale as well as the composite both increase. It does seem odd for the rendering cost to increase so much, and I&rsquo;m not sure that I can properly explain it. Perhaps there is a performance cliff when going from 4x to 8x MSAA on my particular GPU, or maybe there&rsquo;s a bug in my implementation.</p>
<h2 id="bring-it-on-home">Bring It On Home</h2>
<p>So what conclusions can we draw from all of this? At the very least, I would say that programmable sample points can certainly be useful, and that low-resolution MSAA is a potentially viable use case for the functionality. In hindsight it seems that my particular implementation isn&rsquo;t necessarily the best way to show off the improvement that you get from using programmable sample points, since the alpha-blended particles don&rsquo;t have any noticeable triangle edges by default. However it&rsquo;s definitely relevant if you want to consider more general transparent geometry, or perhaps even rendering opaque geometry at half resolution. Either way, I would really like to see broader support for the functionality being exposed in PC API&rsquo;s. Unfortunately it&rsquo;s not part of any Direct3D 12 feature set, so we&rsquo;re currently stuck with Nvidia&rsquo;s D3D11 or OpenGL extensions. It would be really great to see it get added to a future D3D12 revision, and have it be supported across Nvidia, Intel, and AMD hardware. Until that happens I suspect it will mostly get used by console developers.</p>
<p><em>Update: <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/mt492660(v=vs.85).aspx">programmable sample point functionality was added to D3D12!</a></em></p>
<p>If you would like to check out the code for yourself, I&rsquo;ve posted it all on <a href="https://github.com/TheRealMJP/LowResRendering">GitHub</a>. If you&rsquo;d just like to download and run the pre-compiled executable, I&rsquo;ve also made a <a href="https://github.com/TheRealMJP/LowResRendering/releases">binary release</a> available for download. If you look through the build instructions, you&rsquo;ll notice that I didn&rsquo;t include the NVAPI headers and libs in the repository. I made this choice because of the ominous-sounding license terms that are included in the header files, as well as the agreement that you need to accept before downloading it. This means that the code won&rsquo;t compile by default unless you download NVAPI yourself, and place it in the appropriate directory. However if you just want to build it right away, there&rsquo;s a preprocessor macro called &ldquo;UseNVAPI_&rdquo; at the top of LowResRendering.cpp that you can set to 0. If you do that then it won&rsquo;t link against NVAPI, and the app will build without it. Of course if you do this, then you won&rsquo;t be able to use the programmable sample point feature in the sample app. The feature will also be disabled if you aren&rsquo;t running on a Maxwell 2.0 (or newer) Nvidia GPU, although everything else should work correctly on any hardware that supports FEATURE_LEVEL_11_0. Unfortunately I don&rsquo;t have any non-Nvidia hardware to test on, so please let me know if you run into any issues.</p>
<h2 id="references">References</h2>
<p>[1] <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/Ff476218(v=VS.85).aspx">D3D11_STANDARD_MULTISAMPLE_QUALITY_LEVELS enumeration</a> <br>
[2] <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ff471396(v=vs.85).aspx">MSDN documentation for EvaluateAttributeSnapped</a> <br>
[3] <a href="http://www.geforce.com/hardware/technology/mfaa/technology">Multi-Frame Samples Anti-Aliasing (MFAA)</a> <br>
[4] <a href="http://www.cs.ubc.ca/~heidrich/Papers/RW.01_2.pdf">Interleaved Sampling</a> <br>
[5] <a href="https://developer.nvidia.com/sites/default/files/akamai/opengl/specs/GL_NV_sample_locations.txt">GL_NV_sample_locations</a> <br>
[6] <a href="https://developer.nvidia.com/nvapi">NVAPI</a> <br>
[7] <a href="http://advances.realtimerendering.com/s2014/drobot/HRAA_notes_final.pdf">Hybrid Reconstruction Anti-Aliasing</a> <br>
[8] <a href="http://advances.realtimerendering.com/s2015/aaltonenhaar_siggraph2015_combined_final_footer_220dpi.pptx">GPU-Driven Rendering Pipelines</a> <br>
[9] <a href="http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/10/R6xx_3D_Registers.pdf">AMD R600 3D Registers</a> <br>
[10] <a href="http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/10/SI_3D_registers.pdf">AMD Southern Islands 3D Registers</a> <br>
[11] <a href="https://www.opengl.org/registry/specs/AMD/sample_positions.txt">AMD_sample_positions</a> <br>
[12] <a href="http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/OpacityMappingSDKWhitePaper.pdf">OpacityMapping White Paper</a> <br>
[13] <a href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch23.html">High-Speed, Off-Screen Particles (GPU Gems 3)</a> <br>
[14] <a href="http://game.watch.impress.co.jp/docs/20070131/3dlp.htm">Lost Planet Tech Overview</a> (<a href="https://forum.beyond3d.com/threads/capcoms-framework-game-engine.31090/page-6#post-815108">english translation</a>) <br>
[15] <a href="http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/ShopfMixedResolutionRendering.pdf">Mixed Resolution Rendering</a> <br>
[16] <a href="http://advances.realtimerendering.com/s2013/Tatarchuk-Destiny-SIGGRAPH2013.pdf">Destiny: From Mythic Science Fiction to Rendering in Real-Time</a> <br></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://bartwronski.wordpress.com" title="elirian@gmail.com">bartwronski</a> - <time datetime="2015-09-13 21:39:47">Sep 0, 2015</time></p>
<p>Thanks for the post, very interesting! Cool that you covered use case for particles (description of temporal sample varying regular AA was done by Michal Drobot in his HRAA presentation). Can you answer if you used this on The Order 1886 (you mentioned during your Siggraph talk some console specific, NDA details), or is it all NDA&rsquo;d? I have a question about mention of soft particles and proposed solution: &ldquo;In these cases the pixel shader performance would likely suffer if it had to point sample an MSAA depth buffer, and so it would probably make sense to prepare a 1x depth buffer during the downscale phase.&rdquo; - I don&rsquo;t really understand what is this 1x depth buffer - regular full res buffer? Average depth buffer (reintroducing aliasing)? In any case it seems to me that soft particles have to run PS per subsample to get proper transparency ratio, or otherwise will reintroduce half res edges, do I understand correctly? Thanks again for the write up! BTW. picture msaaedges.png is not clickable like others - this is not a problem, but seems like it wasn&rsquo;t intended.</p>
<hr />
####
[]( "") - <time datetime="2015-09-15 12:40:02">Sep 2, 2015</time>
<p>Why resolve at all? With 4xMSAA, you have 4 color samples per low-res pixel, corresponding to the full-res pixel centers. Why throw them away, only to reconstruct during composite? I must be missing something.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2015-09-15 21:56:54">Sep 2, 2015</time>
<p>Hi Bart! Good to hear from you! I didn&rsquo;t use any of these techniques for The Order. After the game shipped I did integrate my own form of subsample jittering, so if that continues to work out then we will probably use it for the next project. Regarding the soft particles, I was suggesting to just use a point sampled 1/2 res depth buffer. Initially I had thought that it would be possible okay to do this as long as the fade was restricted to pixels that were in front of the opaque depth buffer value, but I thought about it some more and I realized that it would that this would still result in artifacts. So at the moment I don&rsquo;t have a good solution for handling soft particles or volumetrics, which is a really big downside. I&rsquo;ll have to think about it some more, and perhaps I can come up with approach that doesn&rsquo;t degenerate to shading for each subsample. Thank you for pointing out the missing link, it should be fixed now! @Anonymous - I don&rsquo;t &ldquo;throw away&rdquo; the subsamples, I still use them during the composite. The resolve step is to allow for bilinear filtering during the composite. If you just used the subsamples without filtering, you get correct edges but the result will look very &ldquo;blocky&rdquo; due to the point sampling (basically you get the same result as Lost Planet). But if you resolve the texture, then you can use bilinear filtering (or some other filter kernel) during the upscale in order to get better-looking results. Technically you could skip the resolve, and during the composite you could filter loading all subsamples for each pixel in the 2x2 filter footprint. But this would require 16 texture samples instead of just 1.</p>
<hr />
####
[AGraphicsGuy](http://agraphicsguy.wordpress.com "jerrycao_1985@icloud.com") - <time datetime="2015-12-06 20:00:28">Dec 0, 2015</time>
<p>Great blog. Just a heads-up, you can actually add &lsquo;read more tag&rsquo; after the first couple of paragraphs so that only part of your blog will be visible in the main page, instead of the full article.</p>
<hr />
####
[Beware of SV_Coverage &#8211; Yosoygames](http://www.yosoygames.com.ar/wp/2017/02/beware-of-sv_coverage/ "") - <time datetime="2017-02-28 11:50:23">Feb 2, 2017</time>
<p>[…] been obvious). Neither GL spec, docs, MSDN docs and other readings warned me about these gotchas. MJP’s blogposts were very useful, but that’s it. And they weren’t very specific to […]</p>
<hr />
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Some of you might remember ATI&rsquo;s old &ldquo;<a href="http://www.tweakguides.com/ATICAT_7.html">Temporal AA</a>&rdquo; feature from their Catalyst Control Panel, which had a similar approach to MFAA in that it alternated sample patterns every frame. However unlike MFAA, it just relied on display persistance to combine samples from adjacent frames, instead of explicitly combining them in a shader.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>If you&rsquo;ve been working with D3D11 for a long time and you don&rsquo;t recognize these diagrams, you&rsquo;re not going crazy. The original version of the D3D11 docs were completely missing all of this information, which actually made the &ldquo;standard&rdquo; MSAA patterns somewhat useless. <a href="/images/converted/programmable-sample-points/d3d11-smq-old.png">This</a> is what the docs for D3D11_STANDARD_MULTISAMPLE_QUALITY_LEVELS look like in the old DirectX SDK documentation, and <a href="/images/converted/programmable-sample-points/d3d11-eas-old.png">this</a> is what EvaluateAttributeSnapped looked like.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
		</item>
		
		<item>
			<title>SIGGRAPH Follow-Up: 2015 Edition</title>
			<link>https://therealmjp.github.io/posts/siggraph-2015/</link>
			<pubDate>Mon, 17 Aug 2015 05:17:57 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/siggraph-2015/</guid>
			<description>SIGGRAPH 2015 wrapped up just a few days ago, and it really was fantastic this year! There was tons of great content, and I got a chance to meet up with some of best graphics programmers in the industry. I wanted to thank anyone that came to my talk at Advances in Real-Time Rendering, as well as anyone who came to our talk at the Physically Based Shading course. It&amp;rsquo;s always awesome to see so many people interested in the latest rendering technology, and the other presenters really knocked it out of the park in both courses.</description>
			<content:encoded><![CDATA[<p>SIGGRAPH 2015 wrapped up just a few days ago, and it really was fantastic this year! There was tons of great content, and I got a chance to meet up with some of best graphics programmers in the industry. I wanted to thank anyone that came to my talk at <a href="http://advances.realtimerendering.com/s2015/index.html">Advances in Real-Time Rendering</a>, as well as anyone who came to our talk at the <a href="http://blog.selfshadow.com/publications/s2015-shading-course/">Physically Based Shading course</a>. It&rsquo;s always awesome to see so many people interested in the latest rendering technology, and the other presenters really knocked it out of the park in both courses. I&rsquo;d also like to thank Natalya Tatarchuk for organizing the Advances course year after year. It&rsquo;s really amazing when you look back on the 10 years worth of high-quality material that she&rsquo;s assembled, and this year in particular was full of some really inspiring presentations. And of course I should thank Stephen Hill and Stephen McAuley as well, who also do a phenomenal job of cultivating top-notch material from both games and film.</p>
<p>For my advances talk, you can find the the slides on <a href="http://readyatdawn.com/presentations/">the company website</a>. They should also be up on the Advances course site in the near future. If you haven&rsquo;t seen it yet, the talk is primarily focused on the antialiasing of The Order, with a section about shadows at the end. There&rsquo;s also some bonus slides about the decal system that I made for The Order, which uses deferred techniques to accumulate a few special-case decal types onto our forward-rendered geometry. For the antialiasing, the tech and techniques presented isn&rsquo;t really anything I&rsquo;d consider to be particularly novel or groundbreaking. However I wanted to give an overview of the problem space as well as our particular approach for handling aliasing, and I hope that came across in the presentation. One thing I really wanted to touch on more was that I firmly believe that we need to go much deeper if we really want to fix aliasing in games. Things like temporal AA and SMAA are fantastic in that that really do make things look a whole lot better, but they&rsquo;re still fundamentally limited in several ways. On the other hand, just brute forcing the problem by increasing sampling rates isn&rsquo;t really a scalable solution in the long term. In some cases we&rsquo;re also undersampling so badly that just having a 2x or 4x increase in our sampling rates isn&rsquo;t going to even come close to fixing the issue. What I&rsquo;d really like to see is more work into figuring out how to have <em>smarter</em> sampling patterns (no more screen-space uniform grids!), and also how to properly prefilter content so that we&rsquo;re not always undersampling. This was actually something brought up by Marco Salvi in his excellent AA talk that was a part of the <a href="http://openproblems.realtimerendering.com/">Open Problems in Real-Time Rendering course</a>, which I was very happy to see. It was also really inspiring to see Alex Evans describe how he strived for filterable scene representations in his talk from the Advances course.</p>
<p>In case you missed it, I uploaded a full <a href="https://github.com/TheRealMJP/MSAAFilter">antialiasing code sample</a> to GitHub to accompany the talk. The code is using my usual sample framework and coding style, which means you can grab it and build it with VS 2013 with no external dependencies. There&rsquo;s also pre-compiled bins in the releases section, in case you would just like the view the app or play around with the shaders. The sample is essentially a successor to the MSAAFilter sample that I <a href="../msaa-resolve-filters/">put out nearly 3 years ago</a>, which accompanied a blog post where I shared some of my research on using higher-order filtering with MSAA resolves. The AA work in The Order is in many ways the natural conclusion of that work, and the new sample reflects that. If you load up the sample, you&rsquo;ll notice that the default scene is a really terrible case for geometric and specular aliasing: it&rsquo;s a rather high-polygon mesh, with lighting from both a directional light as well as from the environment.  I like to evaluate flickering reduction by setting &ldquo;Model Rotation Speed&rdquo; to 1.0, which causes the scene to automatically rotate around its Y axis. The default settings are also fairly close to what we shipped with in The Order, although not exactly the same due to some PS4-specific tweaks. The demo also defaults to a 2x jitter pattern, which we didn&rsquo;t use in The Order. One possible avenue that I never really explored was to experiment with more variation in the MSAA subsample patterns. This is something that you can do on PS4 (as demonstrated in Michal Drobot&rsquo;s talk about <a href="http://advances.realtimerendering.com/s2014/index.html">HRAA in Far Cry 4</a>), and you can also do it on recent Nvidia hardware using their proprietary <a href="https://developer.nvidia.com/nvapi">NVAPI</a>. A really simple thing to try would be to implement <a href="http://www.cs.ubc.ca/~heidrich/Papers/RW.01_2.pdf">interleaved sampling</a>, although it could potentially make the resolve shader more expensive.</p>
<p>As for the <a href="http://blog.selfshadow.com/publications/s2015-shading-course/rad/s2015_pbs_rad_slides.pptx">talk</a> that Dave and I gave at <a href="http://blog.selfshadow.com/publications/s2015-shading-course/">Physically Based Shading</a>, I hope that the images spoke for themselves in terms of showing how much better things looked once we made the switch from SH/H-basis to Spherical Gaussians. It was a very late and risky change for the project, but fortunately it paid off for us by substantially improving the overall visual quality. The nice thing is it&rsquo;s pretty easy to understand why it looks better once we switched. Previously, we partitioned lighting response into diffuse and specular. We then took advantage of the response characteristics to store the input for both responses in two separate ways: for diffuse, we used high spatial resolution but with low angular resolution (SH lightmaps), while for specular we used low spatial resolution but with high angular resolution (sparse cubemap probes). By splitting specular into both low-frequency (high roughness) and high-frequency (low roughness) categories, we were able to use spatially dense sample points for a much broader range of surfaces. These surfaces with rougher specular then benefit from improved visibility/occlusion, which is usually the biggest issue with sparse cubemap probes. This obviously isn&rsquo;t a new idea, in fact Halo 3 was <a href="http://halo.bungie.net/images/Inside/publications/presentations/lighting_material.zip">doing similar things</a> all the way back in 2008! The main difference of course is that we were able to use SG instead of SH, which gave us more flexibility in how we represented per-texel incoming radiance.</p>
<p>SGs can be a really useful tool for all kinds of things in graphics, and I think it would be great if we all added them to our toolbox. To aid with that, Dave Neubelt and Brian Karis are planning on putting out a helpful paper that can hopefully be to SGs what <a href="http://www.ppsloan.org/publications/StupidSH36.pdf">Stupid SH Tricks</a> was to spherical harmonics. Dave and I also have been working on a code sample to release, which lets you switch between various methods for pre-computing both diffuse and specular lighting as well as a path-traced ground truth render. I&rsquo;m hoping to finish this soon, since I&rsquo;m sure it would be very helpful to have working code examples for the various SG operations.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22fangsunjian2@gmail.com%22">fangsunjian</a> - <time datetime="2015-10-25 16:25:53">Oct 0, 2015</time></p>
<p>Thanks, I will check it.</p>
<hr />
####
[Bonk]( "buttwipinglord@yahoo.com") - <time datetime="2015-08-27 03:27:06">Aug 4, 2015</time>
<p>Temporal AA methods I think are the wrong direction for AA to be heading in. There are just too many problems that they cause for them to be worthwhile. Often they cause too many temporal artifacts such as: Double images, blurring in motion rather than resolving detail, smearing, image wobble,etc. None of those problems can be inherently solved because of how the technique works IMO. (There are use cases where there are almost no noticeable draw backs to the user though, Temporal filtering of undersampled or upsampled buffers. Eg PP effects, particle effects,etc) Because of pandering to weak hardware and being as fast as possible it limits the quality of any given technique. (And isn&rsquo;t very forward thinking for the future when GPUs will be faster. At least on PC) Some of them seem completely incapable of actually solving many temporal issues and ironically have less issues with no motion than with motion. (UE4 Temporal AA for example) Solving some aliasing at the core level (Mip-maps for textures, CLEAN/LEAN/Toksvig for specular,etc) is extremely helpful but can only do so much on their own and still leave aliasing behind. The remaining information still needs to be resolved in a way that is stable. And that ultimately is always going to come back to coming at a high cost. Nvidia&rsquo;s own SGSSAA is the best example of this. When there aren&rsquo;t other things interfering (Considering it is never natively implemented in game and has to be forced from outside with compatibility bits that can only cover so much general use cases), it not only is temporally stable. It is actually resolving detail close to the ground truth, statically and temporally without drawbacks. (Also considering it&rsquo;s stuck using the fixed box function for reconstruction as well). In the cases where other things in the individual game are causing problems, creating a hybrid with standard OGSSAA (Preferrably not using a Box function) is unparalleled. Texture aliasing - Check Moire - Check Shader aliasing - Check Temporal Aliasing (flickering, crawling,etc) - Check Specular Aliasing - Check Geometry Aliasing - Check <a href="http://screenshotcomparison.com/comparison.php?id=89831">http://screenshotcomparison.com/comparison.php?id=89831</a> <a href="http://screenshotcomparison.com/comparison.php?id=66524">http://screenshotcomparison.com/comparison.php?id=66524</a> <a href="http://screenshotcomparison.com/comparison.php?id=96519">http://screenshotcomparison.com/comparison.php?id=96519</a> <a href="http://screenshotcomparison.com/comparison.php?id=105669">http://screenshotcomparison.com/comparison.php?id=105669</a> <a href="http://screenshotcomparison.com/comparison.php?id=10426">http://screenshotcomparison.com/comparison.php?id=10426</a> <a href="http://screenshotcomparison.com/comparison.php?id=133931">http://screenshotcomparison.com/comparison.php?id=133931</a> <a href="http://screenshotcomparison.com/comparison.php?id=104965">http://screenshotcomparison.com/comparison.php?id=104965</a> <a href="http://screenshotcomparison.com/comparison.php?id=100739">http://screenshotcomparison.com/comparison.php?id=100739</a> <a href="http://screenshotcomparison.com/comparison.php?id=118994">http://screenshotcomparison.com/comparison.php?id=118994</a> <a href="http://screenshotcomparison.com/comparison.php?id=95619">http://screenshotcomparison.com/comparison.php?id=95619</a> <a href="http://screenshotcomparison.com/comparison.php?id=135762">http://screenshotcomparison.com/comparison.php?id=135762</a> <a href="http://screenshotcomparison.com/comparison.php?id=117804">http://screenshotcomparison.com/comparison.php?id=117804</a> <a href="http://screenshotcomparison.com/comparison.php?id=64880">http://screenshotcomparison.com/comparison.php?id=64880</a> There are some games out there that are even more problematic and require some noodling about. Ex Red Faction 3 <a href="http://screenshotcomparison.com/comparison.php?id=103127">http://screenshotcomparison.com/comparison.php?id=103127</a> <a href="http://screenshotcomparison.com/comparison.php?id=103126">http://screenshotcomparison.com/comparison.php?id=103126</a> (1x2 OGSSAA + 2xMSAA + 2xSGSSAA + FXAA at base rendering resolution before a final 2x2 OGSSAA step |This was running at ~20-30FPS at the time on a GTX 570) . When it comes to the consoles, they are just too inherently weak to be able to push HQAA and still be pushing the graphical boundaries in addition to vastly varying game design philosophies. That doesn&rsquo;t mean you can&rsquo;t get decent and or good quality. But it often comes with drawbacks of it&rsquo;s own. It just feels like developing any more demanding and vastly higher quality techniques (That can be utilized today on PC or tomorrow on future GPUs)don&rsquo;t matter to anyone because it&rsquo;s not being pushed out to the largest LCD userbase. (The consoles) Keep up the good work though, because while i&rsquo;m not terribly fond of TAA, your hybrid solution in The Order generally looked fantastic and far better than just about every game out there on consoles. Ever.</p>
<hr />
####
[Bonk]( "buttwipinglord@yahoo.com") - <time datetime="2015-08-27 03:38:30">Aug 4, 2015</time>
<p>After further testing as well. It seems the Temporal AA component is what is responsible for this problem in motion. Disabling it fixes the problem. No motion temporal aliasing on the edges of the object without Temporal AA. TAA disabled in motion <a href="http://u.cubeupload.com/MrBonk/372MSAAFilter2015082704.png">http://u.cubeupload.com/MrBonk/372MSAAFilter2015082704.png</a></p>
<hr />
####
[]( "jeremie.mangue@gmail.com") - <time datetime="2015-08-17 07:23:58">Aug 1, 2015</time>
<p>Nice presentation. I was looking into spherical radial basis before. I&rsquo;m looking forward to your paper.</p>
<hr />
####
[SIGGRAPH 2015のRendering The Alternate History of The Order: 1886セッションのアンチエイリアスの実装サンプル](http://www.shader.jp/?p=1588 "") - <time datetime="2015-08-17 07:54:40">Aug 1, 2015</time>
<p>[…] SIGGRAPH FOLLOW-UP: 2015 EDITION <a href="https://mynameismjp.wordpress.com/2015/08/16/siggraph-2015/">https://mynameismjp.wordpress.com/2015/08/16/siggraph-2015/</a> […]</p>
<hr />
####
[Bonk]( "buttwipinglord@yahoo.com") - <time datetime="2015-08-27 03:34:51">Aug 4, 2015</time>
<p>&ldquo;Some of them seem completely incapable of actually solving many temporal issues and ironically have less issues with no motion than with motion.&rdquo; I forgot to mention another thing about this as well, is that your own example suffers from this problem somewhat a bit. EXs Static <a href="http://u.cubeupload.com/MrBonk/aabMSAAFilter2015082704.png">http://u.cubeupload.com/MrBonk/aabMSAAFilter2015082704.png</a> (Fantastically smooth, very little aliasing) In motion <a href="http://u.cubeupload.com/MrBonk/MSAAFilter2015082704.png">http://u.cubeupload.com/MrBonk/MSAAFilter2015082704.png</a> (Technique starts to unravel as all edges of the object start to crawl and flicker)</p>
<hr />
####
[fsj]( "fangsunjian2@gmail.com") - <time datetime="2015-10-25 06:03:30">Oct 0, 2015</time>
<p>I wonder what brdf of cloth used in The Order 1886?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2015-10-25 16:10:07">Oct 0, 2015</time>
<p>We covered the cloth BRDF in the course notes from our SIGGRAPH 2013 presentation: <a href="http://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013">http://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013</a>_pbs_rad_notes.pdf</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Mitsuba Quick-Start Guide</title>
			<link>https://therealmjp.github.io/posts/mitsuba-quick-start-guide/</link>
			<pubDate>Sat, 04 Apr 2015 20:36:44 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/mitsuba-quick-start-guide/</guid>
			<description>Angelo Pesce&amp;rsquo;s recent blog post brought up a great point towards the end of the article: having a &amp;ldquo;ground-truth&amp;rdquo; for comparison can be extremely important for evaluating your real-time techniques. For approximations like pre-integrated environment maps it can help visualize what kind of effect your approximation errors will have on a final rendered image, and and in many other cases it can aid you in tracking down bugs in your implementation.</description>
			<content:encoded><![CDATA[<p>Angelo Pesce&rsquo;s <a href="http://c0de517e.blogspot.ca/2015/03/being-more-wrong-parallax-corrected.html">recent blog post</a> brought up a great point towards the end of the article: having a &ldquo;ground-truth&rdquo; for comparison can be extremely important for evaluating your real-time techniques. For approximations like pre-integrated environment maps it can help visualize what kind of effect your approximation errors will have on a final rendered image, and and in many other cases it can aid you in tracking down bugs in your implementation. On Twitter I advocated writing your own path tracer for such purposes, primarily because doing so can be an extremely educational experience. However not everyone has time to write their own path tracer, and then populate with all of the required functionality. And even if you do have the time, it&rsquo;s helpful to have a &ldquo;ground truth for your ground truth&rdquo;, so that you can make sure that you&rsquo;re not making any subtle mistakes (which is quite easy to do with a path tracer!). To help in both of these situations, it&rsquo;s really handy to have a battle-tested renderer at your disposal. For me, that renderer is <a href="https://www.mitsuba-renderer.org/">Mitsuba</a>.</p>
<p>Mitsuba is a free, open source (GPL), physically based renderer that implements a variety of material and volumetric scattering models, and also implements some of the latest and greatest integration techniques (bidirectional path tracing, photon mapping, metropolis light transport, etc.). Since it&rsquo;s primarily an academic research project, it doesn&rsquo;t have all of the functionality and user-friendliness that you might get out of a production renderer like Arnold. That said, it can certainly handle many  (if not all) of the cases that you would want to verify for real-time rendering, and any programmer shouldn&rsquo;t have too much trouble figuring out the interface and file formats once he or she spends some time reading the documentation. It also features a plugin system for integrating new functionality, which could potentially be useful if you wanted to try out a custom material model but still make use of Mitsuba&rsquo;s ray-tracing/sampling/integration framework.</p>
<p>To help get people up and running quickly, I&rsquo;ve come up with a &ldquo;quick-start&rdquo; guide that can show you the basics of setting up a simple scene and viewing it with the Mitsuba renderer. It&rsquo;s primarily aimed at fellow real-time graphics engineers who have never used Mitsuba before, so if you belong in that category then hopefully you&rsquo;ll find it helpful! The guide will walk you through how to import a scene from .obj format into Mitsuba&rsquo;s internal format, and then directly manipulate Mitsuba&rsquo;s XML format to modify the scene properties. Editing XML by hand is obviously not an experience that makes anyone jump for joy, but I think it&rsquo;s a decent way to familiarize yourself with their format. Once you&rsquo;re familiar with how Mitsuba works, you can always write your own exporter that converts from your own format.</p>
<h3 id="1-getting-mitsuba">1. Getting Mitsuba</h3>
<p>The latest version of Mitsuba is available on <a href="https://www.mitsuba-renderer.org/download.html">this page</a>. If you&rsquo;re running a 64-bit version of Windows like me, then you can go ahead and grab the 64-bit version which contains pre-compiled binaries. There are also Mac and Linux versions if either of those is your platform of choice, however I will be using the Windows version for this guide.</p>
<p>Once you&rsquo;ve downloaded the zip file, go ahead and extract it to a folder of your choice. Inside of the folder you should have mtsgui.exe, which is the simple GUI version of the renderer that we&rsquo;ll be using for this guide. There&rsquo;s also a command-line version called mitsuba.exe, should you ever have a need for that.</p>
<p>While you&rsquo;re on the Mitsuba website, I would also recommend downloading the <a href="https://www.mitsuba-renderer.org/docs.html">PDF documentation</a> into the same folder where you extracted Mitsuba. The docs contain the full specification for Mitsuba&rsquo;s XML file format, general usage information, and documentation for the plugin API.</p>
<h3 id="2-importing-a-simple-scene">2. Importing a Simple Scene</h3>
<p>Now that we have Mitsuba, we can get to work on importing a simple scene into Mitsuba&rsquo;s format so that we can render it. The GUI front-end is capable of importing scenes from both COLLADA (*.dae) and Wavefront OBJ (*.obj) file formats, and for this guide we&rsquo;re going to import a very simple scene from an OBJ file that was authored in Maya. If you&rsquo;d like to follow along on your own, then you can grab the &ldquo;TestScene.obj&rdquo; file from the zip file that I&rsquo;ve uploaded here: <a href="https://mynameismjp.files.wordpress.com/2015/04/testscene.zip">https://mynameismjp.files.wordpress.com/2015/04/testscene.zip</a>. Our scene looks like this in Maya:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/scene_maya.png"><img src="/images/converted/mitsuba-quick-start-guide/scene_maya.png" alt="Scene_Maya"></a></p>
<p>As you can see, it&rsquo;s a very simple scene composed of a few primitive shapes arranged in a box-like setup. To keep things really simple with the export/import process, all of the meshes have their default shader assigned to them.</p>
<p>To import the scene into Mitsuba, we can now run mtsgui.exe and select File-&gt;Import from the menu bar.  This will give you the following dialog:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/importer_dialog.png"><img src="/images/converted/mitsuba-quick-start-guide/importer_dialog.png" alt="Importer_Dialog"></a></p>
<p>Go ahead and click the top-left button to browse for the .obj file that you&rsquo;d like to import. Once you&rsquo;ve done this, it will automatically fill in paths for the target directory and target file that will contain the Mitsuba scene definition. Feel free to change those if you&rsquo;d like to create the files elsewhere. There&rsquo;s also an option that specifies whether you&rsquo;d like any material colors and textures as being in sRGB or linear color space.</p>
<p>Once you hit &ldquo;OK&rdquo; to import the scene, you should now see our scene being rendered in the viewport:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/initial_preview.png"><img src="/images/converted/mitsuba-quick-start-guide/initial_preview.png" alt="Initial_Preview"></a></p>
<p>What you&rsquo;re seeing right now is the <em>OpenGL realtime preview</em>. The preview uses the GPU to render your scene with VPL approximations for GI, so that it can give you a rough idea of what your scene will look like once it&rsquo;s actually rendered. Whenever you first open a scene you will get the preview mode, and you&rsquo;ll also revert back to the preview mode whenever you move the camera.</p>
<p>Speaking of the camera, it uses a basic &ldquo;arcball&rdquo; system that&rsquo;s pretty similar to what Maya uses. Hold the left mouse button and drag the pointer to rotate the camera around the focus point, hold the middle mouse button to pan the camera left/right/up/down, and hold the right mouse button to move the camera along its local Z axis (you can also use the mouse wheel for this).</p>
<h3 id="3-configuring-and-rendering">3. Configuring and Rendering</h3>
<p>Now that we have our scene imported, let&rsquo;s try doing an actual render. First, click the button in the toolbar with the gear icon. It should bring up the following dialog, which lets you configure your rendering settings:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/pt_settings.png"><img src="/images/converted/mitsuba-quick-start-guide/pt_settings.png" alt="PT_Settings"></a></p>
<p>That first setting specifies which integrator that you want to use for rendering. If you&rsquo;re not familiar with the terminology being used here, an&quot;integrator&quot; is basically the overall rendering technique used for computing how much light is reflected back towards the camera for every pixel. If you&rsquo;re not sure which technique to use, the path tracer is a good default choice. It makes use of unbiased monte carlo techniques to compute diffuse and specular reflectance from both direct and indirect light sources, which essentially means that if you increase the number of samples it will always converge on the &ldquo;correct&rdquo; result. The main downside is that it can generate noisy results for scenes where a majority of surfaces don&rsquo;t have direct visibility of emissive light sources, since the paths are always traced starting at the camera. The bidirectional path tracer aims to improve on this by also tracing additional paths starting from the light sources. The regular path tracer also won&rsquo;t handle volumetrics, and so you will need to switch to the volumetric path tracer if you every want to experiment with that.</p>
<p>For a path tracer, the primary quality setting is the &ldquo;Samples per pixel&rdquo; option. This dictates how many samples to take for every pixel in the output image, and so you can effectively think of it as the amount of supersampling. Increasing it will reduce aliasing from the primary rays, and also reduce the variance in the results of computing reflectance off of the surfaces. Using more samples will of course increase the rendering time as well, so use it carefully. The &ldquo;Sampler&rdquo; option dictates the strategy used for generating the random samples that are used for monte carlo integration, which can also have a pretty large effect on the resulting variance. I would suggest reading through <em>Physically Based Rendering</em> if you&rsquo;d like to learn more about the various strategies, but if you&rsquo;re not sure then the &ldquo;low discrepancy sampler&rdquo; is a good default choice. Another important option is the &ldquo;maximum depth&rdquo; setting, which essentially lets you limit the renderer to using a fixed number of bounces. Setting it to 1 only gives you emissive surfaces and lights (light -&gt; camera), setting it to 2 gives you emissive + direct lighting on all surfaces (light -&gt; surface -&gt; camera), setting it to 3 gives you emissive + direct lighting + 1 bounce of indirect lighting (light -&gt; surface -&gt; surface -&gt; camera), and so on. The default value of -1 essentially causes the renderer to keep picking paths until it hits a light source,  or the transmittance back to the camera is below a particular threshold.</p>
<p>Once you&rsquo;ve configured everything the way I did in the picture above, go and hit OK to close the dialog. After than, press the big green &ldquo;play&rdquo; button in the toolbar to start the renderer. Once it starts, you&rsquo;ll see an interactive view of the renderer completing the image one tile at a time. If you have a decent CPU it shouldn&rsquo;t take more than 10-15 seconds to finish, at which point you should see this:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/initial_pt.png"><img src="/images/converted/mitsuba-quick-start-guide/initial_pt.png" alt="Initial_PT"></a></p>
<p>Congratulations, you now have a path-traced rendering of the scene!</p>
<h3 id="4the-scene-file-format">4. The Scene File Format</h3>
<p>Now that we have a basic scene rendering, it&rsquo;s time to dig into the XML file for the scene and start customizing it. Go head and open up &ldquo;TestScene.xml&rdquo; in your favorite text editor, and have a look around. It should look like this:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/initial_xml.png"><img src="/images/converted/mitsuba-quick-start-guide/initial_xml.png" alt="Initial_XML"></a></p>
<p>If you scroll around a bit, you&rsquo;ll see declarations for various elements of the scene. Probably what you&rsquo;ll notice first is a bunch of &ldquo;shape&rdquo; declarations: these are the various meshes that make up the scene. Since we imported from .obj, Mitsuba automatically generated a binary file called &ldquo;TestScene.serialized&rdquo; from our .obj file containing the actual vertex and index data for our meshes, which is then referenced by the shapes. Mitsuba can also directly reference .obj or .ply files in a shape, which is convenient if you don&rsquo;t want to go through Mitsuba&rsquo;s import process. It also supports hair meshes, heightfields from an image file, and various primitive shapes (sphere, box, cylinder, rectangle, and disk). Note that shapes support transform properties as well as transform hierarchies, which you can use to position your meshes within the scene as you see fit. See section 8.1 of the documentation for a full description of all of the shape types, and their various properties.</p>
<p>For each shape,  you can see a &ldquo;bsdf&rdquo; property that specifies the BSDF type to use for shading the mesh. Currently all of the shapes are specifying that they should use the &ldquo;diffuse&rdquo; BSDF type, and that the BSDF should use default parameters. You might also notice there&rsquo;s a separate bsdf declaration towards the top of the file, with an ID of &ldquo;initialShadingGroup_material&rdquo;. This comes from the default shader that Maya applies to all meshes, which is also reflected in the .mtl file that was generated along with the .obj file. This BSDF is not actually being used by any of the shapes in the scene, since they all are currently specifying that they just want the default &ldquo;diffuse&rdquo; BSDF. In the next section I&rsquo;ll go over how we can create and modify materials, and then assign them to meshes.</p>
<p>If you scroll way down to the bottom, you&rsquo;ll see the camera and sensor properties, which looks like this:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/initial_camera_xml.png"><img src="/images/converted/mitsuba-quick-start-guide/initial_camera_xml.png" alt="Initial_Camera_XML"></a></p>
<p>You should immediately recognize some of your standard camera properties, such as the clip planes and FOV. Here you can also see an example of a transform property, which is using the &ldquo;lookAt&rdquo; method for specifying the transform. Mitsuba also supports specifying transforms as translation + rotation + scale, or directly specifying the transformation matrix. See section 6.1.6 of the documentation for more details.</p>
<p>If you decide to manually update any of the properties in the scene, you can tell the GUI to re-load the scene from disk by clicking the button with blue, circular arrow on it in the toolbar. Just be aware that if you save the file from the GUI app, it may overwrite some of your changes. So if you decide to set up a nice camera position in the XML file, make sure that you don&rsquo;t move the camera in the app and then save over it!</p>
<h3 id="5-specifying-materials">5. Specifying Materials</h3>
<p>Now let&rsquo;s assign some materials to our meshes, so that we can start making our scene look interesting. As we saw previously, any particular shape can specify which BSDF model it should use as well as various properties of that BSDF. Currently, all of our meshes are using the &ldquo;diffuse&rdquo; BSDF, which implements a simple Lambertian diffuse model. There are many BSDF types available in Mitsuba, which you can read about in section 8.2 of the documentation. To start off, we&rsquo;re going to use the &ldquo;roughplastic&rdquo; model for a few of our meshes. This model gives you a classic diffuse + specular combination, where the diffuse is Lambertian the specular can use one of several microfacet models. It&rsquo;s a good default choice for non-metals, and thus can work well for a wide variety of opaque materials. Let&rsquo;s go down to about line 36 of our scene file, and make the following changes:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/materialchanges_xml1.png"><img src="/images/converted/mitsuba-quick-start-guide/materialchanges_xml1.png" alt="MaterialChanges_XML"></a></p>
<p>As you can see, we&rsquo;ve added BSDF properties for 4 of our meshes. They&rsquo;re all configured to use the &ldquo;roughplastic&rdquo; BSDF with a GGX distribution, a roughness of 0.1, and an IOR of 1.49. Unfortunately Mitsuba does not support specifying the F0 reflectance value for specular, and so we must specify the interior and exterior IOR instead (exterior IOR defaults to &ldquo;air&rdquo;, and so we can leave it at its default value). You can also see that I specified diffuse reflectance values for each shape, with a different color for each. For this I used the &ldquo;srgb&rdquo; property, which specifies that the color is in sRGB color space. You can also use the &ldquo;rgb&rdquo; property to specify linear values, or the &ldquo;spectrum&rdquo; property for spectral rendering.</p>
<p>After making these changes, go ahead click the &ldquo;reload&rdquo; button in Mitsuba followed by the &ldquo;start&rdquo; button to re-render the image. We should now get the following result:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/material_pt.png"><img src="/images/converted/mitsuba-quick-start-guide/material_pt.png" alt="Material_PT"></a></p>
<p>Nice! Our results are noisier on the right side due to specular reflections from the sun, but we can now clearly see indirect specular in addition to indirect diffuse.</p>
<p>To simplify setting up materials and other properties, Mitsuba supports using references instead directly specifying shape properties. To see how that works, let&rsquo;s delete the &ldquo;initialShadingGroup_material&rdquo; BSDF declaration at line 11 and replace it with a new that that we will reference into the cylinder and torus meshes:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/materialchanges2_xml.png"><img src="/images/converted/mitsuba-quick-start-guide/materialchanges2_xml.png" alt="MaterialChanges2_XML"></a></p>
<p>If you look closely, you&rsquo;ll see that for this new material I&rsquo;m also using a texture for the diffuse reflectance. When setting the &ldquo;texture&rdquo; property to the &ldquo;bitmap&rdquo; type, you can tell Mitsuba to load an image file off disk. Note that Mitsuba also supports a few built-in procedural textures that you can use, such as checkerboard and a grid. See section 8.3 for more details.</p>
<p>After refreshing, our render should now look like this:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/material2_pt.png"><img src="/images/converted/mitsuba-quick-start-guide/material2_pt.png" alt="Material2_PT"></a></p>
<p>To finish up with materials, let&rsquo;s assign a more interesting material to the sphere in the back:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/materialchanges3_xml.png"><img src="/images/converted/mitsuba-quick-start-guide/materialchanges3_xml.png" alt="MaterialChanges3_XML"></a></p>
<p>If we now re-render with a sample count of 256 to reduce the variance, we get this result:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/material3_pt.png"><img src="/images/converted/mitsuba-quick-start-guide/material3_pt.png" alt="Material3_PT"></a></p>
<h3 id="6-adding-emitters">6. Adding Emitters</h3>
<p>Up until this point, we&rsquo;ve been using Mitsuba&rsquo;s default lighting environment for rendering. Mitsuba supports a variety of emitters that mostly fall into one of 3 categories: punctual lights, area lights, or environment emitters. Punctual lights are your typical point, spot, and directional lights that are considered to have an infinitesimally small area. Area lights are arbitrary meshes that uniformly emit light from their surface, and therefore must be used with a corresponding &ldquo;shape&rdquo; property. Environment emitters are infinitely distant sources that surround the entire scene, and can either use an HDR environment map, a procedural sun and sky model, or a constant value. For a full listing of all emitter types and their properties, consult section 8.8 of the documentation.</p>
<p>Now, let&rsquo;s try adding an area light to our scene. Like I mentioned earlier, an area light emitter needs to be parented to a &ldquo;shape&rdquo; property that determines the actual 3D representation of the light source. While this shape could be an arbitrary triangle mesh if you&rsquo;d like, it&rsquo;s a lot easier to just use Mitsuba&rsquo;s built-in primitive types instead. For our light source, we&rsquo;ll use the &ldquo;sphere&rdquo; shape type so that we get a spherical area light source:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/arealight_xml.png"><img src="/images/converted/mitsuba-quick-start-guide/arealight_xml.png" alt="AreaLight_XML"></a></p>
<p>After refreshing, our scene now looks like this:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/arealight_pt.png"><img src="/images/converted/mitsuba-quick-start-guide/arealight_pt.png" alt="AreaLight_PT"></a></p>
<p>Notice how the sky and sun are now gone, since we now have an emitter defined in the scene. To replace the sky, let&rsquo;s now try adding our own environment emitter that uses an environment map:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/envmap_xml.png"><img src="/images/converted/mitsuba-quick-start-guide/envmap_xml.png" alt="EnvMap_XML"></a></p>
<p>The &ldquo;uffizi.exr&rdquo; file used here is an HDR light probe from <a href="http://gl.ict.usc.edu/Data/HighResProbes/">USC&rsquo;s high-resolution image probe gallery</a>. Note that this emitter does not support cubemaps, and instead expects a 2D image that uses equirectangular mapping. Here&rsquo;s what it looks like rendered with the path tracer, using a higher sample count of 256 samples per pixel:</p>
<p><a href="/images/converted/mitsuba-quick-start-guide/envmap_pt.png"><img src="/images/converted/mitsuba-quick-start-guide/envmap_pt.png" alt="EnvMap_PT"></a></p>
<h3 id="7-further-reading">7. Further Reading</h3>
<p>At this point, you should hopefully understand the basics of how to use Mitsuba, and how to set up scenes in its XML file format. There&rsquo;s obviously quite a bit of functionality that I didn&rsquo;t cover, which you can read about in the documentation. If you&rsquo;d like to know more about how Mitsuba works, I would very strongly recommend reading through <em><a href="http://www.pbrt.org/">Physically Based Rendering</a></em>. Mitsuba is heavily based on pbrt (which is the open-source renderer described in the book), and the book does a fantastic job of explaining all of the relevant concepts. It&rsquo;s also a must-have resource if you&rsquo;d like to write your own path tracer, which is something that I would highly recommend to anybody working in real-time graphics.</p>
<p>Oh and just in case you missed it, here&rsquo;s the link to the zip file containing the example Mitsuba scene: <a href="https://mynameismjp.files.wordpress.com/2015/04/testscene.zip">https://mynameismjp.files.wordpress.com/2015/04/testscene.zip</a></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://www.coolgames.jecool.net/gi-ground-truth-for-comparison/" title="">GI ground truth for comparison | How to play cool games</a> - <time datetime="2015-05-05 06:55:47">May 2, 2015</time></p>
<p>[…] I’ve tried Mitsuba, as per MJP’s advice, but I really can’t get into it. It’s good, but the XML is ridiculously obtuse and […]</p>
<hr />
####
[seblagarde](http://seblagarde.wordpress.com "lagardese@hotmail.fr") - <time datetime="2015-04-04 15:42:58">Apr 6, 2015</time>
<p>hey, Good to see Mitsuba spreading accros game industry. We use it for frostbite too to compare our in-engine ground thruth with ground thruth :) On thing you Forgot to mention is the tone mapper. For the sake of good comparison we bypass our tone mapper in frostbite and export a screenshot in exr to compare with a exr shot produce by Mitsuba without tone mapping. Only way to have reliable result. So i think you should introduce the image postprocess control of Mitsuba too.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2015-04-06 09:57:50">Apr 1, 2015</time>
<p>Hi Sébastien, I agree, that&rsquo;s a good topic to touch on in the tutorial. Here at RAD we also do comparisons with EXR screenshots, so that we can remove tone mapping from the mix. @Nico: I actually don&rsquo;t have a lot of experience with LuxRender, so I wouldn&rsquo;t be a great person to ask about that! I will definitely have to spend some time with it at some point.</p>
<hr />
####
[Nico](http://ngaloppo.wordpress.com/ "nico@crossbar.net") - <time datetime="2015-04-06 08:20:59">Apr 1, 2015</time>
<p>Is there any advantage of using Mitsuba vs. LuxRender? (<a href="http://www.luxrender.net/en">http://www.luxrender.net/en</a>_GB/index)</p>
<hr />
####
[Wumpf](https://twitter.com/wumpf "r_andreas2@web.de") - <time datetime="2015-05-15 14:54:38">May 5, 2015</time>
<p>Just wanted to thank you for this great introduction/tutorial! :) It helped me a lot to get started with groundtruth renderings for my realtime global illumination experiments :)</p>
<hr />
####
[Nicolas Bertoa](http://nbertoa.wordpress.com "nicolas.bertoa@outlook.com") - <time datetime="2017-03-29 07:55:28">Mar 3, 2017</time>
<p>Thanks for this article, Matt. You said: &ldquo;It’s also a must-have resource if you’d like to write your own path tracer, which is something that I would highly recommend to anybody working in real-time graphics.&rdquo; Some time ago I had the same doubt about going offline or not. Why do you think doing that will help in real-time graphics?</p>
<hr />
####
[AGraphicsGuy](http://agraphicsguy.wordpress.com "jerrycao_1985@icloud.com") - <time datetime="2015-12-06 20:09:26">Dec 0, 2015</time>
<p>There is an alternative by using Blender with mitsuba, which will be much simpler than the above procedure. I&rsquo;m not sure if it exposes full features in mitsuba, it works pretty cool to me.</p>
<hr />
####
[Theo Gottwald](http://www.fa2.de "atg@fa2.de") - <time datetime="2016-01-13 02:13:46">Jan 3, 2016</time>
<p>To make Mitsuba really render specular, it needs to be compiled with a setting of spectrum(something)=30 However this specular version is nowhere to download.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2017-03-29 13:15:16">Mar 3, 2017</time>
<p>Hi Nicolas, I think that writing a path tracer gives you a better perspective on BRDF&rsquo;s and the rendering equation, since you take a more unified approach to solving the rendering equation. In real-time we end up having to do so many specialized paths for things like point lights, area lights, environment specular, etc., since we have such strict limitations on what we can do in a single frame. Meanwhile in a path tracer you&rsquo;re free to to do arbitrary queries on your scene via ray tracing, which lets you focus on integrating to solve the rendering equation. So instead of using approximation X or for one light source and technique Y for handling your environment specular, you just use ray tracing to get radiance and use the same BRDF for everything. Having that flexibility and simplicity also makes it much easier (IMO) to explore things like refraction, and volumetric scattering. This is all my own opinion based on my personal experiences, so your mileage may vary. If nothing else, knowing how a path tracer works will give you valuable knowledge for understanding how offline renderers work, or for writing an offline baking tool that you use for a real-time engine. Monte Carlo integration (which is used heavily in path tracing) is also an invaluable tool to have on your belt for almost any rendering scenario, both real-time and offline.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Some Special Thanks</title>
			<link>https://therealmjp.github.io/posts/some-special-thanks/</link>
			<pubDate>Mon, 23 Mar 2015 08:35:45 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/some-special-thanks/</guid>
			<description>About a month ago, a little game called The Order: 1886 finally hit store shelves. Its release marks the culmination of my past 4 years at Ready At Dawn, which were largely devoted to developing the core rendering and engine technology that was ultimately used for the game. It&amp;rsquo;s also a major milestone for me personally, as it&amp;rsquo;s the first project that I&amp;rsquo;ve worked on full-time from start to finish. I&amp;rsquo;m of course immensely proud of what our team has managed to accomplish, and I feel tremendously lucky to go to work every day with such a talented and dedicated group of people.</description>
			<content:encoded><![CDATA[<p>About a month ago, a little game called The Order: 1886 finally hit store shelves. Its release marks the culmination of my past 4 years at Ready At Dawn, which were largely devoted to developing the core rendering and engine technology that was ultimately used for the game. It&rsquo;s also a major milestone for me personally, as it&rsquo;s the first project that I&rsquo;ve worked on full-time from start to finish. I&rsquo;m of course immensely proud of what our team has managed to accomplish, and I feel tremendously lucky to go to work every day with such a talented and dedicated group of people.</p>
<p>My work wouldn&rsquo;t have been possible without the help of many individuals both internal and external to our studio, but unfortunately there&rsquo;s just too many people to list in a &ldquo;special thanks&rdquo; section of the credits. So instead of that, I&rsquo;ve made my own special thanks section! But before you read it, please know that this list (like everything else on my blog) only represents my own personal feelings and experiences, and does not represent the rest of the team or my company as a whole. And now, without further ado&hellip;</p>
<h3 id="from-sce">From SCE:</h3>
<ul>
<li>
<p><a href="https://twitter.com/TobiasBerghoff">Tobias Berghoff</a>, <a href="https://twitter.com/nonchaotic">Steven Tovey</a>, Matteo Scapuzzi, <a href="https://twitter.com/_bouliiii_">Benjamin Segovia</a>, Vince Diesi, Chris Carty, Nicolas Serres, and everyone else at the Advanced Technology Group. These fine folks have developed a fantastic suite of debugging and performance tools for the PS4, and thus played a huge part in making the PS4 the incredible development platform that it is.</p>
</li>
<li>
<p>Dave Simpson, <a href="https://twitter.com/postgoodism">Cort Stratton</a>, Cedric Lallian, and everyone else at the ICE Team for making the best graphics API that I&rsquo;ve ever used.</p>
</li>
<li>
<p>Geoff Audy, <a href="https://twitter.com/Icetigris">Elizabeth Baumel</a>, Peter Young, and everyone else at Foster City and elsewhere that provided valuable tools and developer support.</p>
</li>
</ul>
<h3 id="from-the-graphics-community">From the graphics community:</h3>
<ul>
<li>
<p>Andrew Lauritzen for providing all of his valuable research on shadows and deferred rendering. His work with VSM and SDSM formed the basis of our shadowing tech in The Order, and his amazing presentation + demo on deferred rendering back in 2010 helped set the standard for state-of-the art in rendering tech for games. Also I&rsquo;ll go ahead and thank him preemptively for (hopefully) forgiving me for thanking him here, even though I promised him last year at GDC that I would thank him in the credits.</p>
</li>
<li>
<p><a href="https://twitter.com/self_shadow">Stephen Hill</a>, whose excellent articles and presentations have always inspired me to strive for the next level of quality when sharing my own work.</p>
</li>
<li>
<p><a href="https://twitter.com/stevemcauley">Steve McAuley</a>, who along with Mr. Hill is responsible for cultivating what is arguably the best collection of material year after year: the physically based shading course at SIGGRAPH. I&rsquo;m very thankful to them for inviting us to participate in 2013, and then helping myself and Dave to deliver our presentation and course notes.</p>
</li>
<li>
<p><a href="https://twitter.com/renderwonk">Naty Hoffman</a>, <a href="https://twitter.com/TimothyLottes">Timothy Lottes</a>, <a href="https://twitter.com/adrianb3000">Adrian Bentley</a>, <a href="https://twitter.com/SebLagarde">Sébastien Legarde</a>,  <a href="https://twitter.com/repi">Johan Andersson</a>, <a href="https://twitter.com/selfresonating">James McLaren</a>, <a href="https://twitter.com/BrianKaris">Brian Karis</a>, Peter-Pike Sloan, <a href="https://twitter.com/Reedbeta">Nathan Reed</a>, <a href="https://twitter.com/ChristerEricson">Christer Ericson</a>, <a href="https://twitter.com/kenpex">Angelo Pesce</a>, Michał Iwanicki, <a href="https://twitter.com/cgyrling">Christian Gyrling</a>, <a href="https://twitter.com/aras_p">Aras Pranckevičius</a>, Michal Valient, <a href="https://twitter.com/BartWronsk">Bart Wronski</a>, <a href="https://twitter.com/jasminpatry">Jasmin Patry</a>, <a href="https://twitter.com/MichalDrobot">Michal Drobot</a>, <a href="https://twitter.com/iryoku1">Jorge Jimenez</a>, <a href="https://twitter.com/PadraicHennessy">Padraic Hennessy</a>, and anyone else who was kind enough to talk shop over a drink at GDC or SIGGRAPH.</p>
</li>
<li>
<p>Anybody who has every given presentations, written articles, or otherwise contributed to the vast wealth of public knowledge concerning computer graphics. I&rsquo;m really proud of the culture of openness and sharing among the graphics community, and also very grateful for it. We often stood on the shoulders of giants when creating the tech for our game, and we couldn&rsquo;t have achieved we did if without drawing from the insights and techniques that were generously shared by other developers and researchers.</p>
</li>
</ul>
<h3 id="from-ready-at-dawn">From Ready at Dawn:</h3>
<ul>
<li>
<p>Everyone that I worked with on the Tools and Engine team: Nick Blasingame, <a href="https://twitter.com/JoeFerfecki">Joe Ferfecki</a>, <a href="https://twitter.com/GabrielSassone">Gabriel Sassone</a>, Sean Flynn, Simone Kulczycki, Scott Murray, <a href="https://twitter.com/fatlimey">Robin Green</a>, Brett Dixon, <a href="https://twitter.com/schuttejoe">Joe Schutte</a>, <a href="https://twitter.com/daveneubelt">David Neubelt</a>, Alex Clark, Garret Foster, <a href="https://twitter.com/tomplunket">Tom Plunket</a>, Aaron Halstead, <a href="https://twitter.com/j_nikolai">Jeremy Nikolai</a>, and <a href="https://twitter.com/jamiehayes81">Jamie Hayes</a>. If you appreciate anything at all about the tech of The Order, then please think of these people! Also I need to give Garret an extra-special thanks for letting myself and Dave take all of the credit for his work on the material pipeline.</p>
</li>
<li>
<p>Our art director Nathan Phail-Liff, who not only directs a phenomenal group of artists but also had a major hand in shaping the direction of the tech that was developed for the project.</p>
</li>
<li>
<p><a href="https://twitter.com/Anthony_Vitale_">Anthony Vitale</a>, who leads the best damn environment art team in the business. If you haven&rsquo;t seen it yet, go check out their <a href="http://www.polycount.com/forum/showthread.php?t=149706">art dump</a> on the polycount forums!</p>
</li>
<li>
<p><a href="https://twitter.com/Ru_Weerasuriya">Ru Weerasuriya</a>, <a href="https://twitter.com/AndreaPessino">Andrea Pessino</a>, and Dider Malenfant for starting this amazing company, and for giving me an opportunity to work there.</p>
</li>
<li>
<p>Everyone else at Ready At Dawn that worked on the project with me. I don&rsquo;t know if I can ever get used to overwhelming amount of sheer talent and dedication that you can have at a game studio, and the people that I worked with had that in spades. I look forward to making many more beautiful things with them in the future!</p>
</li>
</ul>
<p>If you read through all of this, I appreciate you taking the time to do so. It means a lot to me that the people listed above get their due credit, and so I hope that my humble little blog post has enough reach to give them some of the recognition that they rightfully deserve.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22%22"></a> - <time datetime="2015-06-05 16:02:18">Jun 5, 2015</time></p>
<p>Thank you for not erasing me from history.</p>
<hr />
####
[GamingPicks](https://gamingpicks.wordpress.com/ "videogamesvideos@gmail.com") - <time datetime="2015-03-23 11:02:31">Mar 1, 2015</time>
<p>Thank you and all the people at Ready at Dawn, I honestly loved the game. And visually (technical and artistically) is among the best I&rsquo;ve played on my almost 30 years of gaming. Congratulations!</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Shadow Sample Update</title>
			<link>https://therealmjp.github.io/posts/shadow-sample-update/</link>
			<pubDate>Wed, 18 Feb 2015 18:00:06 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/shadow-sample-update/</guid>
			<description>Update 1/24/2016: one of the authors of the Moment Shadow Mapping paper contacted to let me know that there was an issue in my implementation of the 16-bit variant of EVSM. My sample app was clamping the maximum exponential warp factor to 10.0, which can result in overflow for a 16-bit float. This has the effect of reducing light bleeding, but it also causes edge quality to suffer during filtering. This made the light bleeding comparison with MSM16 unfair, particularly since my comparisons did not use high filtering settings.</description>
			<content:encoded><![CDATA[<p><em>Update 1/24/2016: one of the authors of the Moment Shadow Mapping paper contacted to let me know that there was an issue in my implementation of the 16-bit variant of EVSM. My sample app was clamping the maximum exponential warp factor to 10.0, which can result in overflow for a 16-bit float. This has the effect of reducing light bleeding, but it also causes edge quality to suffer during filtering. This made the light bleeding comparison with MSM16 unfair, particularly since my comparisons did not use high filtering settings. The version of the sample that&rsquo;s now uploaded to <a href="https://github.com/TheRealMJP/Shadows">GitHub</a> has corrected this issue, and I&rsquo;ve generated a new set of comparison images. I&rsquo;ve also updated my commentary on these comparisons to better reflect MSM&rsquo;s improvements over EVSM.</em></p>
<p>This past week a paper entitled &ldquo;<a href="http://cg.cs.uni-bonn.de/en/publications/paper-details/peters-2015-msm/">Moment Shadow Mapping</a>&rdquo; was released in advance of its planned publication at I3D in San Francisco. If you haven&rsquo;t seen it yet, it presents a new method for achieving filterable shadow maps, with a promised improvement in quality compared to Variance Shadow Maps. Myself and many others were naturally intrigued, as filterable shadow maps are highly desirable for reducing various forms of aliasing. The paper primarily suggests two variants of the technique: one that directly stores the 1st, 2nd, 3rd, and 4th moments in a RGBA32_FLOAT texture, and another that uses an optimized quantization scheme (which essentially boils down to a 4x4 matrix transform) in order to use an RGBA16_UNORM texture instead. The first variant most likely isn&rsquo;t immediately interesting for people working on games, since 128 bits per texel requires quite a bit of memory storage and bandwidth. It&rsquo;s also the same storage cost as the highest-quality variant of EVSM (VSM with an exponential warp), which already provides high-quality filterable shadows with minimal light bleeding. So that really leaves us with the quantized 16-bit variant. Using 16-bit storage for EVSM results in more artifacts and increased light bleeding compared to the 32-bit version, so if MSM can provide better results than it could potentially be useful for games.</p>
<p>I was eager to see the results myself, so I downloaded the sample app that the authors were kind enough to provide. Unfortunately their sample didn&rsquo;t implement EVSM, and so I wasn&rsquo;t able perform any comparisons. However the implementation of MSM is very straightforward, and so I decided to just integrate it into my <a href="../shadow-maps/">old shadows sample</a>. I updated the corresponding blog post and re-uploaded the binary + source, so if you&rsquo;d like to check it out for yourself then feel free to download it from <a href="https://github.com/TheRealMJP/Shadows">GitHub</a>:</p>
<p><a href="https://github.com/TheRealMJP/Shadows/releases">https://github.com/TheRealMJP/Shadows/releases</a></p>
<p>The MSM techniques can be found under the &ldquo;Shadow Mode&rdquo; setting. I implemented both the Hamburger and Hausdorff methods, which are available as two separate shadow modes. If you change the VSM/MSM format from 32-bit to 16-bit, then the optimized quantization scheme will be used when converting from a depth map to a moment shadow map.</p>
<p>The 32-bit variant of MSM seems to provide quality that&rsquo;s pretty close to the 32-bit variant of EVSM, with slightly worse performance. Both techniques are mostly free of light bleeding, but still exhibit bleeding artifacts for the more extreme cases. The 16-bit variant initially has some of the same issues as the 16-bit version of EVSM, in that it will typically result in artifacts due to a lack of precision in the storage format. Both EVSM16 and MSM16 can reduce or eliminate these artifacts by increasing their respective bias values, but raising them too high can result in a loss of shadow details in areas where the receiver is very close to the occluder. As for light bleeding, there&rsquo;s bad news and there&rsquo;s good news. The bad news is that MSM16 does suffer from increased bleeding compared to the MSM32, which is unfortunate. The good news that it ultimately fares better than EVSM16, which makes for a solid improvement with similar performance and storage cost. In fact, I&rsquo;ve found that bleeding can be almost avoided entirely in my test scenes by using the &ldquo;standard&rdquo; VSM bleeding reduction technique, which essentially just remaps the output range of the shadow visibility term. This technique also works for EVSM16, but in my test scenes this technique is not enough to entirely remove the bleeding for all cases. I gathered some images so that can compare for yourself:</p>
<p><a href="/images/converted/shadow-sample-update/msm-comparison-01-grid.png"><img src="/images/converted/shadow-sample-update/msm-comparison-01-grid_resized_620.png" alt="MSM Comparison 01 Grid"></a></p>
<p>The EVSM images all use the 4-component variant, while the MSM images all use the 4-moment Hamburger variant. For the images with the bleeding &ldquo;fix&rsquo;, they use a reduction factor of 0.25. In all cases the shadow map resolution is 2048x2048, with 4xMSAA, 16x anisotropic filtering, and mipmaps enabled for both EVSM and MSM. The first MSM16 image shows what happens if you don&rsquo;t use a moment bias, while the next one shows what it looks like with a moment bias of 0.030. The last image shows MSM16 with a light bleeding reduction applied, and as you can see the artifacts are almost completely gone. Compare it with the EVSM16 image, where some of the bleeding remains even when applying the reduction factor.</p>
<p>Here&rsquo;s a few more images from another area with light bleeding:</p>
<p><a href="/images/converted/shadow-sample-update/msm-comparison-02-grid.png"><img src="/images/converted/shadow-sample-update/msm-comparison-02-grid_resized_620.png" alt="MSM Comparison 02 Grid"></a></p>
<p>EVSM16 and MSM16 both show bleeding artifacts by default, but the bleeding is reduced quite a bit with MSM16. The bleeding reduction works very well for both techniques for this particular viewpoint, but with MSM16 we can probably get away with a lower bleeding reduction factor.</p>
<p>To finish up, here are some images from another particularly troublesome case: a close up on a character, with heavy filtering enabled.</p>
<p><a href="/images/converted/shadow-sample-update/msm-comparison-03-grid.png"><img src="/images/converted/shadow-sample-update/msm-comparison-03-grid_resized_395.png" alt="MSM Comparison 03 Grid"></a></p>
<p>This is a pretty tough situation for all shadow techniques. The character is very close, and has quite a few small details that need shadows in order for them to read properly. However there&rsquo;s still quite a bit of things in the background that need shadowing, and so the cascades need to cover a range beyond just the character. SDSM helps quite a bit here, since it ensures that we can create a logarithmic partioning of the viewable area for close-to-ideal distribution of our shadow map resolution. Even with that in place, our 9x9 PCF kernel still has some trouble. In the first image, the issues mostly stem from incorrect biasing. The bias factor in that image is computed &ldquo;automatically&rdquo; by determining the slope of the receiver using pixel shader derivatives, which is often quite effective. However it assumes that the receiver is planar, and this assumption is violated when the filter footprint covers an edge between two triangles that aren&rsquo;t coplanar. Switching to a &ldquo;fixed&rdquo; bias factor causes the artifacts to go away, but we then lose shadow detail in areas like the nose. EVSM and MSM also have issues due using a wider filter kernel, which requires increasing their bias factors in order to avoid artifacts at triangle boundaries. Even with an increased bias, EVSM16 still shows some precision artifacts in places where the occluder is rather close to the receiver (check out the nose, or the shadow under the collar). MSM16 also has some precision issues, at least initially. To fix this, I increased the &ldquo;depth bias&rdquo; parameter to 0.4, which directly biases the computed pixel depth. After doing that and applying a little bit of bleeding reduction, we end up with some very nice results!</p>
<p>If you&rsquo;d like to look a the full captures that I used to generate these comparison images, I uploaded the source .PSD files here: <a href="https://mynameismjp.files.wordpress.com/2016/01/msm-comparisons.zip">https://mynameismjp.files.wordpress.com/2016/01/msm-comparisons.zip</a></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://www.revivedgame.com/" title=""></a> - <time datetime="2015-02-18 12:28:17">Feb 3, 2015</time></p>
<p>Hi Thank you for this great update and also so many thanks for nvidia driver bug fix!!! Unfortunately my 770 gpu has a problem with 16 and 32 bit Hausdorff method. <a href="http://postimg.org/image/ou0341izb/">http://postimg.org/image/ou0341izb/</a></p>
<hr />
####
[Michael]( "segura.mikael@orange.fr") - <time datetime="2015-02-21 09:23:14">Feb 6, 2015</time>
<p>I have the same problem and using the 16-bits MSM I have lot of dot on the shadow. Another thing I saw is you set params on cascade shadow map, the original SDSM does all auto the split, is it safer to param or you just did that to win time on the demo ?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2015-02-22 01:01:47">Feb 0, 2015</time>
<p>Hi Michael, If you&rsquo;re referring to the parameters for selecting the min and max depth ranges for the cascades as well as the cascade split depths, for all of my comparisons I was using the &ldquo;Auto Compute Depth Bounds&rdquo; option which automatically computes the min and max visible depth from the depth buffer in a manner similar to SDSM. I also used the logarithmic partitioning scheme, which also automatically computes the split depths.</p>
<hr />
####
[Michael]( "segura.mikael@orange.fr") - <time datetime="2015-02-22 17:30:05">Feb 0, 2015</time>
<p>Hi MJP, Thanks to have explained that, so the “Auto Compute Depth Bounds” + &ldquo;logarithmic partitioning scheme&rdquo; is the full auto. I have a question about the &ldquo;Depth Bounds Readback Latency&rdquo;, is it really needed to have a latency, for performance, which gives possible visual error on low FPS ? Screenshot of the 16-bits dot issue : <a href="http://zupimages.net/up/15/09/1s75.png">http://zupimages.net/up/15/09/1s75.png</a></p>
<hr />
####
[Michael]( "segura.mikael@orange.fr") - <time datetime="2015-02-22 17:37:19">Feb 0, 2015</time>
<p>About the dot, just using a bias of 0.022 removes them. Only some are visible on intersection apparently, which is surely because of the mesh, you can confirm that surely.</p>
<hr />
####
[Update For My Shadow Sample Update &#8211; The Danger Zone](https://mynameismjp.wordpress.com/2016/01/24/update-for-my-shadow-sample-update/ "") - <time datetime="2016-01-24 16:25:05">Jan 0, 2016</time>
<p>[…] I was contacted by Christoph Peters (one of the authors of Moment Shadow Mapping) regarding a blog post where I compared EVSM to MSM using my sample app. He noticed that I was incorrectly clamping the […]</p>
<hr />
####
[unbird]( "ugello@gmx.net") - <time datetime="2016-05-16 10:40:17">May 1, 2016</time>
<p>First, thanks for another great read and source. Again :) I hope you don&rsquo;t mind going off topic but I noticed something peculiar in GPUBatch.hlsl. There&rsquo;s a SV_GroupIndex for the DrawArgsBuffer. My google-fu is failing me here so&hellip; is this another undocumented feature (like SV_DepthGreaterEqual) ? What does it improve ? On what hardware ?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-05-16 12:56:09">May 1, 2016</time>
<p>The docs for SV_GroupIndex are here: <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ff471569(v=vs.85).aspx">https://msdn.microsoft.com/en-us/library/windows/desktop/ff471569(v=vs.85).aspx</a> It&rsquo;s basically just a shortcut for flattening the group thread ID into a 1D value. I don&rsquo;t think it&rsquo;s actually any faster, in fact driver&rsquo;s compiler might even insert some math at the beginning of the shader in order to generate it. It&rsquo;s also not at all necessary in that particular shader since the group size is 1D. I think I just used it out of habit.</p>
<hr />
####
[unbird]( "ugello@gmx.net") - <time datetime="2016-05-16 14:29:51">May 1, 2016</time>
<p>:D I know what it does for shader input. Sorry, maybe I wasn&rsquo;t quite clear. I meant this line: RWByteAddressBuffer DrawArgsBuffer : SV_GroupIndex : register(u0); It&rsquo;s legal to put semantics on globals, I just never saw a SV semantic this way and thought it would give the driver a hint or something. Other than turning up in the shader reflection I don&rsquo;t think they have any other impact. At least as far as I know ;) Thanks for the fast reply.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-05-16 16:57:57">May 1, 2016</time>
<p>Oh, I didn&rsquo;t even see that! I&rsquo;m pretty sure that&rsquo;s just some kind of search/replace gone badly, and it definitely wasn&rsquo;t intentional. I don&rsquo;t think it will have any effect, but I&rsquo;m definitely going to remove it when I get a chance. Thank you for pointing that out!</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Come see me talk at GDC 2014</title>
			<link>https://therealmjp.github.io/posts/come-see-me-talk-at-gdc-2014/</link>
			<pubDate>Wed, 12 Mar 2014 06:49:11 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/come-see-me-talk-at-gdc-2014/</guid>
			<description>Myself and fellow lead graphics programmer David Neubelt will be at GDC next week, talking about the rendering technology behind The Order: 1886. Unfortunately the talk came together a bit late, and so it initially started from the talk that we gave back at SIGGRAPH at last year (which is why it has the same title). However we don&amp;rsquo;t want to just rehash the same material, so we&amp;rsquo;ve added tons of new slides and revamped the old ones.</description>
			<content:encoded><![CDATA[<p>Myself and fellow lead graphics programmer David Neubelt will be at GDC next week, <a href="http://schedule.gdconf.com/session-id/826899">talking</a> about the rendering technology behind The Order: 1886. Unfortunately the talk came together a bit late, and so it initially started from the talk that we gave back at <a href="http://blog.selfshadow.com/publications/s2013-shading-course/">SIGGRAPH</a> at last year (which is why it has the same title). However we don&rsquo;t want to just rehash the same material, so we&rsquo;ve added tons of new slides and revamped the old ones. The resulting presentation has a much more of an engineering focus, and will cover a lot of the nuts and bolts behind our engine and the material pipeline. Some of the new things we&rsquo;ll be covering include:</p>
<ul>
<li>Dynamic lighting</li>
<li>Baked diffuse and specular lighting</li>
<li>Baked and real-time ambient occlusion</li>
<li>Our shader system, and how it interacts with our material pipeline</li>
<li>Details of how we perform compositing in our build pipeline</li>
<li>Hair shading</li>
<li>Practical implementation issues with our shading model</li>
<li>Performance and memory statistics</li>
<li>Several breakdowns showing how various rendering techniques combine to form the final image</li>
<li>At least one new funny picture</li>
</ul>
<p>We want the presentation to be fresh and informative even if you saw our SIGGRAPH presentation, and I&rsquo;m pretty sure that we have enough new material to ensure that it will be. So if you&rsquo;re interested, come by at 5:00 PM on Wednesday. If you can&rsquo;t make it, I&rsquo;m going to try to make sure that we have the slides up for download as soon as possible.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22jischneider@hotmail.com%22">jischneider</a> - <time datetime="2014-04-30 00:56:12">Apr 3, 2014</time></p>
<p>Good material as always. Thanks Matt.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2014-04-02 09:41:14">Apr 3, 2014</time>
<p>Thank you Albert and Laurent! For lighting values we&rsquo;re not using real-life units. We generally attempt to keep the relative intensity between different values consistent with the relative intensity difference for real-world lights, but in some cases we fudge it in order to make authoring easier. The version of the slides on our website has notes in it. You can downoad it here: <a href="http://www.readyatdawn.com/slides/RAD">http://www.readyatdawn.com/slides/RAD</a>_GDC_2014.pptx</p>
<hr />
####
[Laurent](http://www.laurentharduin.fr "laurent.harduin@free.fr") - <time datetime="2014-04-02 08:51:46">Apr 3, 2014</time>
<p>Hi, I downloaded the slides from GDC vault and it&rsquo;s really interesting. Everything looks so nice! I have a few questions : for the emissive lighting values, are you using some real life unit or is it an arbitrary unit ? and do you plan to release a version of the slides with notes ? Thank you very much, can&rsquo;t wait to see the final game ;)</p>
<hr />
####
[]( "") - <time datetime="2014-08-27 02:03:23">Aug 3, 2014</time>
<p>very impressive, will you write a detail article about directional ao maps and dynamic ao? thanks.</p>
<hr />
####
[Albert](http://albertelw.in "albertelwin@gmail.com") - <time datetime="2014-03-28 02:53:06">Mar 5, 2014</time>
<p>Talk was awesome Matt and was great to meet you! I really liked how you guys use material inheritance to lock down the &ldquo;look&rdquo; of levels.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Weighted Blended Order-Independent Transparency</title>
			<link>https://therealmjp.github.io/posts/weighted-blended-oit/</link>
			<pubDate>Tue, 04 Feb 2014 06:58:04 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/weighted-blended-oit/</guid>
			<description>http://mynameismjp.files.wordpress.com/2014/02/blendedoit.zip
Back in December, Morgan McGuire and Louis Bavoil published a paper called Weighted Blended Order-Independent Transparency. In case you haven&amp;rsquo;t read it yet (you really should!), it proposes an OIT scheme that uses a weighted blend of all surfaces that overlap a given pixel. In other words finalColor = w0 * c0 + w1 * c1 + w2 * c2&amp;hellip;etc. With a weighted blend the order of rendering no longer matters, which frees you from the never-ending nightmare of sorting.</description>
			<content:encoded><![CDATA[<p><a href="http://mynameismjp.files.wordpress.com/2014/02/blendedoit.zip">http://mynameismjp.files.wordpress.com/2014/02/blendedoit.zip</a></p>
<p>Back in December, Morgan McGuire  and Louis Bavoil published a paper called <a href="http://jcgt.org/published/0002/02/09/">Weighted Blended Order-Independent Transparency</a>. In case you haven&rsquo;t read it yet (you really should!), it proposes an OIT scheme that uses a weighted blend of all surfaces that overlap a given pixel. In other words finalColor = w0 * c0 + w1 * c1 + w2 * c2&hellip;etc. With a weighted blend the order of rendering no longer matters, which frees you from the never-ending nightmare of sorting. You can actually achieve results that are very close to a traditional sorted alpha blend, as long as your per-surface weights a carefully chosen. Obviously it&rsquo;s that last part that makes it tricky, consequently McGuire and Bavoil&rsquo;s primary contribution is proposing a weighting function that&rsquo;s based on the view-space depth of a given surface. The reasoning behind using a depth-based weighting function is intuitive: closer surfaces obscure the surfaces behind them, so the closer surfaces should be weighted higher when blending. In practice the implementation is really simple: in the pixel shader you compute color, opacity, and a weight value based on both depth and opacity. You then output float4(color* opacity, opacity) * weight  to 1 render target,while also outputting weight alone to a second render target (the first RT needs to be fp16 RGBA for HDR, but the second can just be R8_UNORM or R16_UNORM). For both render targets special blending modes are required, however they both can be represented by standard fixed-function blending available in GL/DX. After rendering all of your transparents, you then perform a full-screen &ldquo;resolve&rdquo; pass where you normalize the weights and then blend with the opaque surfaces underneath the transparents. Obviously this is really appealing since you completely remove any dependency on the ordering of draw calls, and you don&rsquo;t need to build per-pixel lists or anything like that (which is nice for us mortals who don&rsquo;t have pixel sync).  The downside is that you&rsquo;re at the mercy of your weighting function, and you potentially open yourself up to new kinds of artifacts issues depending on what sort of weighting function is used.</p>
<p>When the paper came out I read it and I was naturally interested, so I quickly hacked together a sample project using another project as a base. Unfortunately over the past 2 months there&rsquo;s been holidays, the flu, and several weeks of long hours at work so that we could could finish up a major milestone. So while I&rsquo;ve had time to visit my family and optimize our engine for PS4, I haven&rsquo;t really had any time to come up with a proper sample app that really lets you explore the BOIT technique in a variety of scenarios. However I really hate not having source code and a working sample app to go with papers, so I&rsquo;m going to release it so that others at least have something they can use for evaluating their proposed algorithm. Hopefully it&rsquo;s useful, despite how bad the test scene is. Basically it&rsquo;s just a simple cornell-box like scene made up of a few walls, a sphere, a cylinder, a torus, and a sky (I normally use it for GI testing), but I added the abililty to toggle through 2 alternative albedo maps: a smoke texture, and a tree texture. It doesn&rsquo;t look great, but it&rsquo;s enough to get a few layers of transparency with varying lighting conditions:</p>
<p><a href="/images/converted/weighted-blended-oit/scene_normal.png"><img src="/images/converted/weighted-blended-oit/scene_normal.png" alt="Scene_Normal"></a>
The sample is based on another project I&rsquo;ve been working on for quite some time with my fellow graphics programmer David Neubelt, where we&rsquo;ve been exploring new techniques for baking GI into lightmaps. For that project I had written a simple multithreaded ray-tracer using Embree 2.0 (which is an awesome library, and I <em>highly</em> recommend it), so I re-purposed it into a ground-truth renderer for this sample. You can toggle it on and off  to see what the scene would look like with perfect sorting, which is useful for evaluating the &ldquo;correctness&rdquo; of the BOIT algorithm. It&rsquo;s very fast on my mighty 3.6GHz Core i7, but it might chug a bit for those of you running on mobile CPU&rsquo;s. If that&rsquo;s true I apologize, however I made sure that all of the UI and controls are decoupled from the ray-tracing framerate so that the app remains responsive.</p>
<p>I&rsquo;d love to do a more thorough write-up that really goes into depth on the advantages and disadvantages in multiple scenarios, but I&rsquo;m afraid I just don&rsquo;t have the time for it at the moment. So instead I&rsquo;ll just share some quick thoughts and screenshots:</p>
<p>**It&rsquo;s pretty good for surfaces with low to medium opacity ** - with the smoke texture applied, it actually achieves decent results. The biggest issues are where there&rsquo;s a large difference in the lighting intensity between two overlapping surfaces, which makes sense since this also applies to improperly sorted surfaces rendered with traditional alpha blending. Top image is with Blended OIT, bottom image is ground truth:</p>
<p><a href="/images/converted/weighted-blended-oit/smoke_boit.png"><img src="/images/converted/weighted-blended-oit/smoke_boit.png" alt="Smoke_BOIT"></a>
<a href="/images/converted/weighted-blended-oit/smoke_ref.png"><img src="/images/converted/weighted-blended-oit/smoke_ref.png" alt="Smoke_Ref"></a></p>
<p>If you look at the area where the closer, brighter surface overlaps the darker surface on the cylinder you can see an example of where the results differ from the ground-truth render. Fortunately the depth weighting produces results that don&rsquo;t look immediately &ldquo;wrong&rdquo;, which is certainly a big step up from unsorted alpha blending. Here&rsquo;s another image of the test scene with default albedo maps, with an overall opacity of 0.25:</p>
<p><a href="/images/converted/weighted-blended-oit/lowopacity_boit.png"><img src="/images/converted/weighted-blended-oit/lowopacity_boit.png" alt="LowOpacity_BOIT"></a>
<a href="/images/converted/weighted-blended-oit/lowopacity_ref.png"><img src="/images/converted/weighted-blended-oit/lowopacity_ref.png" alt="LowOpacity_Ref"></a></p>
<p><strong>The technique fails for surfaces with high opacity</strong> - one case that the algorithm has trouble with is surfaces with opacity = 1.0. Since it uses a weighted blend, the weight of the closest surface has to be incredibly high relative to any other surfaces in order for it to appear opaque. Here&rsquo;s the test scene with all surfaces using an opacity of 1.0:</p>
<p><a href="/images/converted/weighted-blended-oit/hiopacity_boit.png"><img src="/images/converted/weighted-blended-oit/hiopacity_boit.png" alt="HiOpacity_BOIT"></a>
<a href="/images/converted/weighted-blended-oit/hiopacity_ref.png"><img src="/images/converted/weighted-blended-oit/hiopacity_ref.png" alt="HiOpacity_Ref"></a></p>
<p>You&rsquo;ll notice in the image that the algorithm does actually work correctly with opacity = 1 if there&rsquo;s no overlap of transparent surfaces, so it does hold up in that particular case. However in general this problem makes it unsuitable for materials like foliage, where large portions of of surface need to be fully opaque. Here&rsquo;s the test scene using a tree texture, which illustrates the same problem:</p>
<p><a href="/images/converted/weighted-blended-oit/tree_boit.png"><img src="/images/converted/weighted-blended-oit/tree_boit.png" alt="Tree_BOIT"></a>
<a href="/images/converted/weighted-blended-oit/tree_ref.png"><img src="/images/converted/weighted-blended-oit/tree_ref.png" alt="Tree_Ref"></a></p>
<p>Like I said earlier, you really need to make the closest surface have a an extremely high weight relative to the surfaces behind it if you want it to appear opaque. One simple thing you could do is to keep track of the depth of the closest surface (say in a depth buffer), and then artificially boost the weight of surfaces if their depth matches the depth buffer weight. If you do this (and also scale your &ldquo;boost&rdquo; factor by opacity) you get something like this:</p>
<p><a href="/images/converted/weighted-blended-oit/tree_dbweight.png"><img src="/images/converted/weighted-blended-oit/tree_dbweight.png" alt="Tree_DBWeight"></a></p>
<p>This result looks quite a bit better, although messing with the weights changes the alpha gradients which gives it a different look. This approach obviously has a lot of failure cases. Since you&rsquo;re relying on depth, you could easily create discontinuities at geometry edges. You can also get situations like this, where a surface visible through a transparent portion of the closest surface doesn&rsquo;t get the weight boost and remains translucent in appearance:</p>
<p><a href="/images/converted/weighted-blended-oit/dbweightfail_boit.png"><img src="/images/converted/weighted-blended-oit/dbweightfail_boit.png" alt="DBWeightFail_BOIT"></a></p>
<p><a href="/images/converted/weighted-blended-oit/dbweight_ref.png"><img src="/images/converted/weighted-blended-oit/dbweight_ref.png" alt="DBWeight_Ref"></a>
Notice how the second tree true trunk appears to have a low opacity since it&rsquo;s behind the closest surface. The other major downside is that you need to render your transparents in a depth prepass, which requires performance as well as the memory for an extra depth buffer. However you may already be doing that in order to optimize tiled forward rendering of transparents. Regardless I doubt it would be useful except in certain special-case scenarios, and it&rsquo;s probably easier (and cheaper) to just stick to alpha-test or A2C for those cases.</p>
<p>**Is it usable? **- I&rsquo;m not sure yet. I feel like it would take a lot of testing the wide range of transparents in our game before knowing if it&rsquo;s going to work out. It&rsquo;s too bad that it has failure cases, but if we&rsquo;re going to be honest the bar is pretty damn low when it comes to transparents in  games. In our engine we make an attempt to sort by depth, but our artists frequently have to resort to manually setting &ldquo;sort priorities&rdquo; in order to prevent temporal issues from meshes constantly switching their draw order. The Blended OIT algorithm on the other hand may produce incorrect results, but those results are stable over time. However I feel the much bigger issue with traditional transparent rendering is that ordering constraints are fundamentally at odds with rendering performance. Good performance requires using instancing, reducing state changes and rendering to low-resolution render targets. All 3 of those these are incompatible with rendering based on Z order, which means living with lots of sorting issues if you want optimal performance. With that in mind it really feels like it&rsquo;s hard to do <em>worse</em> than the current status-quo.
That&rsquo;s about all I have for now. Feel free to download the demo and play around with it. If you missed it, the download link is at the top of the page. Also, please let me know if you have any thoughts or ideas regarding the practicality of the technique, since I would definitely be interested in discussing it further.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://mynameismjp.wordpress.com/" title="mpettineo@gmail.com">MJP</a> - <time datetime="2014-03-10 22:49:29">Mar 1, 2014</time></p>
<p>Hi Morgan, Thank you for letting me know about the typo, and offering some guidance! After GDC I will have to revisit this, and see if I can get better results.</p>
<hr />
####
[Richard Fine](http://crunchbites.wordpress.com "rfine@tbrf.net") - <time datetime="2014-02-16 03:02:45">Feb 0, 2014</time>
<p>Interesting technique. One extension I&rsquo;m wondering about: what if you wrote weighted normals, as well as weight, to that second RT? Could you then include a dynamic directional light in your resolve pass?</p>
<hr />
####
[Victor]( "") - <time datetime="2014-08-28 08:16:40">Aug 4, 2014</time>
<p>Also it seems like the sevind buffer&rsquo;s output is the pixel&rsquo;s alpha value, not the weight.</p>
<hr />
####
[dishwasher](http://none "dishwasherblood@gmail.com") - <time datetime="2014-05-19 17:23:18">May 1, 2014</time>
<p>Hi MjP, this demo includes lightmap - LightmapSH.texdata. I can see how it is used, but how do you generate it? In Maya and then somehow convert it to custom format? Would appreciate help. :)</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2014-05-19 17:39:48">May 1, 2014</time>
<p>I actually have a custom GI baking framework that myself and my coworker use for experimenting with GI techniques. That particular lightmap contains radiance stored as 2nd-order spherical harmonics, and was generated using monte carlo integration to project the radiance at each texel onto the spherical harmonics basis. I use embree to perform the ray intersections on the CPU, which is a pretty awesome library. I was planning on releasing the code at some point once we had something cool to show off, but I could release it earlier if there&rsquo;s interest.</p>
<hr />
####
[dishwasher](http://none "dishwasherblood@gmail.com") - <time datetime="2014-05-19 18:05:27">May 1, 2014</time>
<p>I would be actually very interested in seeing the code, this GI baking framework seems really neat. :)</p>
<hr />
####
[Morgan McGuire](https://plus.google.com/102642479743803485873 "morgan3d@gmail.com") - <time datetime="2014-03-10 06:33:43">Mar 1, 2014</time>
<p>There was a typo in the original paper that may be the reason that your results don&rsquo;t look as good as ours. I just fixed this in the PDF online and wrote a blog post showing good results for some difficult scenes and giving some advice for implementation: <a href="http://casual-effects.blogspot.com/2014/03/weighted-blended-order-independent.html">http://casual-effects.blogspot.com/2014/03/weighted-blended-order-independent.html</a> The primary limitation of the technique is that the depth weight has to be tuned for the content. I use the same curve for all scenes (equation 9 in the paper), scaled to the actual near and far plane of the scene. It looks like you aren&rsquo;t using a steep enough curve to support high alpha values. Try using equation 9 for your depth range, and raise the exponents if it still isn&rsquo;t enough.</p>
<hr />
####
[Rosario](http://www.carmageddon.com "rosario@stainlessgames.com") - <time datetime="2015-11-30 07:54:43">Nov 1, 2015</time>
<p>I tried to implement this technique in our game engine and I was able to resolve all the artefact to a playable state&hellip; for a static scene. But when particles (very low alpha, very near to the camera) go on top of a very distant but almost solid translucent object (the sea at the horizon or ice blocks) we had random annoying flashes. And this is almost unavoidable without tweaking continuously the weight function, probably a &ldquo;per object&rdquo; weight multiplier can solve these problems but I think is too much works for the artist. I&rsquo;m pretty sure somebody will come with a smart compute shader solution at some point, till then I think I&rsquo;ll using this technique only for low-res particle.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Sample Framework Updates</title>
			<link>https://therealmjp.github.io/posts/sample-framework-updates/</link>
			<pubDate>Tue, 17 Sep 2013 05:54:02 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/sample-framework-updates/</guid>
			<description>You may have noticed that my latest sample now has a proper UI instead of the homegrown sliders and keyboard toggles that I was using in my older samples. What you might not have noticed is that there&amp;rsquo;s a whole bunch of behind-the-scenes changes to go with that new UI! Before I ramble on, here&amp;rsquo;s a quick bullet-point list of the new features:
Switched to VS2012 and adopted a few C++11 features New UI back-end provided by AntTweakBar C#-based data-definition format for auto-generating UI Shader hot-swapping Better shader caching, and compressed cache files It occurred to me a little while ago that I could try to develop my framework into something that enables rapid prototyping, instead of just being some random bits of cobbled-together code.</description>
			<content:encoded><![CDATA[<p>You may have noticed that my <a href="http://mynameismjp.wordpress.com/2013/09/10/shadow-maps/">latest sample</a> now has a proper UI instead of the homegrown sliders and keyboard toggles that I was using in my older samples. What you might not have noticed is that there&rsquo;s a whole bunch of behind-the-scenes changes to go with that new UI! Before I ramble on, here&rsquo;s a quick bullet-point list of the new features:</p>
<ul>
<li>Switched to VS2012 and adopted a few C++11 features</li>
<li>New UI back-end provided by <a href="http://anttweakbar.sourceforge.net/doc/">AntTweakBar</a></li>
<li>C#-based data-definition format for auto-generating UI</li>
<li>Shader hot-swapping</li>
<li>Better shader caching, and compressed cache files</li>
</ul>
<p>It occurred to me a little while ago that I could try to develop my framework into something that enables rapid prototyping, instead of just being some random bits of cobbled-together code. I&rsquo;m not sure if anybody else will use it for anything, but so far it&rsquo;s working out pretty well for me.</p>
<h2 id="vs-2012">VS 2012</h2>
<p>A little while ago I switched to working in VS 2012 with the Windows 8 SDK for my samples, but continued using the VS 2010 project format and toolset in case anybody was stuck on 2010 and wanted to compile my code. For this latest sample I decided that legacy support wasn&rsquo;t worth it, and made the full switch the newer compiler. I haven&rsquo;t done any really crazy C++ 11 things yet, but now that I&rsquo;ve started using <a href="http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html">enum class and nullptr</a> I never want to go back. Ever since learning C# I&rsquo;ve always wanted C++ enums to use a similar syntax, and C++11 finally fulfilled my wish. I suspect that I&rsquo;ll feel the same way when I start using <a href="https://blogs.oracle.com/pcarlini/entry/c_11_tidbits_non_static">non-static data member initializers</a> (once I switch to VS 2013, of course).</p>
<h2 id="new-ui">New UI</h2>
<p>When I found out about AntTweakBar and how easy it is to integrate, I felt a little silly for ever trying to write my own ghetto UI widgets. If you haven&rsquo;t seen it before, it basically shows up as a window containing a list of tweakable app settings, which is exactly what I need in my sample apps. It also has a neat alternative to sliders where you sort of rotate a virtual lever, and the speed increases depending on how far away you move the mouse from the center point. Here&rsquo;s what it looks like integrated into my shadows sample:</p>
<p><a href="/images/converted/sample-framework-updates/ant_ui.png"><img src="/images/converted/sample-framework-updates/ant_ui.png" alt="Ant_UI"></a></p>
<p>If you&rsquo;re thinking of using AntTweakBar in your code, you might want to grab the files TwHelper.h/cpp from my sample framework. They provide a bunch of wrapper functions over TwSetParam that add type safety, which makes the library a <em>lot</em> easier to work with. I also have much more comprehensive set of Settings classes that further wrap those functions, but those are a lot more tightly coupled to the rest of the framework.</p>
<h2 id="shader-hot-swapping">Shader Hot-Swapping</h2>
<p>This one is a no-brainer, and I&rsquo;m not sure why I waited so long to do it. I decided not to implement it using the Win32 file-watching API. We&rsquo;ve used that at work, and it quickly became the most hated part of our codebase. Instead I took the simple and painless route of checking the timestamp of a single file every N frames, which works great as long as you don&rsquo;t have thousands of files to go through (usually I only have a dozen or so).</p>
<h2 id="ui-data-definition">UI Data Definition</h2>
<p>For a long time I&rsquo;ve been unhappy with my old way of defining application settings, and setting up the UI for them. Previously I did everything in C++ code by declaring a global variable for each setting. This was nice because I got type safety and VisualAssist auto-complete whenever I wanted to access a setting, but it was also a bit cumbersome because I always had to touch code in multiple places whenever I wanted to add a new setting. This was especially annoying when I wanted to access a setting in a shader, since I had manually handle adding it to a constant buffer and setting the value before binding it to a shader. After trying and failing multiple times to come up with something better using just C++, I thought it might be fun to  try something a bit less&hellip;conventional. Ultimately I drew inspiration from game studios that define their data in a non-C++ language and then use that to auto-generate C++ code for use at runtime. If you can do it this way you get the best of both worlds: you define data in a simple language that can express it elegantly, and then you still get all of your nice type-safety and other C++ benefits. You can even add runtime reflection support by generating code that fills out data structures containing the info that you need to reflect on a type. It sounded crazy to do it for a sample framework, but I thought it would be fun to get out of my comfort zone a bit and try something new.</p>
<p>Ultimately I ended up using C# as my data-definition language. It not only has the required feature set, but I also used to be somewhat proficient in it several years ago. In particular I really like the Attribute functionality in C#,  and thought it would be perfect for defining metadata to go along with a setting. Here&rsquo;s an example of how I ended up using them for the &ldquo;Bias&rdquo; setting from my Shadows sample:</p>
<p><a href="/images/converted/sample-framework-updates/biassetting.png"><img src="/images/converted/sample-framework-updates/biassetting.png" alt="BiasSetting"></a></p>
<p>For enum settings, I just declare an enum in C# and use that new type when defining the setting. I also used an attribute to specify a custom string to associate with each enum value:<a href="/images/converted/sample-framework-updates/enumsetting1.png"><img src="/images/converted/sample-framework-updates/enumsetting1.png" alt="EnumSetting"></a></p>
<p>To finish it up, I added simple C# proxies for the Color, Orientation, and Direction setting types supported by the UI system.</p>
<p>Here&rsquo;s how it all it ties together: I define all of the settings in a file called AppSettings.cs, which includes classes for splitting the settings into groups. This file is added to my Visual Studio C++ project, and set to use custom build step that runs before the C++ compilation step. This build step passes the file to SettingsCompiler.exe, which is a command-line C# app created by a C# project in the same solution. This app basically takes the C# settings file, and invokes the <a href="http://msdn.microsoft.com/en-us/library/microsoft.csharp.csharpcodeprovider.aspx">C# compiler</a> (don&rsquo;t you love languages that can compile themselves?) so that it can be compiled as an in-memory assembly. That assembly is then reflected to determine the settings that are declared in the file, and also to extract the various metadata from the attributes. Since the custom attribute classes need to be referenced by both the SettingsCompiler exe as well as the settings code being compiled, I had to put all of them in a separate DLL project called SettingsCompilerAttributes. Once all of the appropriate data is gathered, the C# project then generates and outputs AppSettings.h and AppSettings.cpp. These files contain global definitions of the various settings using the appropriate C++ UI types, and also contains code for initializing and registering those settings with the UI system. These files are added to the C++ project, so that they can be compiled and linked just like any other C++ code. On top of that, the settings compiler also spits out an HLSL file that declares a constant buffer containing all of the relevent settings (a setting can opt-out of the constant buffer if it wants by using an attribute). The C++ files then have code generated for creating a matching constant buffer resource, filling it out with the setting values once a frame, and binding it to all shader stages at the appropriate slot. This means that all a shader needs to do is #include the file, and it can use the setting. Here&rsquo;s a diagram that shows the basic setup for the whole thing:</p>
<p><a href="/images/converted/sample-framework-updates/settingscompiler1.png"><img src="/images/converted/sample-framework-updates/settingscompiler1.png" alt="SettingsCompiler"></a></p>
<p>This actually works out really nicely in Visual Studio: you just hit F7 and everything builds in the right order. The settings compiler will gather errors from the C# compiler and output them to stdout, which means that if you have a syntax error it gets reported to the VS output window just as if you were compiling it normally through a C# project. MSBuild will even track the timestamp on AppSettings.cs, and won&rsquo;t run the settings compiler unless it&rsquo;s newer than the timestamp on AppSettings.h, AppSettings.cpp or AppSettings.hlsl. Sure it&rsquo;s a really complicated and over-engineered way of handling my problem, but it works and I had fun doing it.</p>
<h2 id="future-work">Future Work</h2>
<p>I think that the next thing that I&rsquo;ll improve will be model loading. It&rsquo;s pretty limiting working with .sdkmesh, and I&rsquo;d like to be able to work with a wider variety of test scenes. Perhaps I&rsquo;ll integrate Assimp, or use it to make a simple converter to a custom format. I&rsquo;d also like to flesh out my SH math and shader code a bit, and add some more useful functionality.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://bartwronski.com/2014/04/10/c-net-graphics-framework/" title="">C#/.NET graphics framework | Bart Wronski</a> - <time datetime="2014-04-10 16:02:05">Apr 4, 2014</time></p>
<p>[…] “Sample Framework” by Matt Pettineo. It seems it wraps very well lots of common steps needed to set up simple DirectX 11 app and Matt adds new features from time to time. In the samples I tried it works pretty well and the code and structure are quite easy to follow. If you like coding in C++ this would be something I would look into first, however I wanted to have something done more in “scripting” style and that would be faster to use. (more about it later). […]</p>
<hr />
####
[morfeas]( "vaspoul@gmail.com") - <time datetime="2013-09-17 22:16:28">Sep 2, 2013</time>
<p>Nice work on the generated C++/Shader code. I think it&rsquo;s definitely the right thing to do, but why stop at just settings? It&rsquo;s a short leap to shader constants and textures (with meta data, grouping and other parameters for DX9/DX11/OGL). Then, as you say, you get consistent shader and C++ code, reflection on C++&hellip;all the good stuff! I&rsquo;ve gone with the standard C++ preprocessor myself but it was hard going at times.</p>
<hr />
####
[fenbf](http://www.bfilipek.com "joebaf@gmail.com") - <time datetime="2014-04-22 00:14:28">Apr 2, 2014</time>
<p>AntTweakBar is really great library. I hope you had no problems with the integration, it literally takes a minutes. Still it would be nice if the author of this library make some more updates :) For instance in the OpenGL version there was a bug when cursor changed: <a href="http://www.bfilipek.com/2013/11/anttweakbar-cursor-lag-workaround.htm">http://www.bfilipek.com/2013/11/anttweakbar-cursor-lag-workaround.htm</a></p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>A Sampling of Shadow Techniques</title>
			<link>https://therealmjp.github.io/posts/shadow-maps/</link>
			<pubDate>Wed, 11 Sep 2013 07:45:40 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/shadow-maps/</guid>
			<description>A little over a year ago I was looking to overhaul our shadow rendering at work in order to improve overall quality, as well as simplify the workflow for the lighting artists (tweaking biases all day isn&amp;rsquo;t fun for anybody). After doing yet another round of research into modern shadow mapping techniques, I decided to do what I usually do and starting working on sample project that I could use as a platform for experimentation and comparison.</description>
			<content:encoded><![CDATA[<p>A little over a year ago I was looking to overhaul our shadow rendering at work in order to improve overall quality, as well as simplify the workflow for the lighting artists (tweaking biases all day isn&rsquo;t fun for anybody). After doing yet another round of research into modern shadow mapping techniques, I decided to do what I usually do and starting working on sample project that I could use as a platform for experimentation and comparison. And so I did just that. I had always intended to put it on the blog, since I thought it was likely that other people would be evaluating some of the same techniques as they upgrade their engines for next-gen consoles. But I was a bit lazy about cleaning it up (there was a lot of code!), and it wasn&rsquo;t the most <em>exciting</em> thing that I was working on, so it sort-of fell by the wayside. Fast forward to a year later, and I found myself  looking for a testbed for some major changes that I was planning for the settings and UI portion of my sample framework. My shadows sample came to mind since it was chock-full of settings, and that lead me to finally clean it up and get it ready for sharing. So here it is: (and now on GitHub!)</p>
<p><a href="https://github.com/TheRealMJP/Shadows">https://github.com/TheRealMJP/Shadows</a></p>
<p>If you head to the <a href="https://github.com/TheRealMJP/Shadows/releases">releases page</a>, there&rsquo;s a zip file containing all of the code and content as well as a pre-compiled executable. Note that I&rsquo;ve fully switched over to VS 2012 now, so you&rsquo;ll need it to open the project and compile the code. If you don&rsquo;t have it installed, then you may need to download and install the <a href="http://www.microsoft.com/en-us/download/details.aspx?id=30679">VC++ 2012 Redistributable</a> in order to run the pre-compiled binary.</p>
<ul>
<li>
<p><em>Update 9/12/2013</em></p>
<ul>
<li><em>Fixed a bug with cascade stabilization behaving incorrectly for very small partitions</em></li>
<li><em>Restore previous min/max cascade depth when disabling Auto-Compute Depth Bounds</em></li>
<li><em>Thanks to Stephen Hill for pointing out the issues!</em></li>
</ul>
</li>
<li>
<p><em>Update 9/18/2013</em></p>
<ul>
<li><em>Ignacio Castaño was kind enough to share the PCF technique being used in The Witness,</em> <em>which he integrated into the sample under the name &ldquo;OptimizedPCF&rdquo;. Thanks Ignacio!</em></li>
</ul>
</li>
<li>
<p><em>Update 11/3/2013</em></p>
<ul>
<li><em>Fixed SettingsCompiler string formatting issues for non-US cultures</em></li>
</ul>
</li>
<li>
<p><em>Update 2/17/2015</em></p>
<ul>
<li><em>Fixed VSM/EVSM filtering shader that was broken with latest Nvidia drivers</em></li>
<li><em>Added Moment Shadow Mapping</em></li>
<li><em>Added notes about biasing VSM, EVSM, and MSM</em></li>
</ul>
</li>
<li>
<p><em>Update 1/23/2016</em></p>
<ul>
<li><em>Fixed incorrect maximum exponent for 16-bit EVSM (thank you Christoph!)</em></li>
<li><em>Compile the main pixel shader on-demand so that all shader permutations don&rsquo;t need to be compiled and loaded up-front</em></li>
<li><em>Uploaded the project to GitHub</em></li>
</ul>
</li>
<li>
<p><em>Update 2/26/2023</em></p>
<ul>
<li><em>Added the section about disabling Z clipping</em></li>
</ul>
</li>
</ul>
<p>The sample project is set up as a typical &ldquo;cascaded shadow map from a directional light&rdquo; scenario, in the same vein as the CascadedShadowMaps11 sample from the old DX SDK or the more recent <a href="http://visual-computing.intel-research.net/art/publications/sdsm/">Sample Distribution Shadow Maps sample</a> from Intel.  In fact I even use the same Powerplant scene from both samples, although I also added in human-sized model so that you can get a rough idea of how well the shadowing looks on characters (which can be fairly important if you want your shadows to look good in cutscenes without having to go crazy with character-specific lights and shadows). The basic cascade rendering and setup is pretty similar to the Intel sample: a single &ldquo;global&rdquo; shadow matrix is created every frame based on the light direction and current camera position/orientation, using an orthographic projection with width, height, and depth equal to 1.0. Then for each cascade a projection is created that&rsquo;s fit to the cascade, which is used for rendering geometry to the shadow map. For sampling the cascade, the projection is described as a 3D scale and offset that&rsquo;s applied to the UV space of the &ldquo;global&rdquo; shadow projection. That way you just use one matrix in the shader to calculate shadow map UV coordinates + depth, and then apply the scale and offset to compute the UV coordinates + depth for the cascade that you want to sample. Unlike the Intel sample I didn&rsquo;t use any kind of deferred rendering, so I decided to just fix the number of cascades at 4 instead of making it tweakable at runtime.</p>
<h2 id="cascade-optimization">Cascade Optimization</h2>
<p>Optimizing how you fit your cascades to your scene and current viewpoint is pretty crucial for reducing perspective aliasing and the artifacts that it causes. The old-school way of doing this for CSM is to chop up your entire visible depth range into N slices using some sort of partitioning scheme (logarithmic, linear, mixed, manual, etc.). Then for each slice of the view frustum, you&rsquo;d tightly fit an AABB to the slice  and use that as the parameters for your orthographic projection for that slice. This gives you the most optimal effective resolution for a given cascade partition, however with 2006-era shadow map sizes you still generally end up with an effective resolution that&rsquo;s pretty low relative to the screen resolution. Combine this with 2006-era filtering (2x2 PCF in a lot of cases), and you end up with quite a bit of aliasing. This aliasing was exceptionally bad, due to the fact that your cascade projections will translate and scale as your camera moves and rotates, which results in rasterization sample points changing from frame to frame as the camera moves. The end result was crawling shadow edges, even from static geometry. The most popular solution for this problem was to trade some effective shadow map resolution for stable sample points that  don&rsquo;t move from frame to frame. This was first proposed (to my knowledge) by Michal Valient in his ShaderX6 article entitled &ldquo;Stable Cascaded Shadow Maps&rdquo;. The basic idea is that instead of tightly mapping your orthographic projection to your cascade split, you map it in such a way that the projection won&rsquo;t change as the camera rotates. The way that Michal did it was to fit a sphere to the entire 3D frustum split and then fit the projection to that sphere, but you could do it any way that gives you a consistent projection size. To handle the translation issue, the projections are &ldquo;snapped&rdquo; to texel-sized increments so that you don&rsquo;t get sub-texel sample movement. This ends up working really well, provided that your cascade partitions never change (which means that changing your FOV or near/car clip planes can cause issues). In general the stability ends up being a net win despite the reduced effective shadow map resolution, however small features and dynamic objects end up suffering.</p>
<p>2 years ago Andrew Lauritzen gave a talk on what he called &ldquo;Sample Distribution Shadow Maps&rdquo;, and released the sample that I mentioned earlier. He proposed that instead of stabilizing the cascades, we could instead focus on reducing wasted resolution in the shadow map to a point where effective resolution is high enough to give us sub-pixel resolution when sampling the shadow map. If you can do that then you don&rsquo;t really need to worry about your projection changing frame to frame, provided that you use decent filtering when sampling the shadow map. The way that he proposed to do this was to analyze the depth buffer generated for the current viewpoint, and use it to automatically generate an optimal partitioning for the cascades. He tried a few complicated ways of achieving this that involved generating a depth histogram on the GPU, but also proposed a much more practical scheme that simply computed the min and max depth values. Once you have the min and max depth, you can very easily use that to clamp your cascades to the subset of your depth range that actually contains visible pixels. This might not sound like a big deal, but in practice it can give you <em>huge</em> improvements. The min Z in particular allows you to have a much more optimal partitioning, which you can get by using an &ldquo;ideal&rdquo; logarithmic partitioning scheme. The main downside is that you need to use the depth buffer, which puts you in the awful position of having the CPU dependent on results from the GPU if you want to do your shadow setup and scene submission on the CPU. In the sample code they simply stall and readback the reduction results, which isn&rsquo;t really optimal at all in terms of performance. When you do something like this the CPU ends up waiting around for the driver to kick off commands to the GPU and for the GPU to finish processing them, and then you get a stall on the GPU while it sits around and waits for the CPU to start kicking off commands again. You can potentially get around this by doing what you normally do for queries and such, and deferring the readback for one or more frames. That way the results are (hopefully) already ready for readback, and so you don&rsquo;t get any stalls. But this can cause you problems, since the cascades will trail a frame behind what&rsquo;s actually on screen. So for instance if the camera moves towards an object, the min Z may not be low enough to fit all of the pixels in the first cascade and you&rsquo;ll get artifacts. One potential workaround is to use the previous frame&rsquo;s camera parameters to try to predict what the depth of a pixel will be for the <em>next</em> frame based on the instantaneous linear velocity of the camera, so that when you retrieve your min/max depth a frame late it actually contains the correct results. I&rsquo;ve actually done this in the past (but not for this sample, I got lazy) and it works as long as long as the camera motion stays constant. However it won&rsquo;t handle moving objects unless you store the per-pixel velocity with regards to depth and factor that into your calculations. The ultimate solution is to do all of the setup and submission on the GPU, but I&rsquo;ll talk about that in detail later on.</p>
<p>These are the options implemented in the sample that affect cascade optimization:</p>
<ul>
<li><strong>Stabilize Cascades</strong> - enables cascade stabilization using the method from ShaderX6.</li>
<li><strong>Auto-Compute Depth Bounds</strong> - computes the min and max Z on the GPU, and uses it to tightly fit the cascades as in SDSM.</li>
<li><strong>Readback Latency</strong> - number of frames to wait for the depth reduction results</li>
<li><strong>Min Cascade Distance/Max Cascade Distance</strong> - manual min and max Z when Auto-Compute Depth Bounds isn&rsquo;t being used</li>
<li><strong>Partition Mode</strong> - can be Logarithmic, PSSM (mix between linear and logarithmic), or manual</li>
<li><strong>Split Distance 0/Split Distance 1/Split Distance 2/Split Distance 3</strong> - manual partition depths</li>
<li><strong>PSSM Lambda</strong> - mix between linear and logarithmic partitioning when PartitionMode is PSSM</li>
<li><strong>Visualize Cascades</strong> - colors pixels based on which cascade they select</li>
</ul>
<h2 id="shadow-filtering">Shadow Filtering</h2>
<p>Shadow map filtering is the other important aspect of reducing artifacts. Generally it&rsquo;s needed to reduce aliasing due to undersampling the geometry being rasterized into the shadow map, but it&rsquo;s also useful for cases where the shadow map itself is being undersampled by the pixel shader. The most common technique for a long time has been Percentage Closer Filtering (PCF), which basically amounts to sampling a normal shadow map, performing the shadow comparison, and then filtering the result of that comparison. Nvidia hardware has been able to do a 2x2 bilinear PCF kernel in hardware since&hellip;forever, and it&rsquo;s required of all DX10-capable hardware. Just about every PS3 game takes advantage of this feature, and Xbox 360 games would too if the GPU supported it. In general you see lots of 2x2 or 3x3 grid-shaped PCF kernels with either a box filter or a triangle filter. A few games (notably the Crysis games) use a randomized sample pattern with sample points located on a disc, which trades regular aliasing for noise. With DX11 hardware there&rsquo;s support for GatherCmp, which essentially gives you the results of 4 shadow comparisons performed for the relevant 2x2 group of texels. With this you can efficiently implement large (7x7 or even 9x9) filter kernels with minimal fetch instructions, and still use arbitrary filter kernels. In fact there was an article in GPU Pro called &ldquo;Fast Conventional Shadow Filtering&rdquo; by Holger Gruen, that did exactly this, and even provided source code. It can be stupidly fast&hellip;in my sample app going from 2x2 PCF to 7x7 PCF only adds about 0.4ms when rendering at 1920x1080 on my AMD 7950. For comparison, a normal grid sampling approach adds about 2-3ms in my sample app for the maximum level of filtering. The big disadvantage to the fixed kernel modes is that they rely on the compiler to unroll the loops, which make for some <em>sloooowwww</em> compile times. The sample app uses a compilation cache so you won&rsquo;t notice it if you just start it up, but without the cache you&rsquo;ll see it takes quite while due to the many shader permutations being used. For that reason I decided to stick with a single kernel shape (disc) rather than using all of the shapes from the GPU Pro article, since compilation times were already bad enough.</p>
<p>So far the only real competitor to gain any traction in games is Variance Shadow Maps (VSM). I won&rsquo;t go deep into the specifics since the <a href="http://www.punkuser.net/vsm/">original paper</a> and <a href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch08.html">GPU Gems article</a> do a great job of explaining it. But the basic gist is that you work in terms of the mean and variance of a distribution of depth values at a certain texel, and then use use that distribution to estimate the probability of a pixel being in shadow. The end result is that you gain the ability to filter the shadow map without having to perform a comparison, which means that you can use hardware filtering (including mipmaps, anistropy or even MSAA) and that you can pre-filter the shadow map with standard &ldquo;full-screen&rdquo; blur pass. Another important aspect is that you generally don&rsquo;t suffer from the same biasing issues that you do with PCF. There&rsquo;s some issues of performance and memory, since you now need to store 2 high-precision values in your shadow map instead of just 1. But in general the biggest problem is the light bleeding, which occurs when there&rsquo;s a large depth range between occluders and receivers. Lauritzen attempted to address this a few years later by applying an exponential warp to the depth values stored in the shadow map, and performing the filtering in log space. It&rsquo;s generally quite effective, but it requires high-precision floating point storage to accommodate the warping. For maximum quality he also proposed storing a negative term, which requires an extra 2 components in the shadow map. In total that makes for 4x FP32 components per texel, which is definitely not light in terms of bandwidth! However you could say that it arguably produces the highest-quality results, and it does so without having to muck around with biasing. This is especially true when pre-filtering, MSAA, anisotropic filtering, and mipmaps are all used, although each of those brings about additional cost. To provide some real numbers, using EVSM4 with 2048x2048 cascades, 4xMSAA, mipmaps, 8xAF, and highest-level filtering (9 texels wide for the first cascade), adds about 11.5ms relative to a 7x7  fixed PCF kernel. A more reasonable approach would be to go with 1024x1024 shadow maps with 4xMSAA, which is around 3ms slower than the PCF version.</p>
<p>These are the shadow filtering modes that are implemented:</p>
<ul>
<li><strong>FixedSizePCF</strong> - optimized GatherCmp PCF with disc-shaped kernel</li>
<li><strong>GridPCF</strong> - manual grid-based PCF sampling using NxN samples</li>
<li><strong>RandomDiscPCF</strong>  - randomized samples on a Poisson disc, with optional per-pixel randomized rotation</li>
<li><strong>OptimizedPCF</strong> - similar to FixedSizePCF, but uses bilinear PCF samples to implement a uniform filter kernel.</li>
<li><strong>VSM</strong> - variance shadow maps</li>
<li><strong>EVSM2</strong> - exponential variance shadow maps, positive term only</li>
<li><strong>EVSM4</strong> - exponential variance shadow maps, both positive and negative terms</li>
<li><strong>MSM Hamburger</strong> - <a href="http://cg.cs.uni-bonn.de/en/publications/paper-details/peters-2015-msm/">moment shadow mapping</a>, using the &ldquo;Hamburger 4MSM&rdquo; technique from the paper.</li>
<li><strong>MSM Hausdorff</strong> - moment shadow mapping, using the &ldquo;Hausdorff 4MSM&rdquo; technique from the paper</li>
</ul>
<p>Here&rsquo;s the options available in my sample related to filtering:</p>
<ul>
<li><strong>Shadow Mode</strong> - the shadow filtering mode, can be one of the above values</li>
<li><strong>Fixed Filter Size</strong> - the kernel width in texels for the FixedSizePCF mode, can be 2x2 though 9x9</li>
<li><strong>Filter Size</strong> - the kernel width in fractional world space units for all other filtering modes. For the VSM modes, it&rsquo;s used in a pre-filtering pass.</li>
<li><strong>Filter Across Cascades</strong> - blends between two cascade results at cascade boundaries to hide the transition</li>
<li><strong>Num Disc Samples</strong> - number of samples to use for RandomDiscPCF mode</li>
<li><strong>Randomize Disc Offsets</strong> - if enabled, applies a per-pixel randomized rotation to the disc samples</li>
<li><strong>Shadow MSAA</strong> - MSAA level to use for VSM, EVSM, and MSM modes</li>
<li><strong>VSM/MSM Format</strong> - the precision to use for VSM, EVSM, and MSM shadow maps. Can be 16bit or 32bit . For VSM the textures will use a UNORM format, for EVSM they will be FLOAT. For the MSM 16-bit version, the optimized quantization scheme from the paper is used to store the data in UNORM texture.</li>
<li><strong>Shadow Anisotropy</strong> - anisotropic filtering level to use for VSM, EVSM, and MSM</li>
<li><strong>Enable Shadow Mips</strong> - enables mipmap generation and sampling for VSM, EVSM, and MSM</li>
<li><strong>Positive Exponent</strong>/<strong>Negative Exponent</strong> - the exponential warping factors for the positive and negative components of EVSM</li>
<li><strong>Light Bleeding Reduction</strong> - reduces light bleeding for VSM/EVSM/MSM, but results in over-darkening</li>
</ul>
<p>In general I try to keep the filtering kernel fixed in world space across each cascade by adjusting the kernel width based on the cascade size. The one exception is the FixedSizePCF mode, which uses the same size kernel for all cascades. I did this because I didn&rsquo;t think that branching over the fixed kernels would be a great idea. Matching the filter kernel for each cascade is nice because it helps hide the seams at cascade transitions, which means you don&rsquo;t have to try to filter across adjacent cascades in order to hide them. It also means that you don&rsquo;t have to use wider kernels on more distant pixels, although this can sometimes lead to visible aliasing on distant surfaces.</p>
<p>I didn&rsquo;t put a whole lot of effort into the &ldquo;RandomDiscPCF&rdquo; mode, so it doesn&rsquo;t produce optimal results. The randomization is done per-pixel, which isn&rsquo;t great since you can clearly see the random pattern tiling over the screen as the camera moves. For a better comparison you would probably want to do something similar to what Crytek does, and tile a (stable) pattern over each cascade in shadowmap space.</p>
<h2 id="biasing">Biasing</h2>
<p>When using conventional PCF filtering, biasing is essential for avoiding &ldquo;shadow acne&rdquo; artifacts. Unfortunately, it&rsquo;s usually pretty tricky to get it right across different scenes and lighting scenarios. Just a bit too much bias will end up killing shadows entirely for small features, which can cause characters too look very bad. My sample app exposes 3 kinds of biasing: manual depth offset, manual normal-based offset, and automatic depth offset based on receiver slope. The manual depth offset, called simply <strong>Bias</strong> in the UI, simply subtracts a value from the pixel depth used to compare against the shadow map depth. Since the shadow map depth is a [0, 1] value that&rsquo;s normalized to the depth range of the cascade, the bias value represents a variable size in world space for each cascade. The normal-based offset, called <strong>Offset Scale</strong> in the UI, is based on &ldquo;Normal Offset Shadows&rdquo;, which was a poster presented at GDC 2011. The basic idea is you actually create a new virtual shadow receiver position by offsetting from the actual pixel position in the direction of the normal. The trick is that you offset more when the surface normal is more perpendicular to the light direction. Angelo Pesce has a hand-drawn diagram explaining the same basic premise on <a href="http://c0de517e.blogspot.com/2011/05/shadowmap-bias-notes.html">his blog</a>, if you&rsquo;re having trouble picturing it.. This technique can actually produce decent results, especially given how cheap it is. However since you&rsquo;re offsetting the receiver position, you actually &ldquo;move&rdquo; the shadows a bit which is a bit weird to see as you tweak the value. Since the offset is a world-space distance, in my sample I scale it based on the depth range of the cascade in order to make it consistent across cascade boundaries. If you want to try using it, I recommend starting with a small manual bias of 0.0015 or so and then slowly increasing the Offset Scale to about 1.0 or 2.0. Finally we have the automatic solution, where we attempt to compute the &ldquo;perfect&rdquo; bias amount based on the slope of the receiver. This setting is called <strong>Use Receiver Plane Depth Bias</strong> in the sample. To determine the slope, screen-space derivatives are used in the pixel shader. When it works, it&rsquo;s fantastic. However it will still run into degenerate cases where it can produce unpredictable results, which is something that often happens when working with screen-space derivatives.</p>
<p>There are also separate biasing parameters for VSM and MSM techniques. The &ldquo;VSM Bias&rdquo; affects VSM and EVSM, while &ldquo;MSM Depth Bias&rdquo; and &ldquo;MSM Moment Bias&rdquo; are used for MSM. For VSM and 32-bit EVSM, a bias value of 0.01 (which corresponds to an actual value of 0.0001) is sufficent. However for 16-bit EVSM a bias of up to 0.5 (0.005) is required to alleviate precision issues. For only the moment bias is particularly relevent. This value needs to be at least 0.001-0.003 (0.000001-0.000003) for 32-bit modes, while the quantized 16-bit mode requires a higher bias of around 0.03 (0.00003). Note that for both EVSM and MSM increasing the bias can also increase the amount of visible light bleeding.</p>
<h2 id="disabling-z-clipping">Disabling Z Clipping</h2>
<p>Shadow maps for directional lights have an interesting problem due to the light source being considered infinitely far away: this means that occluders can also cast shadows for an infinite distance. For a typical cascaded shadow map implementation each cascade will rasterize shadows using an orthgraphic frustum (which is an OBB in world space) that&rsquo;s aligned with the light direction, and where the far clip plane extends just enough to enclose the primary view frustum. But what about the near clip plane for rasterizing that cascade? We can&rsquo;t just pull that infinitely far back towards the directional light, and even if we could we wouldn&rsquo;t want to because we would have terrible depth buffer precision. Meanwhile if we just make the near clip enclose the main view frustum like we did for the far clip plane, we would clip away occluders that are not currently visible but <em>do</em> cast shadows onto visible surfaces. It turns out there&rsquo;s a trick we can use for this specific case: we can simply disable Z clipping, in which case triangles in front of the near clip plane get clamped such that Z = 0. This basically &ldquo;flattens&rdquo; the mesh onto that plane, which is also sometimes called &ldquo;pancaking&rdquo; the occluder. Disabling this clipping is as simple as <a href="https://github.com/TheRealMJP/Shadows/blob/master/Shadows/MeshRenderer.cpp#L570">setting a flag to 0 in the rasterizer state</a>:</p>
<pre tabindex="0"><code>D3D11_RASTERIZER_DESC rsDesc = RasterizerStates::NoCullDesc();
rsDesc.DepthClipEnable = FALSE;
DXCall(device-&gt;CreateRasterizerState(&amp;rsDesc, &amp;shadowRSState));
</code></pre><p>If you&rsquo;re having trouble picturing how this works, I made a simple diagram showing what happens:</p>
<p><img src="/images/shadow-maps/shadow-cascade-pancake.png" alt=""></p>
<p>In this case we have a simple ground plane that&rsquo;s visible to the main camera, and two trees that act as shadow casters. The rightmost tree is straightforward: it is within the main view frustum (in blue), and so it also sits comfortably within the single cascade projection for the directional light shadow map (in red). However the leftmost tree has the problem we mentioned previously: it is not visible to the main camera and is outside of the cascade projection, however it should still cast a visible shadow. With depth clipping disabled, that leftmost tree gets clamped to the cascade projection&rsquo;s near clip plane, which is shown as the thick green line below the tree. These depth values of 0 still end up being less than the shadow depth value of any surfaces within the view frustum, and therefore they still end up being shadowed correctly.</p>
<h2 id="gpu-driven-cascade-setup-and-scene-submission">GPU-Driven Cascade Setup and Scene Submission</h2>
<p>This is a topic that&rsquo;s both fun, and really frustrating. It&rsquo;s fun because you can really start to exploit the flexibility of DX11-class GPU&rsquo;s, and begin to break free of the old &ldquo;CPU spoon-feeds the GPU&rdquo; model that we&rsquo;ve been using for so long now. It&rsquo;s also frustrating because the API&rsquo;s still hold us back a quite a bit in terms of letting the GPU generate its own commands.  Either way it&rsquo;s something I see people talk about but not a lot of people actually doing it, so I thought I&rsquo;d give it a try for this sample. There&rsquo;s actually 2 reasons to try something like this. The first is that if you can offload enough work to the GPU, you can avoid the heavy CPU overhead of frustum culling and drawing and/or batching lots and lots of meshes. The second is that it lets you do SDSM-style cascade optimization based on the depth buffer without having to read back values from the GPU to the CPU, which is always a painful way of doing things.</p>
<p>The obvious path to implementing GPU scene submission would be to make use of DrawInstancedIndirect/DrawIndexedInstancedIndirect. These API&rsquo;s are fairly simple to use: you simply write the parameters to a buffer (with the parameters being the same ones that you would normally pass on the CPU for the non-indirect version), and then on the CPU you specify that buffer when calling the appropriate function. Since instance count is one of the parameters, you can implement culling on the GPU my setting the instance count to 0 when a mesh shouldn&rsquo;t be rendered. However it turns out that this isn&rsquo;t really useful, since you still need to go through the overhead of submitting draw calls and setting associated state on the CPU. In fact the situation is worse than doing it all on the CPU, since you have to submit each draw call even if it will be frustum culled.</p>
<p>Instead of using indirect draws, I decided to make a simple GPU-based batching system. Shadow map rendering is inherently more batchable than normal rendering, since you typically don&rsquo;t need to worry about having specialized shaders or binding lots of textures when you&rsquo;re only rendering depth. In my sample I take advantage of this by using a compute shader to generate one great big index buffer based on the results of a frustum culling pass, which can then be rendered in a single draw call. It&rsquo;s really very simple: during initialization I generate a buffer containing all vertex positions, a buffer containing all indices (offset to reflect the vertex position in the combined position buffer), and a structured buffer containing the parameters for every draw call (index start, num indices, and a bounding sphere). When it&rsquo;s time to batch, I run a compute shader with one thread per draw call that culls the bounding sphere against the view frustum. If it passes, the thread then &ldquo;allocates&rdquo; some space in the output index buffer by performing an atomic add on a value in a RWByteAddressBuffer containing the total number of culled indices that will be present in output index buffer (note that on my 7950 you should be able to just do the atomic in GDS like you would for an append buffer, but unfortunately in HLSL you can only increment by 1 using IncrementCounter()). I also append the draw call data to an append buffer for use in the next pass:</p>
<pre tabindex="0"><code>const uint drawIdx = TGSize * GroupID.x + ThreadIndex;
if(drawIdx &gt;= NumDrawCalls)
    return;

DrawCall drawCall = DrawCalls[drawIdx];
if(FrustumCull(drawCall))
{
    CulledDraw culledDraw;
    culledDraw.SrcIndexStart = drawCall.StartIndex;
    culledDraw.NumIndices = drawCall.NumIndices;
    DrawArgsBuffer.InterlockedAdd(0, drawCall.NumIndices,
                                  culledDraw.DstIndexStart);

    CulledDrawsOutput.Append(culledDraw);
}
</code></pre><p>Once that&rsquo;s completed, I then launch another compute shader that uses 1 thread group per draw call to copy all of the indices from the source index buffer to the final output index buffer that will be used for rendering. This shader is also very simple: it simply looks up the draw call info from the append buffer that tells it which indices to copy and where they should be copied, and then loops enough times for all indices to be copied in parallel by the different threads inside the thread group. The final result is an index buffer containing all of the culled draw calls, ready to be drawn in a single draw call:</p>
<pre tabindex="0"><code>const uint drawIdx = GroupID.x;
CulledDraw drawCall = CulledDraws[drawIdx];
for(uint currIdx = ThreadIndex; currIdx &lt; drawCall.NumIndices; currIdx += TGSize)
{
    const uint srcIdx = currIdx + drawCall.SrcIndexStart;
    const uint dstIdx = currIdx + drawCall.DstIndexStart;

    CulledIndices[dstIdx] = Indices[srcIdx];

}
</code></pre><p>In order to avoid the min/max depth readback, I also had to port my cascade setup code to a compute shader so that the entire process could remain on the GPU. I was surprised to find that this was actually quite a bit more difficult than writing the batching system. Porting arbitrary C++ code to HLSL can be somewhat tedious, due to the various limitations of the language. I also ran into a rather ugly bug in the HLSL compiler where it kept  trying to keep matrices in column-major order no matter what code I wrote and what declarations I used, which I suppose it tries to do as an optimization. However this really messed me up when I tried to write the matrix into a structured buffer, and expected it to be row-major. My advice for the future: if you need to write a matrix from a compute shader, just use a <code>StructuredBuffer&lt;float4&gt;</code> and write out the rows manually. Ultimately after much hair-pulling I got it to work, and finally achieved my goal of 0-latency depth reduction with no CPU readback!</p>
<p>In terms of performance, the GPU-driven path comes in about 0.8ms slower than the CPU version when the CPU uses 1 frame of latency for reading back the min/max depth results. I&rsquo;m not entirely sure why it&rsquo;s that much slower, although I haven&rsquo;t spend much time trying to profile the batching shaders or the cascade setup shader. I also wouldn&rsquo;t be surprised if the actual mesh rendering ends up being a bit slower, since I use 32-bit indices when batching on the GPU as opposed to 16-bit when submitting with the CPU. However when using the 0 frames of latency for the CPU readback, the GPU version turns it around and comes in a full millsecond faster on my PC. Obviously it makes sense that any additional GPU overhead would make up for the stall that occurs on the CPU and GPU when having to immediately read back data. One thing that I&rsquo;ll point out is that the GPU version is quite a bit slower than the CPU version when VSM&rsquo;s are used and pre-filtering is enabled. This is because the CPU path chooses an optimized blur shader based on the filter width for a cascade, and early outs of the blur process if no filtering is required. For the GPU path I got lazy and just used one blur shader that uses a dynamic loop, and it always runs the blur passes even if no filtering is requested. There&rsquo;s no technical reason why you couldn&rsquo;t do it the same way as the CPU path with enough motivation and DrawIndirects.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22Strikemedown1981@gmail.com%22">Jamie Hayes</a> - <time datetime="2013-09-13 19:41:42">Sep 5, 2013</time></p>
<p>With regard to SDSM, I have so many questions! With a great big vertex buffer, how would you manage skinning and world space transformations? I&rsquo;m guessing you would need to generate one great big matrix buffer and eat the cost of having blend weights and indices on everything. Dealing with alpha test seems pretty daunting, maybe impossible? Also, handling objects getting added and removed as you would see in a normal game seems like it would be a headache.</p>
<hr />
####
[An Idiot]( "superidiot@idiots.com") - <time datetime="2014-02-23 15:16:43">Feb 0, 2014</time>
<p>I know I&rsquo;m an idiot but there is something very simple I cannot understand about SDSM: Before writing to the view depth buffer, usually one cleans it to a max value (ie 1) so samples lower than that will be rendered. When doing the min/max reduction&hellip; how the max z is found? Shouldn&rsquo;t it return always 1 if a sample is left uncovered/ showing the background leaving a 1 in the depth buffer? Sorry for my stupidity and thanks</p>
<hr />
####
[An Idiot]( "idiot@idiotmail.com") - <time datetime="2014-03-11 18:11:02">Mar 2, 2014</time>
<p>Thanks a lot Rocko! Now I have SDEVSM working in my game :)</p>
<hr />
####
[An Idiot]( "idiot@ranersintarsioent.com") - <time datetime="2014-02-24 05:04:59">Feb 1, 2014</time>
<p>Thanks! I was thinking of something like that. But what confused me is that I didn&rsquo;t saw that in the PS code so I thought that maybe there was a way to do it without a branch. Now, giving it a better look, I can&rsquo;t see also the four fetches one should do. I can only assume that &ldquo;float4 depthSamples = DepthMap.GatherRed(LinearClampSampler, TexCoord);&rdquo; someway does the four fetches and it also ignores values == 1.0? HLSL is quite a mystery for a GLSL user. Thanks for your quick reply.</p>
<hr />
####
[A sampling of shadow techniques | Light is beautiful](http://lousodrome.net/blog/light/2013/09/15/a-sampling-of-shadow-techniques/ "") - <time datetime="2013-09-15 03:00:40">Sep 0, 2013</time>
<p>[…] On his blog, Matt Pettineo writes about his experiments on cascaded shadow maps, including cascade optimization, shadow map filtering and GPU scene submission: A Sampling of Shadow Techniques. […]</p>
<hr />
####
[Alundra]( "segura.mikael@orange.fr") - <time datetime="2014-04-07 04:27:25">Apr 1, 2014</time>
<p>Hi MJP, SDSM begin to be an old technique, but it&rsquo;s the lastest technique found for shadow (2011). Is SDSM too heavy to use or THIS IS the technique to use nowadays ?</p>
<hr />
####
[Alundra]( "segura.mikael@orange.fr") - <time datetime="2014-04-07 04:35:08">Apr 1, 2014</time>
<p>Another thing is : Why ReadBack is faster ?! I don&rsquo;t get it. I have 5 FPS more on your sample disabling GPU submission, normally FPS is lower using a read-back, why this 5 FPS wasted ?!</p>
<hr />
####
[dishwasher](http://none "dishwasherblood@gmail.com") - <time datetime="2013-11-03 20:30:13">Nov 0, 2013</time>
<p>Now it works fine! Thank you very much for such a quick help!</p>
<hr />
####
[Development resources](http://www.sgc2000.co.uk/2014/03/development-resources/ "") - <time datetime="2014-12-22 06:21:49">Dec 1, 2014</time>
<p>[…] A sampling of shadow techniques […]</p>
<hr />
####
[dishwasher](http://none "dishwasherblood@gmail.com") - <time datetime="2013-11-03 07:25:07">Nov 0, 2013</time>
<p>Actually it seems something is wrong with it, here&rsquo;s an example: LightColor.Initialize(tweakBar, &ldquo;LightColor&rdquo;, &ldquo;SceneControls&rdquo;, &ldquo;Light Color&rdquo;, &ldquo;The color of the light&rdquo;, Float3(10, 8, 5), true, 0.0f, 20.0f, 0,1.0f); Look at the last parameter - 0,1.0f. All the errors ale like that: MinCascadeDistance.Initialize(tweakBar, &ldquo;MinCascadeDistance&rdquo;, &ldquo;Shadows&rdquo;, &ldquo;Min Cascade Distance&rdquo;, &ldquo;The closest depth that is covered by the shadow cascades&rdquo;, 0.0f, 0.0f, 0,1.0f, 0,001.0f); 0,1.0f, 0,001.0f? It looks like something&rsquo;s wrong with generating floats from attrubute strings?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2013-11-03 17:04:52">Nov 0, 2013</time>
<p>Okay, I think I figured out the problem. I didn&rsquo;t specify the culture when outputting strings for float parameters, and it looks like it was using commas instead of decimal points for your locale. Clearly I am not an experienced C# programmer! I uploaded a new zip with some fixes, so could you download that and give it a try?</p>
<hr />
####
[Sample Framework Updates | The Danger Zone](http://mynameismjp.wordpress.com/2013/09/16/sample-framework-updates/ "") - <time datetime="2013-09-16 21:54:05">Sep 1, 2013</time>
<p>[…] may have noticed that my latest sample now has a proper UI instead of the homegrown sliders and keyboard toggles that I was using in my […]</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2013-09-14 17:57:50">Sep 6, 2013</time>
<p>Thank you for the links Christoph! I actually wasn&rsquo;t familiar with that Nvidia extension, and I hadn&rsquo;t seen your presentation either. Sounds very useful indeed!</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2013-09-14 17:51:54">Sep 6, 2013</time>
<p>Hi Jamie! What I did in the demo is definitely not production-ready, and I&rsquo;m not sure if I would even do it exactly the same way if I were going to try to ship with it. For this sample I handled the character by repeating the entire batching process for its meshes, and if you were to do it that way you could handle skinning as long as you also had the bone weight/indices in a combined vertex buffer as well. Or if you could set up system for pre-skinning vertices and writing them out (either via stream-out, or better yet by directly writing to the buffer from the vertex shader) then it would definitely make things easier. In fact if you did that, you could probably stream out vertices from multiple skinned meshes into a the same batch, and then run the culling/batching process on lots of skinned objects simultaneously. Alpha-test would definitely be pretty tricky, although you could always fall back to a DrawIndirect-based approach for anything that&rsquo;s difficult to batch.</p>
<hr />
####
[dishwasher](http://none "dishwasherblood@gmail.com") - <time datetime="2013-11-02 14:20:46">Nov 6, 2013</time>
<p>Sample doesn&rsquo;t build in VS2013: 1&gt; AppSettings.cpp 1&gt;AppSettings.cpp(129): error C2660: &lsquo;SampleFramework11::ColorSetting::Initialize&rsquo; : function does not take 11 arguments 1&gt;AppSettings.cpp(147): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 10 arguments 1&gt;AppSettings.cpp(174): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 11 arguments 1&gt;AppSettings.cpp(177): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 10 arguments 1&gt;AppSettings.cpp(183): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 11 arguments 1&gt;AppSettings.cpp(186): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 11 arguments 1&gt;AppSettings.cpp(189): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 11 arguments 1&gt;AppSettings.cpp(192): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 10 arguments 1&gt;AppSettings.cpp(195): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 10 arguments 1&gt;AppSettings.cpp(201): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 12 arguments 1&gt;AppSettings.cpp(204): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 10 arguments 1&gt;AppSettings.cpp(219): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 10 arguments 1&gt;AppSettings.cpp(222): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 10 arguments 1&gt;AppSettings.cpp(225): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 10 arguments 1&gt;AppSettings.cpp(228): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 10 arguments 1&gt;AppSettings.cpp(231): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 10 arguments 1&gt;AppSettings.cpp(234): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 13 arguments 1&gt;AppSettings.cpp(237): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 12 arguments 1&gt;AppSettings.cpp(240): error C2660: &lsquo;SampleFramework11::FloatSetting::Initialize&rsquo; : function does not take 11 arguments SettingsCompiler and SettingsCompilerAttributes compiled successfully.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2013-11-02 16:48:42">Nov 6, 2013</time>
<p>I just installed VS 2013 on my PC, and the sample builds fine for me. I tried both upgrading the project and not upgrading it, and it compiles either way. Does the code generated in AppSettings.cpp look okay?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2013-09-11 14:38:09">Sep 3, 2013</time>
<p>Hi Sam, We&rsquo;ve been using EVSM and SDSM with similar cascade techniques for some time now in our game, although we have a lot of little tweaks, fixes, and platform-specific optimizations to make everything work in a production setting. We&rsquo;re not currently doing any GPU-driven submission, but I have plans to implement something specific to PS4 that&rsquo;s even more optimal than what I did in this sample. -Matt</p>
<hr />
####
[Christoph Kubisch](http://gravatar.com/crazybutcher "crazybutcher@luxinia.de") - <time datetime="2013-09-14 01:43:23">Sep 6, 2013</time>
<p>Hi thanks for the article! For the GPU-driven submission OpenGL provides multi-draw-indirect which allows for quite efficent read-back less culling. Been doing research on this topic at NVIDIA, some results were shown at thise years GTC <a href="http://on-demand.gputechconf.com/gtc/2013/presentations/S3032-Advanced-Scenegraph-Rendering-Pipeline.pdf">http://on-demand.gputechconf.com/gtc/2013/presentations/S3032-Advanced-Scenegraph-Rendering-Pipeline.pdf</a> from slide 35 on.There is <a href="http://www.opengl.org/registry/specs/NV/bindless">http://www.opengl.org/registry/specs/NV/bindless</a>_multi_draw_indirect.txt which allows to source from arbitrary buffer locations, as well as <a href="http://www.opengl.org/registry/specs/ARB/indirect">http://www.opengl.org/registry/specs/ARB/indirect</a>_parameters.txt which means you can batch the jobs efficiently.</p>
<hr />
####
[Rim]( "remigius@netforge.nl") - <time datetime="2013-09-11 05:18:16">Sep 3, 2013</time>
<p>Thanks for the treatise, Matt. Although it&rsquo;s been quite a while since I touched DirectX, I still found it educational to read up on the advances in shadowing and if nothing else, the article got me amped to finally give those fancy compute shaders a try for non-graphics stuffs. Cheers!</p>
<hr />
####
[Sam Martin]( "sam@palgorithm.co.uk") - <time datetime="2013-09-11 03:45:01">Sep 3, 2013</time>
<p>Hi Matt, This is a great summary of the state of the art and the gpu-driven cascade rendering is really good to see. Are you guys intending on using this approach in production? Cheers, Sam</p>
<hr />
####
[Shadow Mapping Summary &#8211; Part 1](http://the-witness.net/news/2013/09/shadow-mapping-summary-part-1/ "") - <time datetime="2013-09-23 17:23:18">Sep 1, 2013</time>
<p>[…] not going to try to address that here, but just as I was writting this Matt Pettineo released a good overview of some of the different techniques available that is a good starting point. Instead, I’ll […]</p>
<hr />
####
[fenbf](http://www.bfilipek.com "joebaf@gmail.com") - <time datetime="2014-04-19 04:04:22">Apr 6, 2014</time>
<p>Impressive! I made similar app for my master thesis: I&rsquo;ve used openGL and tested CSV vs standard shadow map techniques + some additional improvements.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2014-02-23 23:01:51">Feb 0, 2014</time>
<p>I just ignore any z samples that aren&rsquo;t &lt; 1.0. This keeps you from considering background/sky pixels when computing the max depth, as long as you force your skybox to render at the far clip plane.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2014-02-24 14:44:59">Feb 1, 2014</time>
<p>GatherRed just returns the the red component of the 4 samples that would be used for bilinear filtering, so basically it gives you a 2x2 grid of depth samples in this case. That file is probably a little confusing because there&rsquo;s actually 4 shaders in there. The two that actually get used in the sample are DepthReductionInitialCS and DepthReductionCS, which are the compute shader versions. The &ldquo;Initial&rdquo; shader is the one that&rsquo;s run first, and actually samples the depth buffer. When MSAA is enabled it loads all N subsamples for a given pixel, and when MSAA is not enabled it just grabs the 1 sample. In both cases it does a check of &ldquo;if(depthSample &lt; 1.0f)&rdquo; to decide if it should use a given depth sample, which is on lines 149 and 161. Taking a look at the code, it looks like the pixel shader version doesn&rsquo;t do the check if MSAA is disabled. This is definitely a bug, which is probably why were you confused. That code path never actually gets taken in the sample (I always had MSAA enabled), so I never noticed it. Sorry about that!</p>
<hr />
####
[Shadow Sample Update | The Danger Zone](https://mynameismjp.wordpress.com/2015/02/18/shadow-sample-update/ "") - <time datetime="2015-02-18 10:00:21">Feb 3, 2015</time>
<p>[…] the implementation of MSM is very straightforward, and so I decided to just integrate it into my old shadows sample. I updated the corresponding blog post and re-uploaded the binary + source, so if you’d like […]</p>
<hr />
####
[An Idiot]( "idiot@idiotmail.com") - <time datetime="2014-03-12 15:42:17">Mar 3, 2014</time>
<p>Just another comment to deepen my undestanding: When you compute the frustum slices, you take the linear Z min and maxes for the slice and then multiply them with the frustum rays between near and far corners to get the slice corners. However, the Z min and maxes work for for a ray at the center on the frustum and using them directly on the corner gives different planes, that for example, if using a pretty tight near to the geometry may leave parts out. What I did is to get the central ray of the frustum normalized and then use it to project the depths to the corners. ie: nearCornerRay = cornerRay * dot(normalize(cornerRay),centralRay*prevSplitDist); farCornerRay = cornerRay * dot(normalize(cornerRay),centralRay*splitDist); That fixed my problems, but I have no confidence in that I&rsquo;m doing the correct thing. Thanks!</p>
<hr />
####
[OpenGL Cascaded Shadow Maps | Make Games, Not War](https://johanmedestrom.wordpress.com/2016/03/18/opengl-cascaded-shadow-maps/ "") - <time datetime="2016-03-25 14:04:25">Mar 5, 2016</time>
<p>[…] I’d make a simple project in OpenGL for anyone interested. It is based on this example: <a href="https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/">https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/</a> which explains it in great detail. I’ll go through the relevant code and a download link […]</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-06-16 10:14:41">Jun 4, 2016</time>
<p>Thanks! The code for the OptimizedPCF path was generously provided by Ignacio Castaño, who didn&rsquo;t provide an implementation for a 9x9 filter. I had been meaning to write it myself, but never got around to it. :(</p>
<hr />
####
[Кирилл Грустнёв](https://plus.google.com/111128623163722713524 "simpetar@gmail.com") - <time datetime="2017-10-17 03:27:31">Oct 2, 2017</time>
<p>Hello, Matt. I do not know whom to ask, so i decided to ask you, as your articles are always very profound and you clarify details that no one clarifies :-) i&rsquo;m implementing now sdsm technique from Andrew Lauritzen article with evsm filtering using opengl 4.5. So i read almost everything about esm, vsm, and other *sm, but cannot understand some points. I also looked through your shadow sample and intel&rsquo;s also (i do not know direcx, really). So the questions: 1. When we used positive and negative exponent factors in evsm, why the first is 42 (this i understand), but the last is 5(this is completely obscured to me). 2. All the articles about shadow mapping say that you should use front face culling when rendering to the shadow map (this should decrease wrong selfshadowing and acne), but both you and andrew lauritzen use NoCull in your samples. Why? 3. In Andrew&rsquo;s sample he &ldquo;normalizes&rdquo; exponent factors with cascade ranges, to make the transition between cascades less noticeable. You do not do that. I have ranges from several meters to 10s of kilometers (flight simulator). Is there any point to do that? 4. When blurring evsm, does it make sense to blur with decreasing kernel size (for example, 7x blur for cascade 0, 5x for 1, 3x for 2 and no blur for the last. Is there any &ldquo;state of the art&rdquo; technique? 5. The same question with light space orientation relative to camera space. I have only sun direction (z-axis), so i can rotate the light space basis around it (xy axes), is there the &ldquo;right way&rdquo; to do that? stick it to camera &ldquo;up&rdquo; vector or to world up vector? 6. Is there any point to do some shear transformation to light space. Could it help to better utilize sm resolution, I didn&rsquo;t see any articles on. Now my lighspace is a box. 7. Everybody uses square shadow maps (1024x1024 for ex.). Is it possible to find better aspect knowing scene parameters. Thank you for your answer :-):-)</p>
<hr />
####
[Shadow Filtering For Pointlights | kosmonaut games](https://kosmonautblog.wordpress.com/2017/03/25/shadow-filtering-for-pointlights/ "") - <time datetime="2017-03-25 08:15:59">Mar 6, 2017</time>
<p>[…] Should you be looking for different shadow filtering algorithms, you have come to the wrong place – the right place would be here:https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/ […]</p>
<hr />
####
[]( "") - <time datetime="2016-06-15 15:37:49">Jun 3, 2016</time>
<p>Great work! I just curious: SampleShadowMapOptimizedPCF() in Mesh.hlsl does not contain branch for FilterSize_ == 9. Is it by design?</p>
<hr />
####
[Steve Smith]( "baboon_king@hotmail.com") - <time datetime="2019-02-02 17:47:40">Feb 6, 2019</time>
<p>This is awesome stuff. I&rsquo;m curious (and this has been driving me nuts for hours now) - but how does your mesh rendering shader have depth/w always biased from 0 to 1? I&rsquo;m trying to follow this in my own code (and even for example the DirectX cascaded shadow maps sample) and these values are *way* different. You&rsquo;re doing some magic here I can&rsquo;t figure out&hellip; help!</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2019-02-02 18:15:49">Feb 6, 2019</time>
<p>Hey Steve! Exactly which depth value are you referring to here? Are you talking the depth of the pixel from the light&rsquo;s point of view, that&rsquo;s used to compare against the shadow map value? In general the range of the result of when calculating z / w is a property of the projection matrix that you&rsquo;re using. In my sample the sun shadows are rasterized using an orthographic projection, where z is calculated as (viewSpaceZ - NearClip) / (FarClip - NearClip), and w is always just 1.0. So it naturally ends up in the [0, 1] range, since any other values would get clipped by the near/far clip planes. Does that answer your question? Feel free to let me know if I&rsquo;m misunderstanding.</p>
<hr />
####
[Steve Smith]( "baboon_king@hotmail.com") - <time datetime="2019-02-02 18:52:09">Feb 6, 2019</time>
<p>I did not explain that clearly, apologies. The shadow gen stuff makes sense (and I think I have that all figured out). On the rendering side, you have a super-simple vertex shader that spits out a value (DepthVS) from PositionCS.w. Side by side, my view projection and yours are almost identical, but my w value range appears to be in the order of my near/far clip range (so around 300). The only difference I see is that I&rsquo;m using non-transposed clipspace matrix (which works if you reverse the float4/matrix parameters in the transform mul) but I tried flipping that around and didn&rsquo;t see a difference.</p>
<hr />
####
[Steve Smith]( "baboon_king@hotmail.com") - <time datetime="2019-02-02 21:17:08">Feb 6, 2019</time>
<p>Oh dear lord. I was debugging visually, and completely forgot you had full HDR with tone-mapping (I had mine turned off). Hoo boy. Somebody needs more sleep.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2019-02-03 14:16:17">Feb 0, 2019</time>
<p>Ahh okay, that makes more sense! So yeah: the w component in that case is equivalent to the z component of the view-space vertex position. A perspective projection matrix will typically be setup with the fourth column equal to [0, 0, 1, 0], which ends up setting w = x * 0 + y * 0 + z * 1 + 1 * 0. So you would expect that to be somewhere in the range of [NearClip, FarClip], which is what you&rsquo;re seeing.</p>
<hr />
####
[Steve Smith]( "baboon_king@hotmail.com") - <time datetime="2019-02-03 16:04:09">Feb 0, 2019</time>
<p>Thanks. And thanks for the sample too, it&rsquo;s been incredibly useful. I&rsquo;ve not seen samples this well thought out and well executed since I had access to the old Microsoft ATG stuff. So - thank you.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>DX11.2 Tiled Resources</title>
			<link>https://therealmjp.github.io/posts/dx11-2-tiled-resources/</link>
			<pubDate>Sat, 07 Sep 2013 06:21:28 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/dx11-2-tiled-resources/</guid>
			<description>Tiled resources seems to be the big-ticket item for the upcoming DX11.2 update. While the online documentation has some information about the new functions added to the API, there&amp;rsquo;s currently no information about the two tiers of tiled resource functionality being offered. Fortunately there is a sample app available that provides some clues. After poking around a bit last night, these were the differences that I noticed:
TIER2 supports MIN and MAX texture sampling modes that return the min or max of 4 neighboring texels.</description>
			<content:encoded><![CDATA[<p><a href="http://msdn.microsoft.com/en-us/library/windows/desktop/dn312084%28v=vs.85%29.aspx#tile">Tiled resources</a> seems to be the big-ticket item for the upcoming DX11.2 update. While the online <a href="http://msdn.microsoft.com/en-us/library/windows/apps/bg182880.aspx#four">documentation</a> has some information about the new functions added to the API, there&rsquo;s currently no information about the <a href="http://msdn.microsoft.com/library/windows/apps/dn280435">two tiers</a> of tiled resource functionality being offered. Fortunately there is a <a href="https://tiledresources.codeplex.com/">sample app</a> available that provides some clues. After poking around a bit last night, these were the differences that I noticed:</p>
<ul>
<li>
<p>TIER2 supports MIN and MAX texture sampling modes that return the min or max of 4 neighboring texels. In the sample they use this when sampling a residency texture that tells the shader the highest-resolution mip level that can be used when sampling a particular tile. For TIER1 they emulate it with a Gather.</p>
</li>
<li>
<p>TIER1 doesn&rsquo;t support sampling from unmapped tiles, so you have to either avoid it in your shader or map all unloaded tiles to dummy tile data (the sample does the latter)</p>
</li>
<li>
<p>TIER1 doesn&rsquo;t support packed mips for texture arrays. From what I can gather, packed mips refers to packing multiple mips into a single tile.</p>
</li>
<li>
<p>TIER2 supports a new version of Texture2D.Sample that lets you clamp the mip level to a certain value. They use this to force the shader to sample from lower-resolution mip levels if the higher-resolution mip isn&rsquo;t currently resident in memory. For TIER1 they emulate this by computing what mip level would normally be used, comparing it with the mip level available in memory, and then falling back to SampleLevel if the mip level needs to be clamped. There&rsquo;s also another overload for Sample that returns a status variable that you can pass to a new &ldquo;CheckAccessFullyMapped&rdquo; intrinsic that tells you if the sample operation would access unmapped tiles. The docs don&rsquo;t say that these functions are restricted to TIER2, but I would assume that to be the case.</p>
</li>
</ul>
<p>Based on this information it appears that TIER1 offers all of the core functionality, while TIER2 has few extras bring it up to par with <a href="http://www.opengl.org/registry/specs/AMD/sparse_texture.txt">AMD&rsquo;s sparse texture extension</a>.</p>
]]></content:encoded>
		</item>
		
		<item>
			<title>SIGGRAPH Follow-Up</title>
			<link>https://therealmjp.github.io/posts/siggraph-follow-up/</link>
			<pubDate>Mon, 29 Jul 2013 06:15:04 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/siggraph-follow-up/</guid>
			<description>So I&amp;rsquo;m hoping that if you&amp;rsquo;re reading this, you&amp;rsquo;ve already attended or read the slides from my presentation about The Order: 1886 that was part of the Physically Based Shading Course at SIGGRAPH last week. If not, go grab them and get started! If you haven&amp;rsquo;t read through the course notes already there&amp;rsquo;s a lot of good info there, in fact there&amp;rsquo;s almost 30 pages worth! The highlights include:
Full description of our Cook-Torrance and Cloth BRDF&amp;rsquo;s, including a handy optimization for the GGX Smith geometry term (for which credit belongs to Steve McAuley) Analysis of our specular antialiasing solution Plenty of details regarding the material scanning process HLSL sample code for the Cook-Torrance BRDF&amp;rsquo;s as well as the specular AA roughness modification Lots of beautiful LaTeX equations If you did attend, I really appreciate you coming and I hope that you found it interesting.</description>
			<content:encoded><![CDATA[<p>So I&rsquo;m hoping that if you&rsquo;re reading this, you&rsquo;ve already attended or read the slides from my presentation about The Order: 1886 that was part of the Physically Based Shading Course at SIGGRAPH last week. If not, <a href="http://blog.selfshadow.com/publications/s2013-shading-course/">go grab them</a> and get started! If you haven&rsquo;t read through the course notes already there&rsquo;s a lot of good info there, in fact there&rsquo;s almost 30 pages worth! The highlights include:</p>
<ul>
<li>Full description of our Cook-Torrance and Cloth BRDF&rsquo;s, including a handy optimization for the GGX Smith geometry term (for which credit belongs to Steve McAuley)</li>
<li>Analysis of our specular antialiasing solution</li>
<li>Plenty of details regarding the material scanning process</li>
<li>HLSL sample code for the Cook-Torrance BRDF&rsquo;s as well as the specular AA roughness modification</li>
<li>Lots of beautiful LaTeX equations</li>
</ul>
<p>If you did attend, I really appreciate you coming and I hope that you found it interesting. It was my first time presenting in front of such a large audience, so please forgive me if I seemed nervous. Also I&rsquo;d like to give another thank you to anyone that came out for drinks later that night, I had a really great time talking shop with some of the industry&rsquo;s best graphics programmers. And of course the biggest thanks goes out to Stephen Hill and Stephen McAuley for giving us the opportunity to speak at the best course at SIGGRAPH.</p>
<p>Anyhow&hellip;now that SIGGRAPH is finished and I have some time to sit down and take a breath, I wanted to follow up with some additional remarks about the topics that we presented. I also thought that if I post blogs more frequently, it might inspire a <a href="http://blog.selfshadow.com/">few other people</a> to do the same.</p>
<h2 id="physically-based-brdfs">Physically Based BRDF&rsquo;s</h2>
<p>I didn&rsquo;t even mention anything about the benefits of physically based BRDF&rsquo;s in our presentation because I feel like it&rsquo;s no longer an issue that&rsquo;s worth debating. We&rsquo;ve been using some form of Cook-Torrance specular for about 2 years now, and it&rsquo;s made everything in our game look better. Everything works more like it <em>should</em> work, and requires less fiddling and fudging on the part of the artists. We definitely had some issues to work through when we first switched (I can&rsquo;t tell you how many times they asked me for direct control over the Fresnel curve), but there&rsquo;s no question as to whether it was worth it in the long run. Next-gen consoles and modern PC GPU&rsquo;s have lots of ALU to throw around, and sophisticated BRDF&rsquo;s are a great way to utilize it.</p>
<h2 id="compositing">Compositing</h2>
<p>First of all, I wanted to reiterate that our compositing system (or &ldquo;The Smasher&rdquo; as it&rsquo;s referred to in-house) is a completely <em>offline</em> process that happens in our content build system. When a level is a built we request a build of all materials referenced in that level, which then triggers a build of all materials used in the compositing stack of that material. Once all of the source materials are built, the compositing system kicks in and generates the blended parameter maps. The compositing process itself is very quick since we do it in a simple pixel shader run on the GPU, but it can take some time to build all of the source materials since doing that requires processing the textures referenced by that material. We also support using composited materials as a source material in a composite stack, which means that a single runtime material can potentially depend on a arbitrarily complex tree of source materials. To alleviate this we aggressively cache intermediate and output assets on local servers in our office, which makes build times pretty quick once the cache has been primed. We also used to compile shaders for every material, which caused long build times when changing code used in material shaders. This forced us to change things so that we only compile shaders directly required by a mesh inside of a level.</p>
<p>We also support runtime parameter blending, which we refer to as layer blending. Since it&rsquo;s at runtime we limit it to 4 layers to prevent the shader from becoming too expensive, unlike the compositing system where artists are free to composite in as many materials as they&rsquo;d like. It&rsquo;s mostly used for environment geo as a way to add in some break-up and variety to tiling materials via vertex colors, as opposed to blending in small bits of a layer using blend maps. One notable exception is that we use the layer blending to add a &ldquo;detail layer&rdquo; to our cloth materials. This lets us keep the overall low-frequency material properties in lower-resolution texture maps, and then blend in tiling high-frequency data (such as the textile patterns acquired from scanning).</p>
<p>One more thing that I really wanted to bring up is that the artists absolutely love it. The way it interacts with our template library allows us to keep low-level material parameter authoring in the hands of a few technical people, and enables everyone else to just think in terms of combining high-level &ldquo;building blocks&rdquo; that form an object&rsquo;s final appearance. I have no idea how we would make a game without it.</p>
<h2 id="maya-viewer">Maya Viewer</h2>
<p>A few people noticed that we have our engine running in a Maya viewport, and came up to ask me about it. I had thought that this was fairly common and thus uninteresting, but I guess I was wrong! We do this by creating a custom Viewport 2.0 render override (via MRenderOverride) that uses our engine to render the scene using Maya&rsquo;s DX11 device (Maya 2013.5 and up support a DX11 mode for Viewport 2.0, for 2013 you gave to use OpenGL). With their their render pass API you can basically specify what things you want Maya to draw, so we render first (so that we can fill the depth buffer) and then have Maya draw things like HUD text and wireframe overlays for UI. The VP 2.0 API actually supports letting you hook into their renderer and data structure, which basically lets you specify things like vertex data and shader code while allowing Maya to handle the actual rendering. We don&rsquo;t do that&hellip;we basically just give Maya&rsquo;s device to our renderer and let our engine draw things the same way that it does in-game. To do this, we have a bunch of Maya plugin code that tracks objects in the scene (meshes, lights, plugin nodes, etc.) and handles exporting data off of them and converting them to data that can be consumed by our renderer. Maintaining this code has been a lot of work, since it basically amounts to an alternate path for scene data that&rsquo;s in some ways very different from what we normally do in the game. However it&rsquo;s huge workflow enhancement for all of our artists, so we put up with it. I definitely never thought I would know so much about Maya and its API!</p>
<p>Some other cool things that we support inside of Maya:</p>
<ul>
<li>We can embed our asset editors (for editing materials, particle systems, lens flares, etc.) inside of Maya, which allows for real-time editing of those assets while they&rsquo;re being viewed</li>
<li>GI bakes are initiated from Maya and then run on the GPU, which allows the lighting artists to keep iterating inside of a single tool and get quick feedback</li>
<li>Our pre-computed visibility can also be baked inside of Maya, which allows artists to check for vis dropouts and other bugs without running the game</li>
</ul>
<p>One issue that I had to work around in our Maya viewer was using the debug DX11 device. Since Maya creates the device we can&rsquo;t control the creation flags, which means no helpful DEBUG mode to tell us when we mess something up. To work around this, I had to make our renderer create its own device and use that for rendering. Then when presenting the back buffer and depth buffer to Maya, we have to use DXGI synchronization to copy texture data from our device&rsquo;s render targets to Maya&rsquo;s render targets. It&rsquo;s not terribly hard, but it requires reading through a lot of obscure DXGI documentation.</p>
<p>Sample Code</p>
<p>You may not have noticed, but there&rsquo;s a <a href="https://github.com/TheRealMJP/SpecularAA">sample app</a> to go with the presentation! Dave and I always say that it&rsquo;s a little lame to give a talk about something without providing sample code, so we had to put our money where our mouth is. It&rsquo;s essentially a working implementation of our specular AA solution as well as our Cook-Torrance BRDF&rsquo;s that uses my DX11 sample framework. Seeing for yourself with the sample app is a whole lot better than looking at pictures, since the primary concern is avoiding temporal aliasing as the camera or object changes position. These are all of the specular AA techniques available in the sample for comparison:</p>
<ul>
<li>LEAN</li>
<li>CLEAN</li>
<li>Toksvig</li>
<li>Pre-computed Toksvig</li>
<li>In-shader vMF evaluation of Frequency Domain Normal Map Filtering</li>
<li>Pre-computed vMF evaluation (what we use at RAD)</li>
</ul>
<p>For a ground-truth reference, I also implemented in-shader supersampling and texture-space lighting. The shader supersampling works by interpolating vertex attributes to random sample points surrounding the pixel center, computing the lighting, and then applying a bicubic filter. Texture-space lighting works exactly like you think it would: the lighting result is rendered to a FP16 texture that&rsquo;s 2x the width and height of the normal map, mipmaps are generated, and the geometry is rendered by sampling from the lighting texture with anisotropic filtering. Since linear filtering is used both for sampling the lighting map and generating the mipmaps, the overall results don&rsquo;t always look very good (especially under magnification). However the results <em>are</em> completely stable, since the sample positions never move relative to the surface being shaded. The pixel shader supersampling technique still suffers from some temporal flickering because of this, although it&rsquo;s obviously significantly reduced with higher sample counts.</p>
<p>Dave and I had also intended to implement solving for multiple vMF lobes, but unfortunately we ran out of time and we weren&rsquo;t able to include it. I&rsquo;d like to revisit it at some point and release an updated sample that has it implemented. I don&rsquo;t think it would actually be worth it for a game to store so much additional texture data, however I think it would be useful as a reference. It might also be interesting to see if the data could be used to drive a more sophisticated pre-computation step that bakes the anisotropy into the lower-resolution mipmaps.</p>
<p>Like I mentioned before, the sample also has implementations of the GGX and Beckmann-based specular BRDF&rsquo;s described in our course notes. We also implemented our GGX and Cloth BRDF&rsquo;s as .brdf files for Disney&rsquo;s BRDF Explorer, which you can download <a href="https://mynameismjp.files.wordpress.com/2013/07/rad_brdf.zip">here</a>.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://palgorithm.co.uk" title="sam@palgorithm.co.uk">Sam Martin</a> - <time datetime="2013-07-29 15:25:56">Jul 1, 2013</time></p>
<p>This really is an exceptionally useful set of resources you&rsquo;ve put together here. I saw your talk at siggraph and was very impressed. It&rsquo;s clearly had a lot of love and effort put into it!</p>
<hr />
####
[Simon Brown]( "") - <time datetime="2014-05-08 04:23:04">May 4, 2014</time>
<p>Gah, cannot type, I mean 1/(2*kappa) in course notes and 1/kappa in example project&hellip;</p>
<hr />
####
[Simon Brown]( "") - <time datetime="2014-05-08 02:20:31">May 4, 2014</time>
<p>Great resource! I&rsquo;m a bit confused by a difference between the course notes and sample code when computing effective roughness using existing roughness and kappa. The Han et el paper and course notes lists sigmap=sqrt(sigma*sigma + 2/kappa), but the sample code consistently uses sigmap=sqrt(sigma*sigma + 1/kappa) in GenerateMaps.hlsl and Mesh.hlsl. Is this a typo or the application of some errata? Thanks!</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2014-04-27 11:31:27">Apr 0, 2014</time>
<p>Hi Kostas, I&rsquo;m glad you&rsquo;ve found it useful! I prefer to keep the division by Pi in the diffuse term, as opposed to baking that into the light intensity and then multiplying the specular term by Pi. For me that just fits with my mental model of how I think about the diffuse BRDF. In practice I don&rsquo;t think it should matter as long as you&rsquo;re careful with multiplying your Pi factor through, so that you have the right relative intensity between your diffuse and specular. -Matt</p>
<hr />
####
[Readings on Physically Based Rendering | Interplay of Light](http://interplayoflight.wordpress.com/2013/12/30/readings-on-physically-based-rendering/ "") - <time datetime="2013-12-30 05:14:44">Dec 1, 2013</time>
<p>[…] Pettineo’s follow up to the very interesting The Order: 1886 Siggraph 2013 presentation expands on some PBR topics and […]</p>
<hr />
####
[Kostas Anagnostou](http://interplayoflight.wordpress.com "kostas.anagnostou@gmail.com") - <time datetime="2014-04-27 04:27:23">Apr 0, 2014</time>
<p>Hi Matt, thanks for the great presentation and following write-up as well as the sample application which is extremely useful! A quick question, in the sample&rsquo;s implementation of the BRDFs, the division by Pi is still applied even for punctual lights. Shouldn&rsquo;t it be removed by the Pi in the lighting equation for punctual lights? (or I am reading something wrong?).</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2014-05-09 13:24:40">May 5, 2014</time>
<p>Hi Simon, I think that&rsquo;s just a typo that we never noticed. Thank you for pointing it out! -Matt</p>
<hr />
####
[Notes on Anti-Aliasing : Texture filtering (part 3) &laquo; Mehdi NS](https://mehdins.wordpress.com/2017/08/29/notes-on-anti-aliasing-texture-filtering-part-3/ "") - <time datetime="2017-08-28 19:07:47">Aug 1, 2017</time>
<p>[…] implementation details, the reference is MJP specularAA project, in which the main specular anti aliasing techniques used in games are […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>What I&#39;ve been working on for the past 2 years</title>
			<link>https://therealmjp.github.io/posts/what-ive-been-working-on/</link>
			<pubDate>Wed, 12 Jun 2013 07:48:45 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/what-ive-been-working-on/</guid>
			<description>The announce trailer for Ready At Dawn&amp;rsquo;s latest project was shown during Sony&amp;rsquo;s E3 press conference yesterday, but if you missed it you can watch it here. There&amp;rsquo;s also a few full-res screenshots available here, with less compress-o-vision. It feels really good to finally be able to tell people what game I&amp;rsquo;ve been working on, and that we&amp;rsquo;re making a PS4 title. I&amp;rsquo;m also insanely proud of the trailer itself, as well as the in-house tech we&amp;rsquo;ve developed that made it possible.</description>
			<content:encoded><![CDATA[<p>The announce trailer for Ready At Dawn&rsquo;s latest project was shown during Sony&rsquo;s E3 press conference yesterday, but if you missed it you can watch it <a href="http://www.youtube.com/watch?v=MbAhOB-59Ag">here</a>. There&rsquo;s also a few full-res screenshots available <a href="http://www.gamespot.com/e3/image/6409861-1/">here</a>, with less compress-o-vision. It feels <em>really</em> good to finally be able to tell people what game I&rsquo;ve been working on, and that we&rsquo;re making a PS4 title. I&rsquo;m also insanely proud of the trailer itself, as well as the in-house tech we&rsquo;ve developed that made it possible. I have no doubts that I work with some of the most dedicated and talented people in the industry, and being able to collaborate with them makes it worth getting up in the morning. I&rsquo;m really looking forward to sharing some of the cool things we&rsquo;ve come up with, both on this blog as well as in more <a href="http://s2013.siggraph.org/attendees/courses/events/physically-based-shading-theory-and-practice">formal settings</a>.</p>
<p>I was also seriously impressed by all of the other games and demos that were shown off yesterday. It&rsquo;s really exciting to see everyone gearing up for next gen, and pushing their engines to new levels. These were some stand-outs for me:</p>
<ul>
<li>Infamous: Second Son - characters look awesome! Lots of details in the skin and fabrics, plus some really nice facial animation</li>
<li>Watch Dogs - we&rsquo;ve seen it before, but it looks better and better every time</li>
<li>Battlefield 4 - these guys are just out of control when it comes to destruction and mayhem! Also, really psyched to see that commander mode is making a comeback.</li>
<li>Mirror&rsquo;s Edge 2 - the first game has a special place in my heart, so I would love a sequel even if it didn&rsquo;t look awesome (but it does). It&rsquo;s really cool seeing how good GI can look in a came with a clean art style.</li>
<li>Destiny - I&rsquo;m not usually one for persistent-world multiplayer games, but this game might be exception. Looks like a lot of fun, and has great graphics to boot.</li>
<li>Titan Fall - any game where I can pilot a mech and run on walls has my money</li>
<li>The Dark Sorcerer - very high quality assets, and great lighting/materials. The performance capture is really impressive as well.</li>
</ul>
<p>I&rsquo;m looking forward to seeing more of these games and comparing notes on what techniques make the most out of next-gen hardware. By my count we&rsquo;ve crossed off around 8 or so of the things on <a href="http://mynameismjp.wordpress.com/2011/12/06/things-that-need-to-die/">my list</a>, and hopefully the entire industry will collectively figure out how to make all of them extinct. And then we can come up with new things to add to the list!</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://www.facebook.com/jmautari" title="mautari@gmail.com">John Mautari</a> - <time datetime="2013-06-12 03:14:49">Jun 3, 2013</time></p>
<p>Awesome work as always! Congratulations!</p>
<hr />
####
[]( "") - <time datetime="2013-06-12 00:59:29">Jun 3, 2013</time>
<p>So, just to make this clear: That is all in-engine?</p>
<hr />
####
[Michael Alling](http://www.facebook.com/allingm "allingm@gmail.com") - <time datetime="2013-06-20 20:03:14">Jun 4, 2013</time>
<p>Wow, MJP. You had the only trailer of the show that had me saying &ldquo;that looks CG, these dudes are liars.&rdquo;</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2013-06-13 12:45:57">Jun 4, 2013</time>
<p>Thank you John! And yes, everything was in-engine. :-)</p>
<hr />
####
[]( "") - <time datetime="2013-06-19 06:37:37">Jun 3, 2013</time>
<p>First of all, congrats. At first i thought sony was pulling the same trick when they showed killzone 2, and went for a &ldquo;hey guys, this is realtime.. lol.. jk.. its CG&rdquo;, but then I noticed some clues that it was indeed realtime. Second thing, what are exactly the 8 things you crossed off the list?</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>HLSL User Defined Language for Notepad&#43;&#43;</title>
			<link>https://therealmjp.github.io/posts/hlsl-udl/</link>
			<pubDate>Mon, 05 Nov 2012 07:09:39 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/hlsl-udl/</guid>
			<description>When it comes to writing shaders, Notepad++ is currently my editor of choice. The most recent release of Notepad++ added version 2.0 of their User Defined Language (UDL) system, which adds quite a few improvements. I&amp;rsquo;ve been using an HLSL UDL file that I downloaded from somewhere else for a while now, and I decided to upgrade it to the 2.0 format and also make it work better for SM5.0 profiles.</description>
			<content:encoded><![CDATA[<p>When it comes to writing shaders, <a href="http://notepad-plus-plus.org/news/notepad-6.2-release-udl2.html">Notepad++</a> is currently my editor of choice. The most recent release of Notepad++ added version 2.0 of their User Defined Language (UDL) system, which adds quite a few improvements. I&rsquo;ve been using an HLSL UDL file that I downloaded from somewhere else for a while now, and I decided to upgrade it to the 2.0 format and also make it work better for SM5.0 profiles. I added all of the operators, keywords, types attributes, system-value semantics, intrinsics, and methods, so they all get syntax highlighting now. I also stripped out all of the old pre-SM4.0 intrinsics and semantics, as well as the effect-specifics keywords. I&rsquo;ve exported it as an XML file and uploaded it to my <a href="https://docs.google.com/open?id=0B9NkyJjiExTjN1VyT2pCbWpJYlE">Google Drive</a> so that others can make use of it as well. To use it, you can either import the XML file from the UDL dialog (Language-&gt;Define your language), or you can replace your userDefineLang.xml file in the <code>AppData\Notepad++</code> folder. Enjoy!</p>
<p>Update 7/29/2019: I have long since moved on to Sublime Text as my editor of choice, using Matt Sutherlin&rsquo;s excellent <a href="https://packagecontrol.io/packages/HLSL%20Syntax">HLSL Syntax Package</a>. You&rsquo;re still of course welcome to download my UDL for Notepad++, but I have not updaated it since 2012 so it&rsquo;s missing numerous keywords that were added to HLSL over the years.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://www.guishade.tumblr.com" title="gui.shade@gmail.com">guishade</a> - <time datetime="2013-07-12 14:39:37">Jul 5, 2013</time></p>
<p>vvvv is a super grate platform for dx shader experimenting, no compiling, just runtime</p>
<hr />
####
[JC](http://www.google.com "no@thanks.forme") - <time datetime="2012-11-06 12:21:41">Nov 2, 2012</time>
<p>y u no <a href="http://nshader.codeplex.com/">http://nshader.codeplex.com/</a> ?</p>
<hr />
####
[defaultex]( "default_ex@live.com") - <time datetime="2012-11-16 19:33:43">Nov 5, 2012</time>
<p>Since Notepad++ allows for regex. May want to look at some of the regex in: <a href="http://fxarchitect.codeplex.com">http://fxarchitect.codeplex.com</a> The regex is kinda complex, splitting up HLSL syntax into categories as well as creating hierarchies from scope blocks. Sadly much of the regex has went unused and I have loads more for even more in-depth analysis of HLSL sitting in a monstrous text file (complete with comments) waiting to be put to use when I have more time and interest to work on that editor.</p>
<hr />
####
[Tiago Costa]( "") - <time datetime="2012-11-06 04:55:56">Nov 2, 2012</time>
<p>Thanks for sharing! I was tired of writing shaders without syntax highlighting in Nodepad.</p>
<hr />
####
[SteveM]( "steve.marton@gmail.com") - <time datetime="2012-11-14 19:15:52">Nov 3, 2012</time>
<p>My favorite IDE for shaders is Visual Studio + Visual Assist. Having &ldquo;go to definition&rdquo;, autocomplete, and everything else work is huge! My shaders are in my game solution, so everything&rsquo;s easy to search together with my c++ code, and within reach without switching editors. The only bug I haven&rsquo;t been able to fix is that typing swizzles like v.x and similar will always autocomplete to v-&gt;x and is a pain to fix manually. Anyone have a solution? I&rsquo;m not willing to give up the feature for my c++ coding, so I live with it. Here&rsquo;s how I set it up: In Visual Studio Tools/Options/Text Editor/File Extensions select Visual C++ as the editor and add fx, cgfx, etc as extensions. Close Visual Studio. Put a usertype.dat in C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE, something like this (there might be better ones): <a href="http://www.gamedev.net/topic/472205-hlsl-fx-file-syntax-colors/">http://www.gamedev.net/topic/472205-hlsl-fx-file-syntax-colors/</a> To get VAssist to work, add the extensions &ldquo;.fx;.cgfx;&rdquo; to the registry entry HKEY_CURRENT_USER\Software\Whole Tomato\Visual Assist X\VANet10\ExtSource Restart Visual Studio. Enjoy.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-11-14 00:10:11">Nov 3, 2012</time>
<p>I prefer to edit shaders in Notepad++. It&rsquo;s more lightweight, plus I have some plugins that I like to use.</p>
<hr />
####
[Rybets]( "rybetsg@gmail.com") - <time datetime="2013-08-28 10:03:21">Aug 3, 2013</time>
<p>FX Composer 2.5 + GPU Shader Analyzer + VisualStudio. I never code HLSL from scratch, but study ideas, using Excel (in developer mode, cause it uses sliders) and use Rhino for data visualization. There is a complex math stuff, so it is impossible to iterate in application to find a right formalization of math. ideas.</p>
<hr />
####
[Luc Bloom]( "") - <time datetime="2015-07-02 00:28:30">Jul 4, 2015</time>
<p>Thanks!</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Experimenting with Reconstruction Filters for MSAA Resolve</title>
			<link>https://therealmjp.github.io/posts/msaa-resolve-filters/</link>
			<pubDate>Mon, 29 Oct 2012 07:33:31 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/msaa-resolve-filters/</guid>
			<description>Previous article in the series: A Quick Overview of MSAA
Update 8/26/2017: while working on The Order I improved upon the work described here, which I presented at SIGGRAPH 2015. I also created an updated MSAA + TAA filtering demo that you can find on GitHub, which just about completely supersedes the demo that&amp;rsquo;s linked at the end of the article. So make sure that you look at the new one as well!</description>
			<content:encoded><![CDATA[<p><em>Previous article in the series: <a href="../msaa-overview/">A Quick Overview of MSAA</a></em></p>
<p>Update 8/26/2017: while working on The Order I improved upon the work described here, which I <a href="http://advances.realtimerendering.com/s2015/rad_siggraph_advances_2015.pptx">presented</a> at SIGGRAPH 2015. I also created an updated <a href="https://github.com/TheRealMJP/MSAAFilter">MSAA + TAA filtering demo</a> that you can find on GitHub, which just about completely supersedes the demo that&rsquo;s linked at the end of the article. So make sure that you look at the new one as well!</p>
<p>Despite having the flexibility to implement a custom resolve for MSAA, the &ldquo;standard&rdquo; box filter resolve is still commonly used in games. While the box filter works well enough, it has some characteristics that can be considered undesirable for a reconstruction filter. In particular, the box function has a discontinuity at its edge that results in an infinite frequency response (the frequency domain equivalent of a box function is the sinc function). This causes it to introduce postaliasing when used as a reconstruction filter, since the filter is unable to isolate the original copy of a signal’s spectrum. The primary advantage offered by such a resolve is that it’s cheap from a performance point of view, since only subsamples within a single pixel need to be considered when computing a resolved pixel value.</p>
<p>The question we now want to answer is “can we do better?” Offline renderers such as Pixar&rsquo;s PRMan support a variety of  filter types for antialiasing, so it stands to reason that we should at least explore the possibilities for real-time graphics. If we decide to forego the “standard” resolve offered by <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ff476474%28v=vs.85%29.aspx">ResolveSubresource</a> and instead perform our own resolve using a pixel or compute shader that that accesses the raw multisampled texture data, we are pretty much free to implement whatever reconstruction filter we’d like. So there is certainly no concern over lack of flexibility. Performance, however, is still an issue. Every GPU that I’ve run my code on will perform worse with a custom resolve, even when using a simple box filter with results that exactly match a standard resolve. Currently the performance delta seems to be worse on AMD hardware as opposed to Nvidia hardware. On top of that, there’s additional costs for the increased texture samples required for wider filter kernels. Separable filtering can be used to reduce the number of samples required for wide filters, however you must have special considerations with the rotated grid sample patterns used by MSAA. Unfortunately I haven’t solved these problems yet, so for this sample I’m just going to focus on quality without too much regard for performance. Hopefully in a future article I can revisit this, and address the performance issues.</p>
<p>At this point I feel that I should bring up TXAA. If you’re not familiar, <a href="http://www.geforce.com/landing-page/txaa">TXAA</a> is a library-supported antialiasing technique introduced for recent Nvidia Kepler-based GPU’s. There’s no public documentation as to exactly how it works, but <a href="http://timothylottes.blogspot.com/">Timothy Lottes</a> has mentioned a <a href="http://timothylottes.blogspot.com/2012/07/txaa-out-30479-driver.html">few details</a> here and there on his blog. From the info he’s given, it seems safe to assume that the MSAA resolve used by TXAA is something other than a box filter, and is using a filter width wider than a pixel. Based on these assumptions, you should be able to produce similar results with the framework that I’ve set up.</p>
<h2 id="implementation">Implementation</h2>
<p>The sample application renders my trusty tank scene in HDR to an fp16 render target with either 1x, 2x, 4x, or 8x MSAA enabled. Once the scene is rendered, a pixel shader is used to resolve the MSAA render target to a non-MSAA render target with one of ten available reconstruction filters. I implemented the following filters:</p>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Box</td>
<td><a href="/images/converted/msaa-resolve-filters/filter_01_box.png"><img src="/images/converted/msaa-resolve-filters/filter_01_box_resized_300.png" alt="" title="Filter_01_Box"></a></td>
</tr>
<tr>
<td>Triangle</td>
<td><a href="/images/converted/msaa-resolve-filters/filter_02_triangle.png"><img src="/images/converted/msaa-resolve-filters/filter_02_triangle_resized_300.png" alt="" title="Filter_02_Triangle"></a></td>
</tr>
<tr>
<td>Gaussian</td>
<td><a href="/images/converted/msaa-resolve-filters/filter_03_gaussian.png"><img src="/images/converted/msaa-resolve-filters/filter_03_gaussian_resized_300.png" alt="" title="Filter_03_Gaussian"></a></td>
</tr>
<tr>
<td>Blackman-Harris</td>
<td><a href="/images/converted/msaa-resolve-filters/filter_04_blackmanharris.png"><img src="/images/converted/msaa-resolve-filters/filter_04_blackmanharris_resized_300.png" alt="" title="Filter_04_BlackmanHarris"></a></td>
</tr>
<tr>
<td>Smoothstep (Hermite spline)</td>
<td><a href="/images/converted/msaa-resolve-filters/filter_05_smoothstep.png"><img src="/images/converted/msaa-resolve-filters/filter_05_smoothstep_resized_300.png" alt="" title="Filter_05_Smoothstep"></a></td>
</tr>
<tr>
<td>B-spline</td>
<td><a href="/images/converted/msaa-resolve-filters/filter_06_bspline.png"><img src="/images/converted/msaa-resolve-filters/filter_06_bspline_resized_300.png" alt="" title="Filter_06_BSpline"></a></td>
</tr>
<tr>
<td>Catmull-Rom</td>
<td><a href="/images/converted/msaa-resolve-filters/filter_07_catmullrom.png"><img src="/images/converted/msaa-resolve-filters/filter_07_catmullrom_resized_300.png" alt="" title="Filter_07_CatmullRom"></a></td>
</tr>
<tr>
<td>Mitchell</td>
<td><a href="/images/converted/msaa-resolve-filters/filter_08_mitchell.png"><img src="/images/converted/msaa-resolve-filters/filter_08_mitchell_resized_300.png" alt="" title="Filter_08_Mitchell"></a></td>
</tr>
<tr>
<td>Generalized Cubic</td>
<td><a href="/images/converted/msaa-resolve-filters/filter_09_cubic.png"><img src="/images/converted/msaa-resolve-filters/filter_09_cubic_resized_300.png" alt="" title="Filter_09_Cubic"></a></td>
</tr>
<tr>
<td>Sinc</td>
<td><a href="/images/converted/msaa-resolve-filters/filter_10_sinc.png"><img src="/images/converted/msaa-resolve-filters/filter_10_sinc_resized_300.png" alt="" title="Filter_10_Sinc"></a></td>
</tr>
</tbody>
</table>
<p>I started out by implementing some of the <a href="http://www.renderman.org/RMR/st/PRMan_Filtering/Filtering_In_PRMan.html">filters supported by PRMan</a>, and using similar parameters for controlling the filtering. However I ended up deviating from the PRMan setup in order to make things more intuitive (in my opinion, at least). All filters except for the sinc filter were implemented such that their &ldquo;natural range&rdquo; of non-zero values were in the [-0.5, 0.5] range. This deviates from the canonical filter widths for several of these filters, notably the cubic filters (which are normally defined for the [-2, 2] range). I then used a &ldquo;filter width&rdquo; parameter to inversely scale the inputs to the filtering functions. So for a filter width of 1.0, the filters all have a width equal to the size of a single resolved pixel. The one exception is the sinc filter, where I used the filter width to window the function rather than scaling the input value. I should also note that I implemented all of the filters as radial filters where the input is the screen-space distance from the output pixel center to the sample position. Typically filters for image scaling are used in separable passes where 1D filters are passed the X or Y sample distance. Because of this my &ldquo;Box&rdquo; filter is actually disc-shaped, but it produces very similar results. In fact for a filter width of 1.0 the results are identical to a &ldquo;standard&rdquo; box filter resolve. The &ldquo;Triangle&rdquo; filter uses a standard triangle function,which can be considered a &ldquo;cone&rdquo; function when used as a radial filter. &ldquo;Gaussian&rdquo; uses a standard Gaussian function with a configurable sigma parameter, with the result windowed to [-0.5, 0.5].  The &ldquo;Smoothstep&rdquo; filter simply uses the smoothstep intrinsic available in HLSL, which implements a cubic hermite spline. The &ldquo;Generalized Cubic&rdquo; filter is an implementation of the cubic function suggested by Mitchell and Netravali in their paper, with the B and C parameters being tweakable by the user. The &ldquo;B-spline&rdquo;, &ldquo;Mitchell&rdquo; and &ldquo;Catmull-Rom&rdquo; filters use this same function except with fixed values for B and C. &ldquo;Sinc&rdquo; is the standard sinc function, windowed to [-FilterWidth, FilterWidth] as mentioned previously.</p>
<p>To visualize the filtering function, I added a real-time 1D plot of the currently-selected filter function using the current filter width. I also added a plot of the 1D fourier transform of the filter function (calculated with the help if the awesomely easy-to-integrate <a href="http://sourceforge.net/projects/kissfft/">Kiss FFT library</a>), so that you can also visualize the frequency response of the selected filter type. This can be useful for estimating the amount of postaliasing produced by a filter, as well as the attenuation of frequencies below the Nyquist rate (which results in blurring).</p>
<p>After the resolve is performed, the result is fed into a standard post-processing chain. This phase includes average luminance calculation for auto-exposure, bloom, and HDR tone mapping. I added an option to tone map subsamples in a manner similar to <a href="http://www.humus.name/index.php?page=3D&amp;ID=77">Humus&rsquo;s sample</a>, so that the results can be compared to resolve prior to tone mapping. When this option is activated, the bloom and auto-exposure passes work with non-resolved MSAA textures since the output of the resolve no longer contains linear HDR values. Note that the resolve is still performed prior to post-processing, since I wanted to keep the resolve separate from the post-processing phase so that it was more visible. In production it would most likely be done after all post-processing, however you would still need the same considerations regarding working with non-resolved MSAA data.</p>
<p>Here&rsquo;s a full list of all options that I implemented:</p>
<ul>
<li><strong>MSAA Mode -</strong> the number of MSAA samples to use for the primary render target (1x, 2x, 4x, or 8x)</li>
<li><strong>Filter Type</strong> - the filtering function to use in the resolve step (supports all of the filters listed above)</li>
<li><strong>Use Standard Resolve</strong> - when enabled, a &ldquo;standard&rdquo; box filter resolve is performed using ResolveSubresource</li>
<li><strong>Tone Map Subsamples</strong> - when enabled, tone mapping is applied before the subsamples are resolved</li>
<li><strong>Enable FXAA</strong>  - enables or disables FXAA with high-quality PC settings</li>
<li><strong>Render Triangle</strong> - renders a plain red triangle in the center of the screen</li>
<li><strong>Bloom Exposure</strong> - an exposure (in log2 space) applied to HDR values in order to create the bloom source values</li>
<li><strong>Bloom Magnitude</strong> - a multiplier for the bloom value that&rsquo;s combined with the tone mapped result</li>
<li><strong>Auto-Exposure Key Value</strong> - key value for controlling auto-exposure</li>
<li><strong>Adaptation Rate</strong> - rate at which exposure is adapted over time</li>
<li><strong>Roughness</strong> - roughness used for material specular calculations</li>
<li><strong>Filter</strong> <strong>Size</strong> - the radius of the filter kernel (in pixels) used during the resolve step</li>
<li><strong>Gaussian Sigma</strong> - the sigma parameter for the Gaussian function, used by the Gaussian filter mode</li>
<li><strong>Cubic B</strong> - the &ldquo;B&rdquo; parameter to Mitchell&rsquo;s generalized cubic function, used by the Generalized Cubic filter mode</li>
<li><strong>Cubic C</strong> - the &ldquo;C&rdquo; parameter to Mitchell&rsquo;s generalized cubic function, used by the Generalized Cubic filter mode</li>
<li><strong>Magnification</strong> - magnification level for the final output (magnification is performed with point filtering)</li>
<li><strong>Triangle Rotation Speed</strong> - the speed at which the red triangle (enabled by Render Triangle) is rotated</li>
</ul>
<h2 id="results">Results</h2>
<p>The following table contains links to 1280x720 screenshots from my sample application using various filter types and filter widths. All screenshots have use 4xMSAA, and perform the resolve in linear HDR space (bloom and tone mapping are performed after):</p>
<table>
<thead>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Box</td>
<td><a href="/images/converted/msaa-resolve-filters/normal_0_box_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_0_box_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_0_box_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_0_box_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_0_box_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_0_box_6.png">6.0</a></td>
</tr>
<tr>
<td>Triangle</td>
<td><a href="/images/converted/msaa-resolve-filters/normal_1_triangle_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_1_triangle_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_1_triangle_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_1_triangle_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_1_triangle_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_1_triangle_6.png">6.0</a></td>
</tr>
<tr>
<td>Gaussian</td>
<td><a href="/images/converted/msaa-resolve-filters/normal_2_gaussian_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_2_gaussian_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_2_gaussian_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_2_gaussian_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_2_gaussian_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_2_gaussian_6.png">6.0</a></td>
</tr>
<tr>
<td>Blackman-Harris</td>
<td><a href="/images/converted/msaa-resolve-filters/normal_3_blackman-harris_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_3_blackman-harris_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_3_blackman-harris_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_3_blackman-harris_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_3_blackman-harris_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_3_blackman-harris_6.png">6.0</a></td>
</tr>
<tr>
<td>Smoothstep</td>
<td><a href="/images/converted/msaa-resolve-filters/normal_4_smoothstep_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_4_smoothstep_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_4_smoothstep_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_4_smoothstep_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_4_smoothstep_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_4_smoothstep_6.png">6.0</a></td>
</tr>
<tr>
<td>B-spline</td>
<td><a href="/images/converted/msaa-resolve-filters/normal_5_b-spline_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_5_b-spline_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_5_b-spline_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_5_b-spline_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_5_b-spline_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_5_b-spline_6.png">6.0</a></td>
</tr>
<tr>
<td>Catmull-Rom</td>
<td><a href="/images/converted/msaa-resolve-filters/normal_6_catmull-rom_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_6_catmull-rom_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_6_catmull-rom_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_6_catmull-rom_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_6_catmull-rom_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_6_catmull-rom_6.png">6.0</a></td>
</tr>
<tr>
<td>Mitchell</td>
<td><a href="/images/converted/msaa-resolve-filters/normal_7_mitchell_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_7_mitchell_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_7_mitchell_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_7_mitchell_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_7_mitchell_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_7_mitchell_6.png">6.0</a></td>
</tr>
<tr>
<td>Sinc</td>
<td><a href="/images/converted/msaa-resolve-filters/normal_9_sinc_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_9_sinc_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_9_sinc_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_9_sinc_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_9_sinc_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/normal_9_sinc_6.png">6.0</a></td>
</tr>
</tbody>
</table>
<p>This table contains similar screenshots, except that the tone mapping is performed prior to the resolve:</p>
<table>
<thead>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Box</td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_0_box_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_0_box_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_0_box_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_0_box_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_0_box_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_0_box_6.png">6.0</a></td>
</tr>
<tr>
<td>Triangle</td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_1_triangle_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_1_triangle_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_1_triangle_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_1_triangle_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_1_triangle_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_1_triangle_6.png">6.0</a></td>
</tr>
<tr>
<td>Gaussian</td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_2_gaussian_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_2_gaussian_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_2_gaussian_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_2_gaussian_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_2_gaussian_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_2_gaussian_6.png">6.0</a></td>
</tr>
<tr>
<td>Blackman-Harris</td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_3_blackman-harris_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_3_blackman-harris_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_3_blackman-harris_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_3_blackman-harris_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_3_blackman-harris_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_3_blackman-harris_6.png">6.0</a></td>
</tr>
<tr>
<td>Smoothstep</td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_4_smoothstep_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_4_smoothstep_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_4_smoothstep_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_4_smoothstep_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_4_smoothstep_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_4_smoothstep_6.png">6.0</a></td>
</tr>
<tr>
<td>B-spline</td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_5_b-spline_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_5_b-spline_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_5_b-spline_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_5_b-spline_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_5_b-spline_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_5_b-spline_6.png">6.0</a></td>
</tr>
<tr>
<td>Catmull-Rom</td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_6_catmull-rom_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_6_catmull-rom_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_6_catmull-rom_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_6_catmull-rom_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_6_catmull-rom_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_6_catmull-rom_6.png">6.0</a></td>
</tr>
<tr>
<td>Mitchell</td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_7_mitchell_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_7_mitchell_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_7_mitchell_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_7_mitchell_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_7_mitchell_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_7_mitchell_6.png">6.0</a></td>
</tr>
<tr>
<td>Sinc</td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_9_sinc_1.png">1.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_9_sinc_2.png">2.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_9_sinc_3.png">3.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_9_sinc_4.png">4.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_9_sinc_5.png">5.0</a></td>
<td><a href="/images/converted/msaa-resolve-filters/tonemap_9_sinc_6.png">6.0</a></td>
</tr>
</tbody>
</table>
<p>As we take a close look at the images, the results shouldn&rsquo;t be too surprising. For the most part, wider filter kernels tend to reduce aliasing while smaller filters preserve more high-frequency detail. Personally I find  that the cubic spline filters with no negative lobes (smoothstep and B-Spline) will produce the best results, with the best balance between aliasing and blurring occurring around the 2.0-3.0 range. Here is a magnified image showing the results of 4xMSAA with a standard 1-pixel-wide box filter, followed by the same image with a 3-pixel-wide B-spline filter:</p>
<p><a href="/images/converted/msaa-resolve-filters/pic0_normal.png"><img src="/images/converted/msaa-resolve-filters/pic0_normal.png" alt="" title="Pic0_Normal"></a></p>
<p><a href="/images/converted/msaa-resolve-filters/pic0_bspline.png"><img src="/images/converted/msaa-resolve-filters/pic0_bspline.png" alt="" title="Pic0_BSpline"></a></p>
<center><i>4xMSAA with a "standard" 1-pixel-wide box filter, followed by 4xMSAA with a 3-pixel-wide B-spline filter</i></center>
<p>The appearance of aliasing is reduced on geometry edges with the B-spline filter, particularly edges with higher contrast. Here&rsquo;s another pair of images that are magnified even further, so that you can see the edge quality:</p>
<p><a href="/images/converted/msaa-resolve-filters/pic1_normal.png"><img src="/images/converted/msaa-resolve-filters/pic1_normal.png" alt="" title="Pic1_Normal"></a></p>
<p><a href="/images/converted/msaa-resolve-filters/pic1_bspline1.png"><img src="/images/converted/msaa-resolve-filters/pic1_bspline1.png" alt="" title="Pic1_BSpline"></a></p>
<center><i>Highly magnified images showing 4xMSAA with a 1-pixel-wide box filter, followed by a 3-pixel-wide B-spline filter</i></center>
<p>Here&rsquo;s another set of images showing the results of a wider filter kernel on high-frequency details from normal maps and specular lighting:</p>
<p><a href="/images/converted/msaa-resolve-filters/pic2_normal.png"><img src="/images/converted/msaa-resolve-filters/pic2_normal.png" alt="" title="Pic2_Normal"></a><a href="/images/converted/msaa-resolve-filters/pic2_bspline2.png"><img src="/images/converted/msaa-resolve-filters/pic2_bspline2.png" alt="" title="Pic2_BSpline2"></a><a href="/images/converted/msaa-resolve-filters/pic2_bspline3.png"><img src="/images/converted/msaa-resolve-filters/pic2_bspline3.png" alt="" title="Pic2_BSpline3"></a></p>
<center><i>4xMSAA with a 1-pixel-wide box filter, with a 2-pixel-wide B-spline filter, and with a 3-pixel-wide B-spline filter</i></center>
<p>As you can see in the images, a 2-pixel-wide B-spline filter is actually pretty good in terms of not attenuating details that are close to a pixel in size. A wider filter reduces aliasing even further, but I feel that  filter width of 2.0 is still an improvement over the quality offered by a &ldquo;standard&rdquo; resolve. So it&rsquo;s probably a pretty good place to start if you want better quality, but you prefer a sharper output image. The other cubic filters with negative lobes (such as Catmull-Rom and Mitchell) will also produce a sharper result, however the negative lobe can produce undesirable artifacts if they&rsquo;re too strong. This is especially true when filtering HDR values with high intensity, since they can have a strong effect on neighboring pixels. For this reason I think that Mitchell is a better option over Catmull-Rom, since Mitchell&rsquo;s negative lobes are bit less pronounced. The sinc filter is almost totally unappealing for an MSAA resolve, since the ringing artifacts that it produces are very prominent. Here are three images comparing a 4-pixel-wide Catmull-Rom filter, a 4-pixel-wide Mitchell filter, and a 6-pixel-wide sinc filter:</p>
<p><a href="/images/converted/msaa-resolve-filters/pic5_catmullrom.png"><img src="/images/converted/msaa-resolve-filters/pic5_catmullrom.png" alt="" title="Pic5_CatmullRom"></a></p>
<p><a href="/images/converted/msaa-resolve-filters/pic5_mitchell.png"><img src="/images/converted/msaa-resolve-filters/pic5_mitchell.png" alt="" title="Pic5_Mitchell"></a></p>
<p><a href="/images/converted/msaa-resolve-filters/pic5_sinc.png"><img src="/images/converted/msaa-resolve-filters/pic5_sinc.png" alt="" title="Pic5_Sinc"></a></p>
<p>All of the above images used 4xMSAA, but a wider filter kernel can also work well for 2xMSAA. Here&rsquo;s some close-ups showing &ldquo;normal&rdquo; 2xMSAA vs. 2xMSAA with a B-spline filter vs. 4xMSAA with a B-spline filter:</p>
<p><a href="/images/converted/msaa-resolve-filters/pic4_2x_normal.png"><img src="/images/converted/msaa-resolve-filters/pic4_2x_normal.png" alt="" title="Pic4_2x_Normal"></a><a href="/images/converted/msaa-resolve-filters/pic4_2x_bspline.png"><img src="/images/converted/msaa-resolve-filters/pic4_2x_bspline.png" alt="" title="Pic4_2x_BSpline"></a><a href="/images/converted/msaa-resolve-filters/pic4_4x_bspline.png"><img src="/images/converted/msaa-resolve-filters/pic4_4x_bspline.png" alt="" title="Pic4_4x_BSpline"></a></p>
<center><i>2xMSAA with standard resolve, 2xMSAA with a 3-pixel-wide B-spline filter, and 4xMSAA with a 3-pixel-wide B-spline filter</i></center>
<p>To wrap things up, here a two close-ups showing the results of a wide B-spine filter applied before tone mapping, and the same image with tone mapping applied prior to filtering:</p>
<p><a href="/images/converted/msaa-resolve-filters/pic3_normal.png"><img src="/images/converted/msaa-resolve-filters/pic3_normal.png" alt="" title="Pic3_Normal"></a><a href="/images/converted/msaa-resolve-filters/pic3_tonemap.png"><img src="/images/converted/msaa-resolve-filters/pic3_tonemap.png" alt="" title="Pic3_ToneMap"></a></p>
<center><i>4xMSAA with a 3-pixel wide B-spline filter applied before tone mapping, and after tone mapping</i></center>
<p>These results are little interesting since they illustrate the differences in the two different approaches. In the first image the filtering is performed with HDR values, so you get similar effects to applying DOF or motion blur in HDR where bright values can dominate their local neighborhood. The second image shows quite a different result, where the darker geometry actually ends up looking &ldquo;thicker&rdquo; against the bright blue sky. In general I don&rsquo;t find that it produces a substantial improvement when you&rsquo;re already using a wider filter kernel, or at least not enough to justify the extra effort and performance required to make it work with an HDR post-processing pipeline. However it does tend to play nicer with cubic filters that have negative lobes, since you&rsquo;re not filtering HDR values with arbitrary intensity.</p>
<h2 id="conclusions">Conclusions</h2>
<p>There are clearly a lot of options available to you if you choose to implement a custom MSAA resolve. I think there&rsquo;s some good opportunities here to do an even better job at reducing aliasing in games, and personally I&rsquo;m of the opinion that it&rsquo;s worth reducing the appearance of tiny pixel-wide details if it results in an overall cleaner image. Either way I don&rsquo;t think that a box filter is the best choice no matter what your tastes are.</p>
<p>If you want to download the sample app with source code, you can grab it <a href="https://github.com/TheRealMJP/DX11Samples/releases/tag/v1.1">here</a>. Feel free to download it and perform your own experiments, although I&rsquo;d appreciate if you&rsquo;d share your results and opinions!</p>
<p>Some quick notes about the sample code: I decided to use the newer DirectX headers and libraries from the Windows 8 SDK, so you&rsquo;ll need to have it installed if you want to compile the project. I haven&rsquo;t fully migrated to VS 2012 yet, so I&rsquo;ve left the project in VS 2010 format so that it can be opened by either. I also overhauled a lot of my sample framework code, which includes a shader caching system that uses the totally-awesome <a href="https://sites.google.com/site/murmurhash/">MurmurHash</a> to detect when a shader needs to be re-compiled. Using the new SDK also entailed ditching D3DX, so I&rsquo;ve replaced the texture and mesh loading functionality that I was using with some of my own code combined with some code lifted from <a href="http://directxtk.codeplex.com">DirectXTK</a>. One major downside I should mention is that I&rsquo;m only supporting x64 for now, due to some annoyances with the SDK and redistributing D3DCompiler DLL&rsquo;s. If anybody is still stuck on 32-bit Windows and really wants to run the sample, let me know and I&rsquo;ll try to find some time to get it working.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22macroblock16x16@gmail.com%22">Bryant</a> - <time datetime="2013-01-06 00:13:52">Jan 0, 2013</time></p>
<p>@MJP This is really interesting work, It&rsquo;s unfortunate that you can&rsquo;t use shaders to do custom sample arrangements; it would be great to have access to the samples in order to set your own sample count and arrangement (jittered, poisson, etc). You included sinc in the test, have you done any further tests with windowed sinc functions? It would be interesting to see the effects of reconstruction with a sinc function using different windowing functions, particularly as the breadth of the kernel increases. You&rsquo;ve said your kernels are symmetrical, I know the guys over at ImageMagick have added symmetrical kernels to their resampling with the -distort operator that makes use of elliptical weighted averaging. Have you had any discussions with them? Great articles as usual, it&rsquo;s great talking to you guys from Beyond3D.</p>
<hr />
####
[Timothy Lottes](http://timothylottes.blogspot.com/ "timothylottes@gmail.com") - <time datetime="2012-10-30 09:26:03">Oct 2, 2012</time>
<p>Here is another trick I am using in my title. When correctly doing a linear HDR resolve, linear HDR post processing, then tonemapping to LDR right before scan-out (long after the resolve), bloom plays an important roll in removing any aliasing left over by extreme high dynamic range samples. Usually the tail section of the resolve filter kernel (the smallest coef) help in removing HDR aliasing, when this fails, using bloom to distribute the energy can remove (or rather hide) the remaining aliasing. In order to make the bloom pass fast, one can do an extremely low-cost 1/2x1/2 resolution down-sample during the resolve shader. Note the resolve kernels are radially symmetric. Taking 20 samples in a circle at 4xMSAA, there are 5 sets of 4 samples where each set has the same kernel weight. Taking the sum of samples in each set results in 5 new &ldquo;ring&rdquo; samples. It is fast to both compute the filtered resolve output (weight sum of the 5 &ldquo;ring&rdquo; samples), and the average of the 16 samples in a 2x2 pixel box centered on the pixel (average of 4 &ldquo;ring&rdquo; samples). Then use the standard pixel shader output to store the filtered resolve. And in the same pixel shader, use surface stores (imageStore() in GL) to write out the 2x2 pixel average to the 1/2x1/2 resolution target. Predicate the store to the upper-left pixel of each 2x2 pixel quad. Afterwards the 1/2x1/2 resolution target has the box filtered down-sample (which still needs proper filtering, but at least is fully energy conserving), but just shifted towards the upper-left pixel. It is important to &ldquo;undo&rdquo; the shift when doing the first bloom filtering pass.</p>
<hr />
####
[Timothy Lottes](http://timothylottes.blogspot.com/ "timothylottes@gmail.com") - <time datetime="2012-10-30 08:34:28">Oct 2, 2012</time>
<p>Re &ldquo;randomized sampling&rdquo;, I have not tried this either. Something to keep in mind, in theory, fixed sampling locations can be optimized by the driver into constants avoiding address computation. Fixed sample locations also insure that the filter weight computation can be factored into constants. Also the energy of the kernel is mostly in the samples inside the pixel (say over 50%, for 4 samples for 4xMSAA), so one always wants to include those samples. The rest of the energy distributed over a much larger number of samples (say up to 32 samples if using a 3x3 pixel kernel). In the aim of reducing samples, it probably makes more sense to do a fixed truncation of samples towards the outside of the kernel as they contribute less energy. For my game (a personal independent project) I&rsquo;ve been using 4xMSAA with 16 or 20 samples (circle pattern instead of full 32 in the 3x3 pixel square) using kernel {B=1,C=0} (no negative lobes) and resolving in one pass.</p>
<hr />
####
[Osamar]( "osamar2001@hotmail.com") - <time datetime="2012-10-30 07:21:24">Oct 2, 2012</time>
<p>I have not the mathematical or programming knoledge. But, it could be faster to calculate a &ldquo;step pyramid&rdquo; like filter, or all cost more or less the same?</p>
<hr />
####
[AmazingFactory]( "first10@btinternet.com") - <time datetime="2012-10-30 05:47:33">Oct 2, 2012</time>
<p>Impressive work The &ldquo;Filtering in PRMan&rdquo; article has a great description of aliasing: Aliasing = high frequency data masquerading as low-frequency data (jaggies, Moiré, noise, sparkling, popping, etc.) that results from sampling a signal with improper filtering. And since the aliasing is mixed in with the real low-frequency data, once added it cannot be removed.</p>
<hr />
####
[mkh]( "") - <time datetime="2013-03-02 06:39:48">Mar 6, 2013</time>
<p>Nice article! I&rsquo;m interested in playing with the sample, however the download at Codeplex seems to be broken. Could you reupload the sample? Thanks alot! Cheers!</p>
<hr />
####
[Jeremy Shopf &raquo; Image Reconstruction and MSAA](http://jshopf.com/blog/?p=304 "") - <time datetime="2012-11-01 17:15:37">Nov 4, 2012</time>
<p>[&hellip;] posted a truly excellent read regarding resolving multi-sampled images titled “Experimenting with Reconstruction Filters for MSAA Resolve”. Even better, he had previously posted a nice sampling theory primer and introduction to MSAA which [&hellip;]</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2013-03-02 12:49:56">Mar 6, 2013</time>
<p>The Codeplex link still seems to be working for me, but just in case I uploaded the zip file to my Google Drive as well: <a href="https://docs.google.com/file/d/0B9NkyJjiExTjR3Z0YzRIQUdYWkU/edit?usp=sharing">https://docs.google.com/file/d/0B9NkyJjiExTjR3Z0YzRIQUdYWkU/edit?usp=sharing</a></p>
<hr />
####
[anteru]( "wordpress@catchall.shelter13.net") - <time datetime="2012-10-29 00:09:51">Oct 1, 2012</time>
<p>Cool, I did something similar (but less extensive) two years ago: <a href="https://bitbucket.org/Anteru/wideaaresolve">https://bitbucket.org/Anteru/wideaaresolve</a> That was partially fallout from the SRAA research, which requires a wider resolve for good quality. Especially on the test scene I used, with lots of contrast, Lanczos resolve is so much better than the standard MSAA that it&rsquo;s not even funny any more. One thing I never got around to try is randomized sampling, by picking a subset of the samples for each pixel. This will introduce noise, but should make it faster as you need to fetch less samples per pixel.</p>
<hr />
####
[Timothy Lottes]( "tlottes@nvidia.com") - <time datetime="2012-10-29 08:56:14">Oct 1, 2012</time>
<p>Awesome write up. As for performance of custom resolves, I&rsquo;ve got a few performance related bugs filled on this at NVIDIA so If anyone is looking to ship a title using a wide resolve kernel, please send email note to nvidia.com, user tlottes, and I can use as leverage to get higher priority getting those (software) bugs fixed.</p>
<hr />
####
[deadc0de](http://c0de517e.blogspot.ca "kenpex@tin.it") - <time datetime="2012-10-29 20:00:23">Oct 1, 2012</time>
<p>You rock - Angelo</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-10-29 20:39:44">Oct 1, 2012</time>
<p>@anteru: that&rsquo;s a really cool demo, I&rsquo;ve never seen it before! What you&rsquo;d *really* want to improve the sampling quality is to adjust the sampling positions used for rasterization per-pixel, but unfortunately with GPU&rsquo;s it seems that we&rsquo;re stuck with a fixed pattern. @Timothy: thank you! I&rsquo;ll be sending you an email shortly. @Angelo: thank you sir. :-)</p>
<hr />
####
[Image Reconstruction and MSAAJeremy Shopf](http://www.jshopf.net/?p=304 "") - <time datetime="2013-08-05 18:24:48">Aug 1, 2013</time>
<p>[…] posted a truly excellent read regarding resolving multi-sampled images titled “Experimenting with Reconstruction Filters for MSAA Resolve”. Even better, he had previously posted a nice sampling theory primer and introduction to MSAA which […]</p>
<hr />
####
[SIGGRAPH FOLLOW-UP: 2015 Edition | The Danger Zone](https://mynameismjp.wordpress.com/2015/08/16/siggraph-2015/ "") - <time datetime="2015-08-16 21:18:04">Aug 0, 2015</time>
<p>[…] play around with the shaders. The sample is essentially a successor to the MSAAFilter sample that I put out nearly 3 years ago, which accompanied a blog post where I shared some of my research on using higher-order filtering […]</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2017-06-01 21:17:24">Jun 4, 2017</time>
<p>Hey there, that was a bug in the original implementation. I had fixed it at some point, but I guess that the version uploaded here is still the original with the bug. Anyhow, I would suggest looking at the newer MSAAFilter sample that I posted on GitHub, since that one improves on the old sample in just about every way: <a href="https://github.com/TheRealMJP/MSAAFilter">https://github.com/TheRealMJP/MSAAFilter</a></p>
<hr />
####
[Simpetar]( "simpetar@yandex.ru") - <time datetime="2017-06-01 07:54:09">Jun 4, 2017</time>
<p>Sorry, may be i missed something but i cannot understand the line 196 in your resolve.hlsl shader: SampleOffset += SampleOffsets[subSampleIdx].xy; Why is here += in the inner loop? As far as i understand it will be correct only for index 0. Thank you for your answer.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>A Quick Overview of MSAA</title>
			<link>https://therealmjp.github.io/posts/msaa-overview/</link>
			<pubDate>Thu, 25 Oct 2012 07:03:27 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/msaa-overview/</guid>
			<description>Previous article in the series: Applying Sampling Theory to Real-Time Graphics
Updated 1/27/2016 - replaced the MSAA partial coverage image with a new image that illustrates subsamples being written to, as suggested by Simon Trümpler.
MSAA can be a bit complicated, due to the fact that it affects nearly the entire rasterization pipeline used in GPU’s. It’s also complicated because really understanding why it works requires at least a basic understanding of signal processing and image resampling.</description>
			<content:encoded><![CDATA[<p><em>Previous article in the series: <a href="../applying-sampling-theory-to-real-time-graphics/">Applying Sampling Theory to Real-Time Graphics</a></em></p>
<p><em>Updated 1/27/2016 - replaced the MSAA partial coverage image with a new image that illustrates subsamples being written to, as suggested by <a href="http://simonschreibt.de/game-art-tricks/">Simon Trümpler</a>.</em></p>
<p>MSAA can be a bit complicated, due to the fact that it affects nearly the entire rasterization pipeline used in GPU’s. It’s also complicated because really understanding <em>why</em> it works requires at least a basic understanding of signal processing and image resampling. With that in mind I wanted to provide an quick overview of how MSAA works on a GPU, in order to provide the some background material for the following article where we’ll experiment with MSAA resolves. Like the previous article on signal processing, feel free to skip if you’re already an expert. Or better yet, read through it and correct my mistakes!</p>
<h2 id="rasterization-basics">Rasterization Basics</h2>
<p>A modern D3D11-capable GPU features hardware-supported rendering of point, line, and triangle primitives through <em>rasterization</em>. The rasterization pipeline on a GPU takes as input the <em>vertices</em> of the primitive being rendered, with vertex positions provided in the homogeneous clip space produced by transformation by some projection matrix.  These positions are used to determine the set of pixels in the current render target where the triangle will be visible. This visible set is determined from two things: <em>coverage</em>, and <em>occlusion</em>. Coverage is determined by performing some test to determine if the primitive overlaps a given pixel. In GPU’s, coverage is calculated by testing if the primitive overlaps a single sample point located in the exact center of each pixel <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. The following image demonstrates this process for a single triangle:</p>
<p><a href="/images/converted/msaa-overview/coverage.png"><img src="/images/converted/msaa-overview/coverage.png" alt="" title="Coverage"></a></p>
<center><i>Coverage being calculated for a rasterized triangle. The blue circles represent a grid of sample points, each located at the center of a pixel. The red circles represent sample points covered by the triangle.</i></center>
<p>Occlusion tells us whether a pixel covered by a primitive is also covered by any other triangles, and is handled by <em>z-buffering</em> in GPU’s. A z-buffer, or depth buffer, stores the depth of the closest primitive relative to the camera at each pixel location. When a primitive is rasterized, its interpolated depth is compared against the value in the depth buffer to determine whether or not the pixel is occluded. If the depth test succeeds, the appropriate pixel in the depth buffer is updated with new closest depth. One thing to note about the depth test is that while it is often shown as occurring after pixel shading, almost all modern hardware can execute some form of the depth test before shading occurs. This is done as an optimization, so that occluded pixels can skip pixel shading. GPU’s still support performing the depth test after pixel shading in order to handle certain cases where an early depth test would produce incorrect results. One such case is where the pixel shader manually specifies a depth value, since the depth of the primitive isn’t known until the pixel shader runs.</p>
<p>Together, coverage and occlusion tells us the <em>visibility</em> of a primitive. Since visibility can be defined as 2D function of X and Y, we can treat it as a signal and define its behavior in terms of concepts from signal processing. For instance, since coverage and depth testing is performed at each pixel location in the render target the visibility sampling rate is determined by the X and Y resolution of that render target. We should also note that triangles and lines will inherently have discontinuities, which means that the signal is not bandlimited and thus no sampling rate will be adequate to avoid aliasing in the general case.</p>
<h2 id="oversampling-and-supersampling">Oversampling and Supersampling</h2>
<p>While it’s generally impossible to completely avoid aliasing of an arbitrary signal with infinite frequency, we can still reduce the appearance of aliasing artifacts through a process known as <em>oversampling</em>. Oversampling is the process of sampling a signal at some rate that’s higher than our intended final output, and then reconstructing and resampling the signal again at the output sample rate. As you&rsquo;ll recall from the first article, sampling at a higher rate causes the  clones of a signal’s spectrum to be further apart. This results in less of the higher-frequency components leaking into the reconstructed version of the signal, which in the case of an image means a reduction in the appearance of aliasing artifacts.</p>
<p>When applied to graphics and 2D images we call this <em>supersampling,</em> often abbreviated as <em>SSAA</em>. Implementing it in a 3D rasterizer is trivial: render to some resolution higher than the screen, and then downsample to screen resolution using a reconstruction filter. The following image shows the results of various supersampling patterns applied to a rasterized triangle:</p>
<p><a href="/images/converted/msaa-overview/supersampling-patterns.png"><img src="/images/converted/msaa-overview/supersampling-patterns.png" alt="" title="Supersampling Patterns"></a></p>
<center><i>Supersampling applied to a rasterized triangle, using various sub-pixel patterns. Notice how aliasing is reduced as the sample rate increases, even though the number of pixels is the same in all cases. Image from Real-Time Rendering, 3rd Edition, A K Peters 2008</i></center>
<p>The simplicity and effectiveness of supersampling resulted in it being offered as a driver option for many early GPU’s. The problem, however, is performance. When the resolution of the render target is increased, the sampling rate of visibility increases. However since the execution of the pixel shader is also tied to the resolution of the pixels, the pixel shading rate would also increase. This meant that any work performed in the pixel shader, such as lighting or texture fetches, would be performed at a higher rate and thus consume more resources. The same goes for bandwidth used when writing the results of the pixel shader to the render target, since the write (and blending, if enabled) is performed for each pixel. Memory consumption is also increased, since the render target and corresponding z buffer must be larger in size. Because of these adverse performance characteristics, supersampling was mostly relegated to a high-end feature for GPU’s with spare cycles to burn.</p>
<h2 id="supersampling-evolves-into-msaa">Supersampling Evolves into MSAA</h2>
<p>So we’ve established that supersampling works in principle for reducing aliasing in 3D graphics, but that it’s also prohibitively expensive. In order to keep most of the benefit of supersampling without breaking the bank in terms of performance, we can observe that aliasing of triangle visibility function (AKA geometric aliasing) only occurs at the edges of rasterized triangles. If we hopped into a time machine and traveled back to 2001, we would also observe that pixel shading mostly consists of texture fetches and thus doesn’t suffer from aliasing (due to mipmaps). These observations would lead us to conclude that geometric aliasing is the primary form of aliasing for games, and should be our main focus. This conclusion is what what caused MSAA to be born.</p>
<p>In terms of rasterization, MSAA works in a similar manner to supersampling. The coverage and occlusion tests are both performed at higher-than-normal resolution, which is typically 2x through 8x. For coverage, the hardware implements this by having N sample points within a pixel, where N is the multisample rate. These samples are known as subsamples, since they are sub-pixel samples. The following image shows the subsample placement for a typical 4x MSAA rotated grid pattern:</p>
<p><a href="/images/converted/msaa-overview/msaa_4xpattern.png"><img src="/images/converted/msaa-overview/msaa_4xpattern.png" alt="" title="MSAA_4xPattern"></a></p>
<center><i>Typical MSAA 4x Sample Pattern</i></center>
<p>The triangle is tested for coverage at each of the N sample points, essentially building a bitwise <em>coverage mask</em> representing the portion of the pixel covered by a triangle <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. For occlusion testing, the triangle depth is interpolated at each covered sample point and tested against the depth value in the z buffer. Since the depth test is performed for each subsample and not for each pixel, the size of the depth buffer must be augmented to store the additional depth values. In practice this means that the depth buffer will N times the size of the non-MSAA case. So for 2xMSAA the depth buffer will be twice the size, for 4x it will be four times the size, and so on.</p>
<p>Where MSAA begins to differ from supersampling is when the pixel shader is executed. In the standard MSAA case, the pixel shader is not executed for each subsample. Instead, the pixel shader is executed only once for each pixel where the triangle covers at least one subsample. Or inwords, it is executed once for each pixel where the coverage mask is non-zero. At this point pixel shading occurs in the same manner as non-MSAA rendering: the vertex attributes are interpolated to the center of the pixel and used by the pixel shader to fetch textures and perform lighting calculations. This means that the pixel shader cost does not increase substantially when MSAA is enabled, which is the primary benefit of MSAA over supersampling.</p>
<p>Although we only execute the pixel shader once per covered pixel, it is not sufficient to store only one output value per pixel in the render target. We need the render target to support storing multiple samples, so that we can store the results from multiple triangles that may have partially covered a single pixel. Therefore an MSAA render target will have enough memory to store N subsamples for each pixel. This is conceptually similar to an MSAA z buffer, which also has enough memory to store N subsamples. Each subsample in the render target is mapped to one of the subsample points used during rasterization to determine coverage. When a pixel shader outputs its value, the value is only written to subsamples where both the coverage test and the depth test passed for that pixel. So if a triangle covers half the sample points in 4x sample pattern, then half of the subsamples in the render target receive the pixel shader output value. Or if all of the sample points are covered, then all of the subsamples receive the output value. The following image demonstrates this concept:</p>
<p><a href="/images/converted/msaa-overview/msaa_partial_coverage2.png"><img src="/images/converted/msaa-overview/msaa_partial_coverage2_resized_756.png" alt="MSAA_Partial_Coverage2"></a>
<em>Results from non-MSAA and 4x MSAA rendering when a triangle partially covers a pixel. Based on an image from Real-Time Rendering, 3rd Edition</em></p>
<p>By using the coverage mask to determine which subsamples to be updated, the end result is that a single pixel can end up storing the output from N different triangles that partially cover the sample pixel. This effectively gives us the result we want, which is an oversampled form of triangle visibility. The following image, taken from the <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/cc627092%28v=vs.85%29.aspx">Direct3D 10 documentation</a>[1], visually summarizes the rasterization process for the case of 4xMSAA:</p>
<p><a href="/images/converted/msaa-overview/msaa_rasterization.png"><img src="/images/converted/msaa-overview/msaa_rasterization.png" alt="" title="MSAA_Rasterization"></a></p>
<center><i>An image from the D3D10 documentation detailing the results of rasterizing various primitives with 4xMSAA.</i></center>
<h2 id="msaa-resolve">MSAA Resolve</h2>
<p>As with supersampling, the oversampled signal must be resampled down to the output resolution before we can display it. With MSAA, this process is referred to as <em>resolving</em> the render target. In its earliest incarnations, the resolve process was carried out in fixed-function hardware on the GPU. The filter commonly used was a 1-pixel-wide box filter, which essentially equates to averaging all subsamples within a given pixel. Such a filter produces results such that fully-covered pixels end up with the same result as non-MSAA rendering, which could be considered either good or bad depending on how you look at it (good because you won&rsquo;t unintentially reduce details through blurring, bad because a box filter will introduce postaliasing). For pixels with triangle edges, you get a trademark gradient of color values with a number of steps equal to the number of sub-pixel samples. Take a look at the following image to see what this gradient looks like for various MSAA modes:</p>
<p><a href="/images/converted/msaa-overview/msaa-edges.png"><img src="/images/converted/msaa-overview/msaa-edges.png" alt="" title="MSAA Edges"></a></p>
<center><i>Trademark MSAA edge gradients resulting from reconstruction using box filtering</i></center>
<p>One notable exception to box filtering was Nvidia’s “Quincunx” AA, which was available as a driver option on their DX8 and DX9-era hardware (which includes the RSX used by the PS3). When enabled, it would use a 2-pixel-wide triangle filter centered on one of the samples in a 2x MSAA pattern. The “quincunx” name comes the fact that the resolve process ends up using 5 subsamples that are arranged in the cross-shaped quincunx pattern. Since the quincunx resolve uses a wider reconstruction filter, aliasing is reduced compared to the standard box filter resolve. However, using a wider filter can also result in unwanted attenuation of higher frequencies. This can lead to a “blurred” look that appears to lack details, which is a complaint sometimes levied against PS3 games that have used the feature. AMD later added a similar feature to their 3 and 4000-series GPU’s called “Wide Tent” that also made use of a triangle filter with width greater than a pixel.</p>
<p>As GPU’s became more programmable and the API’s evolved to match them, we eventually gained the ability to perform the MSAA resolve in a custom shader instead of having to rely on an API function to do that. This is an ability we’re going to explore in the following article.</p>
<h2 id="compression">Compression</h2>
<p>As we saw earlier, MSAA doesn’t actually improve on supersampling in terms of rasterization complexity or memory usage. At first glance we might conclude that the only advantage of MSAA is that pixel shader costs are reduced. However this isn’t actually true, since it’s also possible to improve bandwidth usage. Recall that the pixel shader is only executed once per pixel with MSAA. As a result, the same value is often written to all N subsamples of an MSAA render target. GPU hardware is able to exploit this by sending the pixel shader value coupled with another value indicating which subsamples should be written, which acts as a form of lossless compression. With such a compression scheme the bandwidth required to fill an MSAA render target can be significantly less than it would be for the supersampling case.</p>
<h2 id="csaa-and-eqaa">CSAA and EQAA</h2>
<p>Since its introduction, the fundamentals of MSAA have not seen significant changes as graphics hardware has evolved. We already discussed the special resolve modes supported by the drivers for certain Nvidia and ATI/AMD hardware as well as the ability to arbitrarily access subsample data in an MSAA render target, which are two notable exceptions. A third exception has been Nvidia’s <em>Coverage Sampling Antialiasing</em>(CSAA)[2] modes supported by their DX10 and DX11 GPU’s. These modes seek to improve the quality/performance ratio of MSAA by decoupling the coverage of triangles within a pixel from the subsamples storing the value output by the pixel shader. The idea is that while subsamples have high storage cost since they store pixel shader outputs, the coverage can be stored as a compact bitmask. This is exploited by rasterizing at a certain subsample rate and storing coverage at that rate, but then storing the actual subsample values at a lower rate. As an example, the “8x” CSAA mode stored 8 coverage samples and 4 pixel shader output values. When performing the resolve, the coverage data is used to augment the quality of the results. Unfortunately Nvidia does not provide public documentation of this step, and so the specifics will not be discussed here. They also do not provide programmatic access to the coverage data in shaders, thus the data will only be used when performing a standard resolve through D3D or OpenGL functions.</p>
<p>AMD has introduced a very similar feature in their 6900 series GPU’s, which they’ve named <em>EQAA</em>[3]. Like Nvidia, the feature can be enabled through driver options or special MSAA quality modes but it cannot be used in custom resolves performed via shaders.</p>
<h2 id="working-with-hdr-and-tone-mapping">Working with HDR and Tone Mapping</h2>
<p>Before HDR became popular in real-time graphics, we essentially rendered display-ready color values to our MSAA render target with only simple post-processing passes applied after the resolve. This meant that after resolving with a box filter, the resulting gradients along triangle edges would be perceptually smooth between neighboring pixels<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>. However when HDR, exposure, and tone mapping are thrown into the mix there is no longer anything close to a linear relationship between the color rendered at each pixel and the perceived color displayed on the screen. As a result, you are no longer guaranteed to get the smooth gradient you would get when using a box filter to resolve LDR MSAA samples. This can seriously affect the output of the resolve, since it can end up appearing as if no MSAA is being used at all if there is extreme contrast on a geometry edge.</p>
<p>This strange phenomenon was first pointed out (to my knowledge) by <a href="https://twitter.com/_Humus_">Humus</a> (Emil Persson), who created a sample[4] demonstrating it as well as a corresponding ShaderX6 article. In this same sample he also demonstrated an alternative approach to MSAA resolves, where he used a custom resolve to apply tone mapping to each subsample individually before filtering. His results were pretty striking, as you can see from these images (left is a typical resolve, right is resolve after tone mapping):</p>
<p><a href="/images/converted/msaa-overview/humus_normal.png"><img src="/images/converted/msaa-overview/humus_normal.png" alt="" title="Humus_Normal"></a>
<a href="/images/converted/msaa-overview/humus_resolve.png"><img src="/images/converted/msaa-overview/humus_resolve.png" alt="" title="Humus_Resolve"></a></p>
<center><i>HDR rendering with MSAA. The top image applies tone mapping after a standard MSAA resolve, while the bottom image applies tone mapping before the MSAA resolve.</i></center>
<p>It’s important to think about what it actually means to apply tone mapping before the resolve. Before tone mapping, we can actually consider ourselves to be working with values representing physical quantities of light within our simulation. Primarily, we’re dealing with the <em>radiance</em> of light reflecting off of a surface towards the eye. During the tone mapping phase, we attempt to convert from a physical quantity of light to a new value representing the color that should be displayed on the screen. What this means is that by changing where the resolve takes places, we’re actually oversampling a different signal! When resolving before tone mapping we’re oversampling the signal representing physical light being reflected towards the camera, and when resolving after tone mapping we’re oversampling the signal representing colors displayed on the screen. Therefore an important consideration we have to make is which signal we actually want to oversample. This directly ties into post-processing, since a modern game will typically have several post-processing effects needing to work with HDR radiance values rather than display colors. Thus we want to perform tone mapping as the last step in our post-processing chain. This presents a potential difficulty with the approach of tone mapping prior to resolve, since it means that all previous post-processing steps must work with a non-resolved MSAA as an input and also produce an MSAA buffer as an output. This can obviously have serious memory and performance implications, depending on how the passes are implemented.</p>
<p>Update 8/26/2017: a much simpler and more practical alternative to performing tone mapping at MSAA resolution is to instead use the following process:</p>
<ol>
<li>During MSAA resolve of an HDR render target, apply tone mapping and exposure to each sub-sample</li>
<li>Apply a reconstruction filter to each sub-sample to compute the resolved, tone mapped value</li>
<li>Apply the inverse of tone mapping and exposure (or an approximation) to go back to linear HDR space</li>
</ol>
<p>Doing it this way lets you apply the resolve before post-processing, while still giving you much higher-quality results than a standard resolve. I&rsquo;m not sure who originally came up with this idea, but I recall seeing it mentioned on the Beyond3D forums many years ago. Either way it was popularized by Brian Karis through a <a href="http://graphicrants.blogspot.com/2013/12/tone-mapping.html">blog post</a>. You can see it in action in my updated <a href="https://github.com/TheRealMJP/MSAAFilter">MSAA/TAA sample</a> on GitHub, as well as in <a href="http://advances.realtimerendering.com/s2015/rad_siggraph_advances_2015.pptx">my presentation</a> from SIGGRAPH 2015.</p>
<h2 id="mlaa-and-other-post-process-aa-techniques">MLAA and Other Post-Process AA Techniques</h2>
<p><em>Morphological Anti-Aliasing</em> is an anti-aliasing technique originally developed by Intel[5] that initiated a wave of performance-oriented AA solutions commonly referred to as <em>post-process anti-aliasing</em>. This name is due to the fact that they do not fundamentally alter the rendering/rasterization pipeline like MSAA does. Instead, they work with only a non-MSAA render target to produce their results. In this way these techniques are rather interesting, in that they do not actually rely on increasing the sampling rate in order to reduce aliasing. Instead, they use what could be considered an advanced reconstruction filter in order to approximate the results that you would get from oversampling. In the case of MLAA in particular, this reconstruction filter uses pattern-matching in an attempt to detect the edges of triangles. The pattern-matching relies on the fact that for a fixed sample pattern, common patterns of pixels will be produced by the rasterizer for a triangle edge. By examining the color of the local neighborhood of pixels, the algorithm is able to estimate where a triangle edge is located and also the orientation of the line making up that particular edge. The edge and color information is then enough to estimate an analytical description of that particular edge, which can be used to calculate the exact fraction of the pixel that will be covered by the triangle. This is very powerful if the edge was calculated correctly, since it eliminates the need for multiple sub-pixel coverage samples. In fact if the coverage amount is used to blend the triangle color with the color behind that triangle, the results will match the output of standard MSAA rendering with infinite subsamples! The following image shows some of the patterns used for edge detection, and the result after blending:</p>
<p><a href="/images/converted/msaa-overview/mlaa_patterns.png"><img src="/images/converted/msaa-overview/mlaa_patterns.png" alt="" title="MLAA_Patterns"></a></p>
<center><i>MLAA edge detection using pattern recognition (from MLAA: Efficiently Moving Antialiasing from the GPU to the CPU)</i></center>
<p>The major problems with MLAA and similar techniques occur when the algorithm does <em>not</em> accurately estimate the triangle edges. Looking at only a single frame, the resulting artifacts would be difficult or impossible to discern. However in a video stream the problems become apparent due to sub-pixel rotations of triangles that occur as the triangle or the camera move in world space. Take a look at the following image:</p>
<p><a href="/images/converted/msaa-overview/mlaa_rotation.png"><img src="/images/converted/msaa-overview/mlaa_rotation.png" alt="" title="MLAA_Rotation"></a></p>
<center><i>Two different triangle edge orientations resulting in the same rasterization pattern</i></center>
<p>In this image, the blue line represents a triangle edge during one frame and the green line represents the same triangle edge in the following frame. The orientation of the edge relative to the pixels has changed, however in both cases only the leftmost pixel is marked as being “covered” by the rasterizer. Consequently the same pixel pattern (marked by the blue squares in the image) is produced by the rasterizer for both frames, and the MLAA algorithm detects the same edge pattern (denoted by the thick red line in the image). As the edge continues rotating, eventually it will cover the top-middle pixel’s sample point and that that pixel will “turn on”. In the resulting video stream that pixel will appear to “pop on”, rather than smoothly transitioning from a non-covered state to a covered state. This is a trademark temporal artifact of geometric aliasing, and MLAA is incapable of reducing it. The artifact can be even more objectionable for thin or otherwise small geometry, where entire portions of the triangle will appear and disappear from frame to frame causing a “flickering” effect. MSAA and supersampling are able to reduce such artifacts due to the increased sampling rate used by the rasterizer, which results in several “intermediate” steps in the case of sub-pixel movement rather than pixels “popping” on and off. The following animated GIFs demonstrate this effect on a single rotating triangle<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<p><a href="/images/converted/msaa-overview/triangle_fxaa1.gif"><img src="/images/converted/msaa-overview/triangle_fxaa1.gif" alt="" title="Triangle_FXAA"></a>
<a href="/images/converted/msaa-overview/triangle_msaa1.gif"><img src="/images/converted/msaa-overview/triangle_msaa1.gif" alt="" title="Triangle_MSAA"></a></p>
<center><i>Two animations of a rotating triangle. The top image has FXAA enabled, which uses techniques similar to MLAA to reconstruct edges. The bottom edge uses 4x MSAA, which supersamples the visibility test at the edges of the triangle. Notice how in the MSAA image pixels will transition through intermediate values as the triangle moves across the sub-pixel sample points. The FXAA image lacks this characteristic, despite producing smoother gradients along the edges.</i></center>
<p>Another potential issue with MLAA and similar algorithms is that they may fail to detect edges or detect “false” edges if only color information is used. In such cases the accuracy of the edge detection can be augmented by using a depth buffer and/or a normal buffer. Another potential issue is that the algorithm uses the color adjacent to a triangle as a proxy for the color behind the triangle, which could actually be different. However this tends to be non-objectionable in practice.</p>
<h2 id="references">References</h2>
<p>[1] <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/cc627092%28v=vs.85%29.aspx">http://msdn.microsoft.com/en-us/library/windows/desktop/cc627092%28v=vs.85%29.aspx</a> <br>
[2] <a href="http://www.nvidia.com/object/coverage-sampled-aa.html">http://www.nvidia.com/object/coverage-sampled-aa.html</a> <br>
[3] <a href="http://developer.amd.com/Resources/archive/ArchivedTools/gpu/radeon/assets/EQAA%20Modes%20for%20AMD%20HD%206900%20Series%20Cards.pdfhttp://developer.amd.com/Resources/archive/ArchivedTools/gpu/radeon/assets/EQAA%20Modes%20for%20AMD%20HD%206900%20Series%20Cards.pdf" title="http://developer.amd.com/Resources/archive/ArchivedTools/gpu/radeon/assets/EQAA Modes for AMD 6900 Series Cards.pdf">http://developer.amd.com/Resources/archive/ArchivedTools/gpu/radeon/assets/EQAA Modes for AMD HD 690 Series Cards.pdf</a> <br>
[4] <a href="http://www.humus.name/index.php?page=3D&amp;ID=77">http://www.humus.name/index.php?page=3D&amp;ID=77</a> <br>
[5] <a href="http://software.intel.com/en-us/articles/mlaa-efficiently-moving-antialiasing-from-the-gpu-to-the-cpu/">MLAA: Efficiently Moving Antialiasing from the GPU to the CPU</a><br></p>
<p><em>Next article in the series: <a href="../msaa-resolve-filters/">Experimenting with Reconstruction Filters for MSAA Resolve</a></em></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://kartones.net/blogs/lawebdeprogramacion/archive/2012/11/11/noticias-11-11-2012.aspx" title="">Noticias 11-11-2012 - La Web de Programación</a> - <time datetime="2012-11-11 05:40:16">Nov 0, 2012</time></p>
<p>[&hellip;] Excelente artículo explicando que es el MSAA (Multi Sampling Anti Aliasing): Técnico y detallado. [&hellip;]</p>
<hr />
####
[A Closer Look At Mantle and EQAA In Civilization](http://forums.hexus.net/graphics-cards/332789-closer-look-mantle-eqaa-civilization.html#post3400117 "") - <time datetime="2014-10-23 22:37:29">Oct 4, 2014</time>
<p>[…] every pixel to be tested for color and coverage in two, four or eight locations. Learn More: MSAA Overview Like MSAA, AMD’s Enhanced Quality Anti-Aliasing (EQAA) also comes in 2x, 4x and 8x sampling […]</p>
<hr />
####
[Brian Breczinski (@BrianBreczinski)](http://twitter.com/BrianBreczinski "BrianBreczinski@twitter.example.com") - <time datetime="2012-10-25 11:51:01">Oct 4, 2012</time>
<p>&ldquo;This means that the pixel shader performance does not increase substantially when MSAA is enabled, which is the primary benefit of MSAA over supersampling.&rdquo; I think you mean &ldquo;decrease&rdquo;</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-10-25 12:43:17">Oct 4, 2012</time>
<p>Yeah I actually meant to write &ldquo;pixel shader cost&rdquo;. Thanks for pointing that out!</p>
<hr />
####
[Anonymous Coward]( "") - <time datetime="2012-10-25 17:31:20">Oct 4, 2012</time>
<p>Excellent article. This means that the pixel shader performance does not *increase* substantially when MSAA is enabled &ndash;&gt; This means that the pixel shader performance does not *decrease* substantially when MSAA is enabled</p>
<hr />
####
[AMD reigns supreme in Sid Meier&#8217;s Civilization: Beyond Earth | ArdzTV](http://ardz.tv/amd-reigns-supreme-in-sid-meiers-civilization-beyond-earth/ "") - <time datetime="2014-10-24 11:45:02">Oct 5, 2014</time>
<p>[…] LEARN MORE: A Quick Overview of MSAA […]</p>
<hr />
####
[Niels Fröhling (@Ethatron)](http://twitter.com/Ethatron "niels@paradice-insight.us") - <time datetime="2012-10-31 13:47:39">Oct 3, 2012</time>
<p>&ldquo;Working with HDR and Tone Mapping&rdquo;: The fundamental underlying problem is that the color-space is non-linear. You&rsquo;ll also get a wrong resolve with simple LDR sRGB because the filtering is done in the wrong domain (linear filtering in a non-linear domain). It may not be very visible in LDR sRGB, and maybe the hardware has sRGB-filtering as a special case in some fixed function block, given the rendertarget is marked to be sRGB, and textures translate from sRGB to RGB while being fetched. You can always make it &ldquo;right&rdquo; by using a linear color-space (linear means the euclidian distance between two values in any part of the range is identical respective the change in energetic magnitude). In the case of sRGB it&rsquo;s rather easy, as the &ldquo;tonemapper&rdquo; is a simple gamma-function which is easily invertible (you have to store shader-output in linear RGB), but if you use a custom tonemapping-curve you may end up with a situation of uninvertability, and you don&rsquo;t know how to store the values in the rendertarget to be linearly filterable. What Humus is basically doing is translating the output of his shaders into a linearly filterable form, it&rsquo;s not really an exclusive artifact of HDR, and that linear color-space can still be HDR, it&rsquo;s not necessary for the values to collapse into [0,255] space (like &ldquo;normal&rdquo; tonemapper would do), the only requirement is that the color-space is linear. That&rsquo;s my understanding of it, maybe I just oversee some aspect.</p>
<hr />
####
[drbaltazar]( "Michelmrnet@hotmail.com") - <time datetime="2014-02-18 06:32:14">Feb 2, 2014</time>
<p>As for linear ? Best bet would be to all use scrgb but given how big it is ,not sure it is a great idea !</p>
<hr />
####
[drbaltazar]( "Michelmrnet@hotmail.com") - <time datetime="2014-02-18 06:27:24">Feb 2, 2014</time>
<p>Too many assumption in this thread !you assume everybody have a 100% sRGB monitor that can do 0 to 255 ,average is 3 to 252 so imagine what happen to all the data .imagine how screwed up the gamma curve will subtle ?ROFL let me recap ,user cannot adjust the min 0 black point to the proper value (average being 3 that would be in fact the real 0 ,this part is pretty much fixed in color.org profile so you say now gamma is accurate white point is accurate ? Right ? Nope because then user meet another problem ms doesn&rsquo;t supply a setting for max white value on average poor man screen max is 252 all color profile expect 255 .guess what happen ? Yep most white get crushed (just like that black get crushed )worst the gamma isn&rsquo;t gonna be at proper value because it expect 0 to 255 when in fact you are at 3 to 252 .even color.org fixed version is wrong because it assume 3 to 255 .so you still get gamma issue .and white point in this ?ya you get the idea .so add all the fix in this and its no wonder image look bad at time !simplest fix ? Add a way to adjust minimum for 0 to 3 and add a way to set max from 252 to 255 ms and all would yell :but just buy a proper monitor that do 0 to 255 100% sRGB .I&rsquo;m sure nobody will listen to this advice .lastly everybody assume copy protection in HDMI, dvi, dp is really letting game signal do their thing .I aint sure it does .I begin to suspect part of what game Dev use is copy protected and if license wasn&rsquo;t paid ? Ya you get the idea .its all fine and good to ignore a lot of stuff but game Dev artist are so good now almost nobody need msaa or all the other post process .and lastly tearing and jagged is created way before the memory even get that data so what does it ? Its the various buffer timing of memory cache etc that cause it .and only is maker be it ms or Linux can fix this .seeing new android stuff I think Google has almost fixed it .(ya I was surprised)me if I was Dev I would disable msaa and all and find the issue in os and then notify ms so they can adjust those value to proper number since these setting have been avail for almost ever</p>
<hr />
####
[Priyadarshi Sharma](https://www.facebook.com/priyadarshi1 "priyadarshi.sh@gmail.com") - <time datetime="2014-02-02 18:59:04">Feb 0, 2014</time>
<p>Great article! I really like the way you write. It&rsquo;s similar (or even better) to the style used in books like Physically based Rendering and Real-time rendering. You should write one!</p>
<hr />
####
[Ben Supnik&#8217;s &#8230; A Flicker of Hope for Flicker | Aerosoft Sim News](http://asn.aerosoft.com/?p=15514 "") - <time datetime="2012-11-01 22:09:14">Nov 4, 2012</time>
<p>[&hellip;] the prospect of explaining them was daunting.  Fortunately Matt Pettineo did an awesome job with this post.  Go read it; I’ll wait [&hellip;]</p>
<hr />
####
[A Flicker of Hope for Flicker | X-Plane Developer](http://developer.x-plane.com/2012/11/a-flicker-of-hope-for-flicker/ "") - <time datetime="2012-11-01 06:37:29">Nov 4, 2012</time>
<p>[&hellip;] the prospect of explaining them was daunting.  Fortunately Matt Pettineo did an awesome job with this post.  Go read it; I’ll wait [&hellip;]</p>
<hr />
####
[AMD reigns supreme in Sid Meier&#8217;s Civilization: Beyond Earth | Daily Cliche](http://www.dailycliche.com/amd-reigns-supreme-in-sid-meiers-civilization-beyond-earth/ "") - <time datetime="2014-11-18 11:56:03">Nov 2, 2014</time>
<p>[…] LEARN MORE: A Quick Overview of MSAA […]</p>
<hr />
####
[VR distortion correction using vertex displacement - GeekTechTalk](http://www.geektechtalk.com/vr-distortion-correction-using-vertex-displacement/ "") - <time datetime="2016-01-27 09:11:51">Jan 3, 2016</time>
<p>[…] up the MSAA – if you are unfamiliar with what MSAA does other than remove jaggies – I suggest a quick read here – or otherwise just accept that turning on 4x MSAA is a little bit like rendering on a screen […]</p>
<hr />
####
[Ogre Progress Report: December 2017 | OGRE - Open Source 3D Graphics Engine](https://www.ogre3d.org/2018/01/01/ogre-progress-report-december-2017 "") - <time datetime="2017-12-31 18:29:34">Dec 0, 2017</time>
<p>[…] may have heard that combining HDR and MSAA can still results in nasty aliasing. Sometimes even make it worse. This can happen if you’re looking at a very dark object with a […]</p>
<hr />
####
[OpenGLのCompute shaderでアンチエイリアシングを実装する | tatsyblog](http://tatsyblog.sakura.ne.jp/wordpress/applications/graphics/1651/ "") - <time datetime="2017-01-27 22:50:55">Jan 5, 2017</time>
<p>[…] Quick Overview of MSAA – <a href="https://mynameismjp.wordpress.com/2012/10/24/msaa-overview/">https://mynameismjp.wordpress.com/2012/10/24/msaa-overview/</a> […]</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-01-26 18:53:18">Jan 2, 2016</time>
<p>Thank you Simon! The error is now fixed. :)</p>
<hr />
####
[Simon](http://simonschreibt.de "simon@simonschreibt.de") - <time datetime="2016-01-26 00:22:49">Jan 2, 2016</time>
<p>Wonderful article - very well written and understandable. I&rsquo;ve found a tiny error and I just tell it because I like it when people point me to things like this on my own blog. :) I guess here&rsquo;s an &ldquo;is&rdquo; too much: &ldquo;This is exploited is by rasterizing&rdquo; Thanks again for your time you invested in this. *thumbs up*</p>
<hr />
####
[Anti-aliasing technics comparison - SAPPHIRE Nation - Community blog by SAPPHIRE Technology](http://sapphirenation.net/anti-aliasing-technics-comparison/ "") - <time datetime="2016-11-29 00:44:30">Nov 2, 2016</time>
<p>[…] Performance-wise, MSAA is a major improvement over SSAA. The boost was achieved by sampling two or more adjacent pixels together, instead of rendering the entire scene at a very high resolution. Thanks to that, further optimizations can be performed to share samples between different pixels. For example, if there is a group of pixels of similar colors, not every single one needs to be analyzed entirely—and that’s what boosts performance. SSAAx4 means a single pixel requires four samples, but with MSAAx4 some samples can be split between adjacent pixels, thus freeing up computational power. This is why MSAA is so much faster than SSAA. The main drawback of MSAA is the lower image quality it produces. It simply can’t deal with transparencies, a fact that is clearly visible in most modern games. If you want to learn something more about MSAA, take a look here. […]</p>
<hr />
####
[Anti-Aliasing Research - Programming Money](http://www.programmingmoney.com/anti-aliasing-research/ "") - <time datetime="2016-11-07 19:28:01">Nov 1, 2016</time>
<p>[…] In-depth explanation of Anti-Aliasing: <a href="https://mynameismjp.wordpress.com/2012/10/24/msaa-overview/">https://mynameismjp.wordpress.com/2012/10/24/msaa-overview/</a> […]</p>
<hr />
####
[Beware of SV_Coverage &#8211; Yosoygames](http://www.yosoygames.com.ar/wp/2017/02/beware-of-sv_coverage/ "") - <time datetime="2017-02-28 11:54:09">Feb 2, 2017</time>
<p>[…] Neither GL spec, docs, MSDN docs and other readings warned me about these gotchas. MJP’s blogposts were very useful, but that’s it. And they weren’t very specific to […]</p>
<hr />
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The rasterization process on a modern GPU can actually be quite a bit more complicated than this, but those details aren&rsquo;t particularly relevant to the scope of this article.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>This mask is directly available to ps_5_0 pixel shaders in DX11 via the SV_Coverage system value.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>The gamma-space rendering commonly used in the days before HDR would actually produce gradients that weren&rsquo;t completely smooth, although later GPU&rsquo;s supported performing the resolve in linear space. Either way the results were pretty close to being perceptually smooth, at least compared to the results that can occur with HDR rendering.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>These animations were captured from the sample application that I&rsquo;m going to discuss in the next article. So if you&rsquo;d like to see live results without compression, you can download the sample app from <a href="http://mynameismjp.wordpress.com/2012/10/28/msaa-resolve-filters/">that article</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
		</item>
		
		<item>
			<title>Applying Sampling Theory To Real-Time Graphics</title>
			<link>https://therealmjp.github.io/posts/applying-sampling-theory-to-real-time-graphics/</link>
			<pubDate>Mon, 22 Oct 2012 06:59:09 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/applying-sampling-theory-to-real-time-graphics/</guid>
			<description>Previous article in the series: Signal Processing Primer
Computer graphics is a field that constantly deals with discrete sampling and reconstruction of signals, although you might not be aware of it yet. This article focuses on the ways in which sampling theory can be applied to some of the common tasks routinely performed in graphics and 3D rendering.
Image Scaling The concepts of sampling theory can are most easily applicable to graphics in the form of image scaling.</description>
			<content:encoded><![CDATA[<p><em>Previous article in the series: <a href="../signal-processing-primer/">Signal Processing Primer</a></em></p>
<p>Computer graphics is a field that constantly deals with discrete sampling and reconstruction of signals, although you might not be aware of it yet. This article focuses on the ways in which sampling theory can be applied to some of the common tasks routinely performed in graphics and 3D rendering.</p>
<h2 id="image-scaling">Image Scaling</h2>
<p>The concepts of sampling theory can are most easily applicable to graphics in the form of <em>image scaling</em>. An image, or bitmap, is typically the result of sampling a color signal at discrete XY sample points (pixels) that are evenly distributed on a 2D grid. To rescale it to a different number of pixels, we need to calculate a new color value at sample points that are different from the original pixel locations. In the previous article we mentioned that this process is known as <em>resampling</em>, and is also referred to as <em>interpolation</em>. Any graphics programmer should be familiar with the <em>point</em> (also known as <em>nearest-neighbor</em>) and <em>linear</em> (also known as <em>bilinear)</em> interpolation modes supported natively in GPU’s which are used when sampling textures. In case you’re not familiar, point filtering simply picks the closest texel to the sample point and uses that value. Bilinear filtering on the other hand picks the 4 closest texels, and linearly interpolates those values in the X and Y directions based on the location of the sample point relative to the texels. It turns out that these modes are both just implementations of a reconstruction filter, with point interpolation using a box function and linear interpolation using a triangle function. If you look back at the diagrams showing reconstruction with a box function and triangle function, you can see actually see how the reconstructed signal resembles the visual result that you get when performing point and linear sampling. With the box function you end up getting a reconstructed value that’s “snapped” to the nearest original sample point, while with a triangle function you end up with straight lines connecting the sample points. If you’ve used point and linear filtering, you probably also intuitively understand that point filtering inherently results in more aliasing than linear filtering when resizing an image. For reference, here&rsquo;s an image showing the same rotated checkerboard pattern being resampled with a box filter and a triangle filter:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/point_linear_filtering.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/point_linear_filtering.png" alt="" title="Point_Linear_Filtering"></a></p>
<center><i>An image of a rotated checkerboard pattern being enlarged with a box filter (point filtering) and a triangle filter (bilinear filtering)</i></center>
<p>Knowing what we do about aliasing and reconstruction filters, we can now put some mathematical foundation behind what we intuitively knew all along.  The box function’s frequency domain equivalent (the sinc function) is smoother and wider than the triangle function’s frequency domain equivalent (the sinc2 function), which results in significantly more postaliasing. Of course we should note even though the triangle function might be considered among the the “low end” of reconstruction filters in terms of quality, it is still attractive due to its low performance impact. Not only is the triangle function very cheap to evaluate at a given point in terms of ALU instructions, but more importantly the function evaluates to 0 for all distances greater than or equal to 1. This is important for performance, because it means that any pixels that are further than a distance of 1.0 from the resampled pixel location will not have to be considered. Ultimately this means that we only need to fetch a maximum of 4 pixels (in a 2x2 area) for linear filtering, which limits bandwidth usage and cache misses. For point filtering the situation is even better, since the box function hits zero at 0.5 (it has a width of 1.0) and thus we only need to fetch one pixel.</p>
<p>Outside of realtime 3D rendering, it is common to use <em>cubic filters</em> (also known as <em>bicubic filters</em>) as a higher-quality alternative to point and linear filters when scaling images. A cubic filter is not a single filtering function, but rather a family of filters that interpolate using a 3rd-order (cubic) polynomial. The use of such functions in image processing dates back to Hsieh Hou’s paper entitled “Cubic Splines for Image Interpolation and Digital Filtering”[1] which proposed using cubic B-splines as the basis for interpolation. Cubic splines are attractive for filtering because they can be used to create functions where the 1st derivative is continuous across the entire domain range, which known as being <em>C1 continuous</em>. Being C1 continuous also implies that the function is <em>C0 continuous</em>, which means that the 0th derivative is also continuous. So in other words, the function itself will would have no visible discontinuities if you were to plot the function. Remember that there is an inverse relationship between rate of change in the spatial domain and the frequency domain, therefore a smooth function without discontinuities is desirable for reducing postaliasing. A second reason that cubic splines are attractive is that the functions can be made to be zero-valued after a certain point, much like a box or triangle function. This means the filter will have a limited width, which is optimal from a performance point of view. Typically cubic filters use functions defined along the [-2, 2] range, which is double the width of a unit triangle function. Finally, a third reason for the attractiveness of cubic filters is that they can be made to produce acceptable results when applied as a <em>seperable</em> <em>filter</em>. Seperable filters can be applied independently in two passes along the X and Y dimensions, which reduces the number of neighboring pixels that need to be considered when applying the filter and thus improves the performance.</p>
<p>In 1988, Don Mitchell and Arun Netravali published a paper entitled <em>Reconstruction Filters in Computer Graphics</em>[2] which narrowed down the set of possible of cubic filtering functions into a generalized form dependent on two parameters called <em>B</em> and <em>C</em>. This family of functions produces filtering functions that are always C1 continuous, and are normalized such that area under the curve is equal to one. The general form they devised is found below:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/generalized-cubic.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/generalized-cubic.png" alt="" title="Generalized Cubic"></a></p>
<center><i>Generalized form for cubic filtering functions</i></center>
<p>Below you can find  graphs of some of the common curves in use by popular image processing software[3], as well as the result of using them to enlarge the rotated checkerboard pattern that we used earlier:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/generalized-cubic-graphs-resized.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/generalized-cubic-graphs-resized.png" alt="" title="Generalized Cubic Graphs Resized"></a></p>
<center><i>Common cubic filtering functions using Mitchell’s generalized form for cubic filtering. From top- left going clockwise: cubic(1, 0) AKA cubic B-spline, cubic(1/3, 1/3) AKA Mitchell filter, cubic(0, 0.75) AKA Photoshop bicubic filter, and cubic(0, 0.5) AKA Catmull-Rom spline</i></center>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/cubic-filters.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/cubic-filters.png" alt="" title="Cubic Filters"></a></p>
<center><i>Cubic filters used to enlarge a rotated checkerboard pattern</i></center>
<p>One critical point touched upon in Mitchell’s paper is that the sinc function isn’t usually desirable for image scaling, since by nature the pixel structure of an image leads to discontinuities which results in unbounded frequencies. Therefore ideal reconstruction isn’t possible, and ringing artifacts will occur due to Gibb’s phenomenon. Ringing was identified by Schrieber and Troxel[4] as being one of four negative artifacts that can occur when using cubic filters, with the other three being aliasing, <em>blurring</em> and <em>anisotropy effects</em>. Blurring is recognized as the loss of detail due to too much attenuation of higher frequencies, and is often caused by a filter kernel that is too wide. Anisotropic effects are artifacts that occur due to applying the function as a separable filter, where the resulting 2D filtering function doesn’t end up being radially symmetrical.</p>
<p>Mitchell suggested that the purely frequency domain-focused techniques of filter design were insufficient for designing a filter that produces subjectively pleasing results to the human eye, and instead emphasized balancing the 4 previously-mentioned artifacts against the amount of postaliasing in order to design a high-quality filter for image scaling. He also suggested studying human perceptual response to certain artifacts in order to subjectively determine how objectionable they may be. For instance, Earl Brown[5] discovered that ringing from a single negative lobe can actually increase perceived sharpness in an image, and thus can be a desirable effect in certain scenarios. He also pointed out that ringing from multiple negative lobes, such as what you get from a sinc function, will always degrade quality. Here&rsquo;s an image of our friend Rocko enlarged with a Sinc filter, as well as an image of a checkerboard pattern enlarged with the same filter:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/ringing_multilobe.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/ringing_multilobe.png" alt="" title="Ringing_Multilobe"></a></p>
<center><i>Ringing from multiple lobes caused by enlargement with a windowed sinc filter</i></center>
<p>Ultimately, Mitchell segmented the domain of his B and C parameters into what he called &ldquo;regions of dominant subjective behavior. In other words, he determined which values of each parameter resulted in undesirable artifacts. In his paper he included the following chart showing which artifacts were associated with certain ranges of the B and C parameters:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/mitchell_chart.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/mitchell_chart.png" alt="" title="Mitchell_Chart"></a></p>
<center><i>A chart showing the dominant areas of negative artifacts for Mitchell's generalized cubic function. From "Reconstruction Filters in Computer Graphics" \[Mitchell 88\]</i></center>
<p>Based on his analysis, Mitchell determined that (1/3, 1/3) produced the highest-quality results. For that reason, it is common to refer to the resulting function as a “Mitchell filter”. The following images show the results of using non-ideal parameters to enlarge Rocko, as well as the results from using Mitchell&rsquo;s suggested parameters:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/cubic-artifacts.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/cubic-artifacts.png" alt="" title="Cubic Artifacts"></a></p>
<center><i>Undesirable artifacts caused by enlargement using cubic filtering. The top left image demonstrates anisotropy effects, the top right image demonstrates excessive blurring, and the bottom left demonstrates excessive ringing. The bottom right images uses a Mitchell filiter, representing ideal results for a cubic filter. Note that these images have all been enlarged an extra 2x with point filtering after resizing with the cubic filter, so that the artifacts are more easier to see.</i></center>
<h2 id="texture-mapping">Texture Mapping</h2>
<p>Real-time 3D rendering via rasterization brings about its own particular issues related to aliasing, as well as specialized solutions for dealing with them. One such issue is aliasing resulting from resampling textures at runtime in order to map them to a triangle’s 2D projection in screen space, which I’ll refer to as <em>texture aliasing</em>. If we take the case of a 2D texture mapped to a quad that is perfectly perpendicular to the camera, texture sampling essentially boils down to a classic image scaling problem: we have a texture with some width and height, the quad is scaled to cover a grid of screen pixels with a different width and height, and the image must be resampled at the pixel locations where pixel shading occurs. We already mentioned in the previous section that 3D hardware is natively capable of applying “linear” filtering with a triangle function. Such filtering is sufficient for avoiding severe aliasing artifacts when upscaling or downscaling, although for downscaling this only holds true when downscaling by a factor &lt;= 2.0. Linear filtering will also prevent aliasing when rotating an image, which is important in the context of 3D graphics since geometry will often be rotated arbitrarily relative to the camera. Like image scaling, rotation is really just a resampling problem and thus the same principles apply. The following image shows how the pixel shader sampling rate changes for a triangle as it’s scaled and rotated:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/triangle-sampling-rate.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/triangle-sampling-rate.png" alt="" title="Triangle Sampling Rate"></a></p>
<center><i>Pixel sampling rates for a triangle. Pixel shaders are executed at a grid of fixed locations in screen space (represented by the  red dots in the image), thus the sampling rate for a texture depends on the position, orientation, and projection of a given triangle. The green triangle represents the larger blue triangle after being scaled and rotated, and thus having a lower sampling rate.</i></center>
<h2 id="mipmapping">Mipmapping</h2>
<p>When downscaling by a factor greater than 2, linear filtering leads to aliasing artifacts due to high-frequency components of the source image leaking into the downsampled version. This manifests as temporal artifacts, where the contents of the texture appear to flicker as a triangle moves relative to the camera. This problem is commonly dealt with in image processing by widening the filter kernel so that its width is equal to the size of the downscaled pixel. So for instance if downscaling from 100x100 to 25x25, the filter kernel would be greater than or equal in width to a 4x4 square of pixels in the original image. Unfortunately widening the filter kernel isn’t usually a suitable option for realtime rendering, since the number of memory accesses increases with O(N2) as the filter width increases. Because of this a technique known as <em>mipmapping</em> is used instead. As any graphics programmer should already know, mipmaps consist of a series of <em>prefiltered</em> versions of a 2D texture that were downsampled with a kernel that’s sufficiently wide enough to prevent aliasing. Typically these downsampled versions are generated for dimensions that are powers of two, so that each successive mipmap is half the width and height of the previous mipmap. The following image from Wikipedia shows an example of typical mipmap chain for a texture:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/mipmaps.jpg"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/mipmaps.jpg" alt="" title="Mipmaps"></a></p>
<center><i>An example of a texture with mipmaps. Each mip level is roughly half the size of the level before it. Image take from Wikipedia.</i></center>
<p>A box function is commonly used for generating mip maps, although it’s possible to use any suitable reconstruction filter when downscaling the source image. The generation is also commonly implemented recursively, so that each mip level is generated from the mip level preceding it. This makes the process computationally cheap, since a simple linear filter can be used at each stage in order to achieve the same results as a wide box filter applied to the highest-resolution image. At runtime the pixel shader selects the appropriate mip level by calculating the <em>gradients</em> of the texture coordinate used for sampling, which it does by comparing texture coordinate used for one pixel to the texture coordinate used in the neighboring pixels of a 2x2 quad. These gradients, which are equal to the partial derivatives of the texture coordinates with respect to X and Y in screen space, are important because they tell us the relationship between a given 2D image and the rate at which we’ll sample that image in screen space. Smaller gradients mean that the sample points are close together, and thus we’re using a high sampling rate. Larger gradients result from the sample points being further apart, which we can interpret to mean that we’re using a low sampling rate. By examining these gradients we can calculate the highest-resolution mip level that would provide us with an image size less than or equal to our sampling rate. The following image shows a simple example of mip selection:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/mip_selection.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/mip_selection.png" alt="" title="Mip_Selection"></a></p>
<center><i>Using texture coordinate gradients to select a mip level for a 4x4 texture.</i></center>
<p>In the image, the two red rectangles represent texture-mapped quads of different sizes rasterized to a 2D grid of pixels_._ For the topmost quad, the a value of 0.25 will be computed as the partial derivative for the U texture coordinate with respect to the X dimension, and the same value will be computed as the partial derivative for the V texture coordinate with respect to the Y dimension. The larger of the two gradients is then used to select the appropriate mip level based on the size of the texture. In this case, the length of the gradient will be 0.25 which means that the 0th (4x4) mip level will be selected. For the lower quad the size of the gradient is doubled, which means that the 1st mip level will be selected instead. Quality can be further improved through the use of <em>trilinear filtering,</em> which linearly interpolates between the results of bilinearly sampling the two closest mip levels based on the gradients. Doing so prevents visible seams on a surface at the points where a texture switches to the next mip level.</p>
<p>One problem that we run into with mipmapping is when an image needs to be downscaled more in one dimension than in the other. This situation is referred to as <em>anisotropy</em>, due to the differing sampling rates with respect to the U and V axes of the texture. This happens all of the time in 3D rendering, particularly when a texture is mapped to a ground plane that’s nearly parallel with the view direction. In such a case the plane will be projected such that the V gradients grow more quickly than the U gradients as distance from the camera increases, which equates to the sampling rate being lower along the V axis. When the gradient is larger for one axis than the other, 3D hardware will use the larger gradient for mip selection since using the smaller gradient would result in aliasing due to undersampling. However this has the undesired effect of over-filtering along the other axis, thus producing a “blurry” result that’s missing details. To help alleviate this problem, graphics hardware supports <em>anisotropic filtering</em>. When this mode is active, the hardware will take up to a certain number of “extra” texture samples along the axis with the larger gradient. This allows the hardware to “reduce” the maximum gradient, and thus use a higher-resolution mip level. The final result is equivalent to using a rectangular reconstruction filter in 2D space as opposed to a box filter. Visually such a filter will produce results such that aliasing is prevented, while details are still perceptible. The following images demonstrate anisotropic filtering on a textured plane:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/anisotropy_1x.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/anisotropy_1x.png" alt="" title="Anisotropy_1x"></a><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/anisotropy_16x.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/anisotropy_16x.png" alt="" title="Anisotropy_16x"></a></p>
<center><i>A textured plane without anisotropic filtering, and the same plane with 16x anistropic filtering. The light grey grid lines demonstrate the distribution of pixels, and thus the rate of pixel shading in screen space. The red lines show the U and V axes of the texture mapped to plane. Notice the lack of details in the grain of the wood on the left image, due to over-filtering of the U axis in the lower-resolution mip levels.</i></center>
<h2 id="geometric-aliasing">Geometric Aliasing</h2>
<p>A second type of aliasing experienced in 3D rendering is known as <em>geometric aliasing</em>. When a 3D scene composed of triangles is rasterized, the <em>visibility</em> of those triangles is sampled at discrete locations typically located at the center of the screen pixels. Triangle visibility is just like any other signal in that there will be aliasing in the reconstructed signal when the sampling rate is inadequate (in this case the sampling rate is determined by the screen resolution). Unfortunately triangular data will always have discontinuities, which means the signal will never be bandlimited and thus no sampling rate can be high enough to prevent aliasing. In practice these artifacts manifest as the familiar jagged lines or “jaggies” commonly seen in games and other applications employing realtime graphics. The following image demonstrates how these aliasing artifacts occur from rasterizing a single triangle:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/geometric-aliasing.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/geometric-aliasing.png" alt="" title="Geometric Aliasing"></a></p>
<center><i>Geometric aliasing occurring from undersampling the visibility of a triangle. The green, jagged line represents the outline of the triangle seen on a where pixels appear as squares of a uniform color.</i></center>
<p>Although we’ve already established that no sampling rate would allow us to perfectly reconstruct triangle visibility, it is possible to reduce aliasing artifacts with a process known as <em>oversampling</em>. Oversampling essentially boils down to sampling a signal at some rate higher than our intended output, and then using those samples points to reconstruct new sample points at the target sampling rate. In terms of 3D rendering this equates to rendering at some resolution higher than the output resolution, and then downscaling the resulting image to the display size. This process is known as <em>supersampling</em>, and it’s been in use in 3D graphics for a very long time. Unfortunately it’s an expensive option, since it requires not just rasterizing at a higher resolution but also shading pixels at a higher rate. Because of this, an optimized form of supersampling known as <em>multi-sample antialiasing</em> (abbreviated as MSAA) was developed specifically for combating geometric aliasing. We&rsquo;ll discuss MSAA and geometric aliasing in more detail in the following article.</p>
<h2 id="shader-aliasing">Shader Aliasing</h2>
<p>A third type of aliasing that’s common in modern 3D graphics is known as <em>shader aliasing</em>. Shader aliasing is similar to texture aliasing, in that occurs due to the fact that the pixel shader sampling rate is fixed in screen space. However the distinction is that shader aliasing refers to undersampling of signals that are evaluated analytically in the pixel shader using mathematical formulas, as opposed to undersampling of a texture map. The most common and noticeable case of shader aliasing results from applying per-pixel specular lighting with low roughness values (high specular exponents for Phong and Blinn-Phong). Lower roughness values result in narrower lobes, which make the specular response into a higher-frequency signal and thus more prone to undersampling. The following image contains plots of the N dot H response of a Blinn-Phong BRDF with varying roughness, demonstrating it becomes higher frequency for lower roughnesses:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/specular_frequency.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/specular_frequency.png" alt="" title="Specular_Frequency"></a></p>
<center><i>N dot H response of a Blinn-Phong BRDF with various exponents. Note how the response becomes higher-frequency for higher exponents, which correspond to lower roughness values. Image from Real-Time Rendering, 3rd Edition, A K Peters 2008</i></center>
<p>Shader aliasing is most likely to occur when normal maps are used, since they increase the frequency of the surface normal and consequently cause the specular response to vary rapidly across a surface. HDR rendering and physically-based shading models can compound the problem even further, since they allow for extremely intense specular highlights relative to the diffuse lighting response. This category of aliasing is perhaps the most difficult to solve, and as of yet there are no silver-bullet solutions. MSAA is almost entirely ineffective, since the pixel shading rate is not increased compared to the non-MSAA. Supersampling is effective, but prohibitively expensive due to the increased shader and bandwidth costs required to shade and fill a larger render target. Emil Persson demonstrated a method of selectively supersampling the specular lighting inside the pixel shader[6], but this too can be expensive if the number of lights are high or if multiple normal maps need to be blended in order to compute the final surface normal.</p>
<p>A potential solution that has been steadily gaining some ground[7][8] is to modify the specular shading function itself based on normal variation. The theory behind this is that microfacet BRDF’s naturally represent micro-level variation along a surface, with the amount of variation being based on a roughness parameter. If we increase the roughness of a material as the normal map details become relatively smaller in screen space, we use the BRDF itself to account for the undersampling of the normal map/specular lighting response. Increasing roughness decreases the frequency of the resulting reflectance, which in turn reduces the appearance of artifacts. The following image contains an example of using this technique, with an image captured with 4x shader supersampling as a reference:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/specaliasing_none.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/specaliasing_none.png" alt="" title="SpecAliasing_None"></a>
<a href="/images/converted/applying-sampling-theory-to-real-time-graphics/specaliasing_4xss.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/specaliasing_4xss.png" alt="" title="SpecAliasing_4xSS"></a>
<a href="/images/converted/applying-sampling-theory-to-real-time-graphics/specaliasing_clean.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/specaliasing_clean.png" alt="" title="SpecAliasing_Clean"></a></p>
<center><i>The topmost image shows an example of shader aliasing due to undersampling a high-frequency specular BRDF combined with a high-frequency normal map. The middle image shows the same scene with 4x shader supersampling applied. The bottom image shows the results of of using a variant of CLEAN mapping to limit the frequency of the specular response.</i></center>
<p>This approach (and others like it) can be considered to be part of a broader category of antialiasing techniques known as <em>prefiltering</em>. Prefiltering amounts to applying some sort of low-pass filter to a signal before sampling it, with the goal of ensuring that the signal&rsquo;s bandwidth is less than half of the sampling rate. In a lot of cases this isn&rsquo;t practical for graphics since we don&rsquo;t have adequate information about what we&rsquo;re sampling (for instance, we don&rsquo;t know what triangle should is visible for a pixel until we sample and raterize the triangle). However in the case of specular aliasing from normal maps, the normal map contents are known ahead of time.</p>
<h2 id="temporal-aliasing">Temporal Aliasing</h2>
<p>So far, we have discussed graphics in terms of sampling a 2D signal. However we’re often concerned with a third dimension, which is time. Whenever we’re rendering a video stream we’re also sampling in the time domain, since the signal will completely change as time advances. Therefore we must consider sampling along this dimension as well, and how it can produce aliasing.</p>
<p>In the case of video we are still using discrete samples, where each sample is a complete 2D image representing our scene at some period of time. This sampling is similar to our sampling in the spatial domain: there is some frequency of the signal we are sampling, and if we undersample that signal aliasing will occur. One classic example of a temporal aliasing is the so-called “<a href="http://en.wikipedia.org/wiki/Wagon-wheel_effect">wagon-wheel effect</a>”, which refers to the phenomenon where a rotating wheel may appear to rotate more slowly (or even backwards) when viewed in an undersampled video stream. This animated GIF from Wikipedia demonstrates the effect quite nicely:</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/wagonwheeleffect.gif"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/wagonwheeleffect.gif" alt="" title="WagonWheelEffect"></a></p>
<center><i>A demonstration of the wagon-wheel effect that occurs due to temporal aliasing. In the animation the camera is moving to the right at a constant speed, yet the shapes appear to speed up, slow down, and even switch direction. Image taken from Wikipedia.</i></center>
<p>In games, temporal sampling artifacts usually manifest as “jerky” movements and animations.  Increases in framerate correspond to an increase in sampling rate along the time domain, which allows for better sampling of faster-moving content. This is directly analogous to the improvements that are visible from increasing output resolution: more details are visible, and less aliasing is perceptible.</p>
<p>The most commonly-used anti-aliasing technique for temporal aliasing is <em>motion blur</em>. Motion blur actually refers to an effect visible in photography, which occurs due to the shutter of the camera being open for some non-zero amount of time. This produces a result quite different than what we produce in 3D rendering, where by default we get an image representing one infinitely-small period of time. To accurately simulate the effect, we could supersample in the time domain by rendering more frames than we output and applying a filter to the result. However this is prohibitively expensive just like spatial supersampling, and so approximations are used. The most common approach is to produce a per-pixel velocity buffer for the current frame, and then use that to approximate the result of oversampling with a blur that uses multiple texture samples from nearby pixels. Such an approach can be considered an example of advanced reconstruction filter that uses information about the rate of change of a signal rather than additional samples in order to reconstruct an approximation of the original sample. Under certain conditions the results can be quite plausible, however in many cases noticeable artifacts can occur due to the lack of additional sample points. Most notably these artifacts will occur where the occlusion of a surface by another surface changes during a frame, since information about the occluded surface is typically not available to the post-process shader performing the reconstruction. The following image shows three screenshots of a model rotating about the camera&rsquo;s z-axis: the model rendered with no motion blur, the model rendered with <a href="http://graphics.cs.williams.edu/papers/MotionBlurI3D12/">Morgan McGuire&rsquo;s post-process motion blur technique</a>[9] applied using 16 samples per pixel, and finally the model rendered temporal supersampling enabled using 32 samples per frame&rdquo;</p>
<p><a href="/images/converted/applying-sampling-theory-to-real-time-graphics/motionblur.png"><img src="/images/converted/applying-sampling-theory-to-real-time-graphics/motionblur.png" alt="" title="MotionBlur"></a></p>
<center><i>A model rendered without motion blur, the same model rendered with post-process motion blur, and the same model rendered with temporal supersampling.</i></center>
<h2 id="references">References</h2>
<p>[1] Hou, Hsei. Cubic Splines for Image Interpolation and Digital Filtering. IEEE Transactions on Acoustics, Speech, and Signal Processing. Vol. 26, Issue 6. December 1978. <br>
[2] Mitchell, Don P. and Netravali, Arun N. <a href="http://www.cs.utexas.edu/~fussell/courses/cs384g/lectures/mitchell/Mitchell.pdf">Reconstruction Filters in Computer Graphics</a>. SIGGRAPH &lsquo;88 Proceedings of the 15th annual conference on Computer graphics and interactive techniques. <br>
[3] <a href="http://entropymine.com/imageworsener/bicubic/">http://entropymine.com/imageworsener/bicubic/</a> <br>
[4] Schreiber, William F. Transformation Between  Continuous  and Discrete  Representations  of Images:  A  Perceptual  Approach. IEEE Transactions on Pattern Analysis and Machine Intelligence. Volume 7, Issue 2. March 1985. <br>
[5] Brown, Earl F. Television: The  Subjective  Effects  of Filter  Ringing  Transients. February, 1979. <br>
[6] <a href="http://www.humus.name/index.php?page=3D&amp;ID=64">http://www.humus.name/index.php?page=3D&amp;ID=64</a> <br>
[7] <a href="http://blog.selfshadow.com/2011/07/22/specular-showdown/">http://blog.selfshadow.com/2011/07/22/specular-showdown/</a> <br>
[8] <a href="http://advances.realtimerendering.com/s2012/index.html">http://advances.realtimerendering.com/s2012/index.html</a> <br>
[9] McGuire, Morgan. Hennessy, Padraic. Bukowski, Michael, and Osman, Brian. <a href="http://graphics.cs.williams.edu/papers/MotionBlurI3D12/">A Reconstruction Filter for Plausible Motion Blur</a>. I3D 2012. <br></p>
<p><em>Next article in the series: <a href="../msaa-overview/">A Quick Overview of MSAA</a></em></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22%22"></a> - <time datetime="2013-10-13 10:55:23">Oct 0, 2013</time></p>
<p>&gt; In the animation the camera is moving to the right at a constant speed Actually, that&rsquo;s not true. According to the wikipedia description of the gif, it&rsquo;s the acceleration that&rsquo;s constant, not the speed. (Which makes much more sense!)</p>
<hr />
####
[Ruud van Gaal](http://www.racer.nl "ruud@racer.nl") - <time datetime="2012-10-22 07:57:51">Oct 1, 2012</time>
<p>Interesting article; now onto learning more on frequency lowering for specular highlights, which is indeed a problem (well, I have several areas of shimmering in my graphics engine).</p>
<hr />
####
[AmazingFactory]( "first10@btinternet.com") - <time datetime="2012-10-22 04:57:32">Oct 1, 2012</time>
<p>worth mentioning that cubic b-spline interpolation results in color shift because it does not interpolate exactly also b-splines are C2 continuous (which means you can use b-spline to interpolate a heightmap and create perfectly smooth normals)</p>
<hr />
####
[Rim]( "remigius@netforge.nl") - <time datetime="2012-10-24 04:09:43">Oct 3, 2012</time>
<p>Interesting as usual. I&rsquo;ll spare you my usual puritan amateur comments ;)</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Signal Processing Primer</title>
			<link>https://therealmjp.github.io/posts/signal-processing-primer/</link>
			<pubDate>Mon, 15 Oct 2012 08:20:18 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/signal-processing-primer/</guid>
			<description>For a theoretical understanding of aliasing and anti-aliasing, we can turn to the fields of signal processing[1] and sampling theory[2]. This article will explain some of the basics of these two related field in my own words, taking a more theoretical point of view. In the following article the concepts covered here will be used to analyze common aspects of real-time graphics, so that we can describe them in terms of signal processing.</description>
			<content:encoded><![CDATA[<p>For a theoretical understanding of aliasing and anti-aliasing, we can turn to the fields of <a href="http://en.wikipedia.org/wiki/Signal_processing">signal processing</a>[1] and <a href="http://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem">sampling theory</a>[2]. This article will explain some of the basics of  these two related field in my own words, taking a more theoretical point of view. In the <a href="../applying-sampling-theory-to-real-time-graphics/">following article</a> the concepts covered here will be used to analyze common aspects of real-time graphics, so that we can describe them in terms of signal processing. If you&rsquo;d like some further reading, I&rsquo;d recommend consulting chapter 7 of <em>Physically Based Rendering</em>[3] (available as a <a href="http://www.pbrt.org/chapters/pbrt_chapter7.pdf">free download</a>), chapter 5 of <em>Real-Time Rendering, 3rd Edition</em>[4] or <em>Principles of Digital Image Synthesis</em>[5] (which is also <a href="http://realtimerendering.com/Principles_of_Digital_Image_Synthesis_v1.0.1.pdf">freely available for download</a>)</p>
<p>As always, I’m more interested in the material being correct than I am in sounding like I’m smart. So if you see anything that you feel is incorrect or have any additional insights to share, please let me know in the comments!</p>
<h2 id="sampling-theory">Sampling Theory</h2>
<p>Sampling theory deals with the process of taking some continuous <em>signal</em> that varies with one or parameters, and sampling the signal at discrete values of those parameters. If you&rsquo;re not familiar with signals and signal processing, you can think of a signal as some continuous function of any dimension that varies along its domain. To sample it, we then calculate that function&rsquo;s value at certain points along the curve. Usually the points at which we sample are evenly-spaced apart, which we call <em>uniform sampling.</em> So for instance if we had a 1D signal defined as f(x) = x^2, and we might sample it at x =0, x = 1, x = 2 x = 3, and so on. This would give us our set of discrete samples, which in this case would be 0, 1, 4, 9, and continuing on in that fashion. Here&rsquo;s an image showing our continuous function of  f(x) = x^2, and the result of discretely sampling that function at integer values of x:</p>
<p><a href="/images/converted/signal-processing-primer/sampling.png"><img src="/images/converted/signal-processing-primer/sampling.png" alt="" title="Sampling"></a></p>
<center><i>Discretely sampling a continuous function</i></center>
<p>Working with discrete samples has a lot of advantages. For instance, it allows us to store a representation of an arbitrary signal in memory by simply storing the sampled values in an array. It also allows us to perform operations on a signal by repeatedly applying the same operation in a loop for all sample points. But what happens when we need the value of s signal at a location that we didn&rsquo;t sample at?  In such cases, we can use a process known as <em>reconstruction</em> to derive an approximation of the original continuous function. With this approximation we can then discretely sample its values at a new set of sample points, which is referred to as <em>resampling</em>. You may also see this referred to as <em>interpolation</em> in cases where local discrete values are used to compute an &ldquo;in between&rdquo; value.</p>
<p>The actual process of reconstruction involves applying some sort of <em>reconstruction filter</em> to a set of discrete samples. Typically this filter is some function that is symmetrical about x=0, often with non-zero values only in a small region surrounding x=0. The following image contains a few functions commonly used as reconstruction filters:</p>
<p><a href="/images/converted/signal-processing-primer/filters.png"><img src="/images/converted/signal-processing-primer/filters.png" alt="" title="Filters"></a></p>
<center><i>Various functions used as reconstruction filters. Starting from the top left and moving clockwise: the box function, the triangle function, and the sinc function. Image from Real-Time Rendering, 3rd Edition, A K Peters 2008</i></center>
<p>The filter is applied using a process known as <em>convolution</em>. In the case of discrete sample points, a convolution implies multiplying the sample values with an instance of the filter function translated such that it is centered about the same point, and then summing the result from all sample points. If you&rsquo;re having trouble understand what this means, take a look at the following three images which show the result of convolution with three common reconstruction filters:</p>
<p><a href="/images/converted/signal-processing-primer/convolution_box.png"><img src="/images/converted/signal-processing-primer/convolution_box.png" alt="" title="Convolution_Box"></a></p>
<p><a href="/images/converted/signal-processing-primer/convolution_triangle.png"><img src="/images/converted/signal-processing-primer/convolution_triangle.png" alt="" title="Convolution_Triangle"></a></p>
<p><a href="/images/converted/signal-processing-primer/convolution_sinc.png"><img src="/images/converted/signal-processing-primer/convolution_sinc.png" alt="" title="Convolution_Sinc"></a></p>
<center><i>Discretely-sampled signals being reconstructed with a box function, a triangle function, and a sinc function. Images from Real-Time Rendering, 3rd Edition, A K Peters 2008</i></center>
<p>If you&rsquo;ve ever written a full-screen Gaussian blur shader, then you&rsquo;ve used convolution. Think about how you would write such a shader: you loop over nearby pixel values (sample points) in a texture, multiply each value by the Gaussian function evaluated using the distance from your output pixel, and sum the results. Evaluating a function using the distance to the sample point is equivalent to translating the filter function to the location of the sample point, although you may not have thought of it this way.</p>
<p>Of the common reconstruction filters, the sinc filter is particularly interesting. This is because it is theoretically possible to use it to exactly reconstruct the original continuous signal, provided that the signal was adequately sampled. This is known as <em>ideal reconstruction.</em> To define what “adequately sampled” means for a continuous signal, we must now discuss aliasing.</p>
<p>Key Takeaway Points:</p>
<ul>
<li>Continuous signals(functions) can be sampled at discrete sample points</li>
<li>An approximation of the original continuous signal can be reconstructed by applying a filter to the discrete sample values</li>
</ul>
<h2 id="frequency-and-aliasing">Frequency and Aliasing</h2>
<p>Signals are often described in terms of their <em>frequency</em>, which in rough terms describes how quickly a signal changes over their domain. In reality a signal is not composed of just one frequency, but can have an entire spectrum of frequencies. Mathematically a signal can be converted from its original representation (often referred to as the <em>time domain</em> or <em>spatial domain</em>, depending on the context) to its spectrum of frequencies (known as the <em>frequency domain</em>) using the <em>Fourier transform</em>. Once in the frequency domain, it can be determined if there is some maximum frequency where all frequencies above that have an intensity of zero. If such a maximum frequency exists, the signal is said to be <em>bandlimited</em>, which means we can determine the <em>bandwidth</em> of that signal.  Depending on the context, the term &ldquo;bandwidth&rdquo; can be either the <em>passband bandwidth</em> or the <em>baseband bandwidth</em>. The passband bandwidth is equal to the maximum frequency minus the minimum frequency, while the baseband bandwidth simply refers to the refers to the maximum frequency. With sampling theory we are primarily concerned with the baseband bandwidth, because it is used to determine the <em>Nyquist rate</em> of the signal. The Nyquist rate is minimum rate at which the signal should be sampled in order to prevent <em>aliasing</em> from occurring, and it is equal to 2 times the baseband bandwidth. The term “aliasing” refers the fact that a signal can become indistinguishable from a lower-frequency signal when <em>undersampled</em>. The following image demonstrates this phenomenon with two sine waves of different frequencies, where the samples would be the same for either signal:</p>
<p><a href="/images/converted/signal-processing-primer/aliasing-sines.png"><img src="/images/converted/signal-processing-primer/aliasing-sines.png" alt="" title="Aliasing Sines"></a></p>
<center><i>Aliasing of a sampled sine wave. Image from Wikipedia</i></center>
<p>In practice, aliasing that occurs due to undersampling will result in errors in the reconstructed signal. So in other words, the signal you end up with will be different than the one you were originally sampling. For signals that are not bandlimited, there is no maximum frequency and thus there is no sampling rate that won’t result in aliasing after reconstruction.</p>
<p>To better understand how and why aliasing occurs, it can be helpful to look at things in the frequency domain. Let’s start with the plot of the frequency spectrum for an arbitrary signal:</p>
<p><a href="/images/converted/signal-processing-primer/frequencyspace.png"><img src="/images/converted/signal-processing-primer/frequencyspace.png" alt="" title="FrequencySpace"></a></p>
<center><i>Frequency spectrum of an arbitrary signal. Image from Wikipedia.</i></center>
<p>As we can see from the plot, there is a maximum frequency located at point “B”, meaning that the signal is bandlimited and has a bandwidth equal to B. When this signal is discretely sampled, an infinite number of “copies” of the signal will appear alongside the original at various points. Here’s an image illustrating how it looks:</p>
<p><a href="/images/converted/signal-processing-primer/frequencyspace_aliases.png"><img src="/images/converted/signal-processing-primer/frequencyspace_aliases.png" alt="" title="FrequencySpace_Aliases"></a></p>
<center><i>Replicas of a signals frequency spectrum. Image from Wikipedia</i></center>
<p>The location of the signal duplicates is determined by the sampling rate, which is marked as “fs” in the plot. Since these duplicates are present, we must use a filter (the reconstruction filter) to remove these duplicates and leave us with only the frequency spectrum that was within the original signal’s bandwidth (referred to as the <em>baseband</em> frequencies). The obvious solution is to use a box function in the frequency domain, since a box function implies multiplying a certain range of values by 1 and all other values by 0. So if we were to use a box function with a width of B, we would remove the duplicates while leaving the original signal intact. The following diagram shows how this might work:</p>
<p><a href="/images/converted/signal-processing-primer/frequencyspace_filter.png"><img src="/images/converted/signal-processing-primer/frequencyspace_filter.png" alt="" title="FrequencySpace_Filter"></a></p>
<center><i>A reconstruction filter is used to isolate the original copy of a signal's spectrum. Image from Wikipedia</i></center>
<p>What’s important to keep in mind is that we typically need to apply our reconstruction filter in the spatial domain, and not in the frequency domain. This means that we need to use the spatial domain equivalent of a box function in frequency space, and it turns out that this is the previously-mentioned sinc function. By now it should make sense why the sinc function is called the ideal reconstruction filter, since it has the ability to leave certain frequency ranges untouched while completely filtering out other frequencies. For this same reason it is also common to refer to the frequency domain box function as the <em>ideal low-pass filter</em>.</p>
<p>Now let’s look at what happens when we don’t sample the signal at an adequate rate. As we saw earlier, the duplicates of a signal will appear at multiples of the sampling frequency. So the higher our sampling rate the further apart they will be, while the lower our sampling rate the closer they will be. Earlier we learned that the critical sampling rate for a signal is 2B, so let’s look at the plot of a signal that’s been sampled at a rate lower than 2B:</p>
<p><a href="/images/converted/signal-processing-primer/frequencyspace_overlap.png"><img src="/images/converted/signal-processing-primer/frequencyspace_overlap.png" alt="" title="FrequencySpace_Overlap"></a></p>
<center><i>Inadequate sampling rate results in overlap of a signal's replicas. Image from Wikipedia</i></center>
<p>Once we dip below the Nyquist rate of the signal, the duplicates begin to overlap in the frequency domain. After this happens it is no longer possible to isolate the original copy of the signal with a sinc filter, and thus we end up with aliasing. The bottom plot in the above image demonstrates what an alias of the original signal would look like. Since its frequency response is identical to that of the original signal, it is completely indistinguishable.</p>
<p>Key Takeaway Points:</p>
<ul>
<li>Signals can be decomposed into a spectrum of frequencies, with the spectrum being tied to the rate of change of the signal</li>
<li>Signals with a maximum frequency are <em>bandlimited</em></li>
<li>A signal’s <em>Nyqust rate</em> is equal to two times its maximum frequency, and this is the minimum sampling rate required to perfectly reconstruct a signal without aliasing</li>
<li>Signal reconstruction can be viewed as the process of removing “replicas” of a signal’s spectrum in the frequency domain</li>
</ul>
<h2 id="reconstruction-filter-design">Reconstruction Filter Design</h2>
<p>Aliasing that results from undersampling is referred to as <em>prealiasing</em>, since it occurs before reconstruction takes place. However it is also possible for artifacts to occur due to the reconstruction process itself. For instance, imagine if we used a box function that was too wide when applying a reconstruction filter. The result would look like this:</p>
<p><a href="/images/converted/signal-processing-primer/frequencyspace_postaliasing.png"><img src="/images/converted/signal-processing-primer/frequencyspace_postaliasing.png" alt="" title="FrequencySpace_Postaliasing"></a></p>
<center><i>A wide reconstruction filter fails to isolate the original copy of a signal's spectrum. Image from Wikipedia</i></center>
<p>With such a reconstruction we would still end up with artifacts in the reconstructed signal, even though it was adequately sampled.</p>
<p>As we’ve demonstrated, using the wrong size box function in the frequency domain is one way to adversely affect the quality of our reconstructed signal. However we’ve already mentioned that a variety of functions can be applied as a filter in the spatial domain, and these functions all have a frequency domain counterpart that differs from the box function that we previously discussed. With this in mind, we can reason that the choice in filter will affect how well we isolate the signal in the frequency domain, and that this will affect how much postaliasing is introduced into the reconstructed signal. Let’s look at some common filtering functions, and compare their plots with the plots of their frequency domain counterparts:</p>
<p><a href="/images/converted/signal-processing-primer/box_sinc.png"><img src="/images/converted/signal-processing-primer/box_sinc.png" alt="" title="Box_Sinc"></a></p>
<center><i>Box function -> Sinc Function</i></center>
<p><a href="/images/converted/signal-processing-primer/triangle_sinc2.png"><img src="/images/converted/signal-processing-primer/triangle_sinc2.png" alt="" title="Triangle_Sinc2"></a></p>
<center><i>Triangle Function -> (Sinc Function)2</i></center>
<p><a href="/images/converted/signal-processing-primer/gaussian_gaussian.png"><img src="/images/converted/signal-processing-primer/gaussian_gaussian.png" alt="" title="Gaussian_Gaussian"></a></p>
<center><i>Gaussian Function -> Gaussian Function</i></center>
<p><a href="/images/converted/signal-processing-primer/sinc2_triangle.png"><img src="/images/converted/signal-processing-primer/sinc2_triangle.png" alt="" title="Sinc2_Triangle"></a></p>
<center><i>(Sinc Function)2 -> Triangle Function</i></center>
<p><a href="/images/converted/signal-processing-primer/sinc_box.png"><img src="/images/converted/signal-processing-primer/sinc_box.png" alt="" title="Sinc_Box"></a></p>
<center><i>Sinc Function -> Box Function</i></center>
<p>By looking at the frequency domain counterpart of a spatial domain filter function, we can get a rough idea of how well it’s going to preserve the frequency range we’re interested in and filter out the extraneous copies of our signal’s spectrum. The field of <em>filter design</em> is primarily concerned with this process of analyzing a filter’s frequency domain spectrum, and using that to evaluate or approximate that filter’s overall performance. Looking that the spectrums plotted in the above images, we can see that the non-sinc functions will all attenuate the baseband frequencies in some way. For some functions we can also observe that the frequency domain equivalent has no maximum value above which all frequencies have a value of zero, which means that the frequency domain filter extends infinitely in both directions. This ultimately means that all of the infinite replicas of the signal’s spectrum will bleed into the reconstructed signal to some extent, which will cause aliasing.</p>
<p>One general pattern that we can observe from looking at the plots of the spatial domain filter functions and their frequency domain equivalents is that there is an inverse relationship between rate of change in one representation and its counterpart. For instance, have a look at the spatial domain box function. This function has a discontinuity at some value, resulting in infinite rate of change. Consequently its frequency domain counterpart is the sinc, which extends to infinity representing the infinite rate of change inherent in the box function’s discontinuity. By the same token a sinc function in the spatial domain equates to a box function in the frequency domain since the relationship is reciprocal. The Gaussian function is a special case, where the spatial domain and frequency domain counterparts are the same function. For this reason the Gaussian function represents the exact “midpoint” between smoothly-changing functions and sharply-changing functions in the spatial and frequency domains. Another important aspect of this relationship is that by making a filtering function “wider” (which can be achieved by dividing the input distance by some value greater than 1), the resulting frequency spectrum for that function will become more “narrow”. As an example, have a look at the spectrum of a “unit” box function with width of 1.0 compared with the spectrum of a box function with width of 4.0</p>
<p><a href="/images/converted/signal-processing-primer/box-fft-unit.png"><img src="/images/converted/signal-processing-primer/box-fft-unit.png" alt="" title="Box FFT Unit"></a></p>
<center><i>Frequency spectrum of a box function with width of 1.0</i></center>
<p><a href="/images/converted/signal-processing-primer/box-fft-4.png"><img src="/images/converted/signal-processing-primer/box-fft-4.png" alt="" title="Box FFT 4"></a></p>
<center><i>Frequency spectrum of a box function with width of 4.0</i></center>
<p>The graphs clearly show that as the filter kernel becomes wider, the magnitude of the lowest frequencies becomes higher. This is really just another manifestation of the behavior we noted earlier regarding rate of change in the spatial domain and the frequency domain, since “wider” functions will change more slowly over time and thus will have more low frequency components in its frequency spectrum.</p>
<p>One difficult aspect of filter design is that we often must not just consider the filter’s frequency domain representation, but we also must consider the effect that its spatial domain representation will have on the reconstructed signal. In particular, we must be careful with filters that have negative lobes, such as the sinc filter. Such filters can produce an effect known as <em>ringing</em> when applied to sharp discontinuities, where the reconstructed signal oscillates about the signal being sampled. Take a look at the plot of a square wave being reconstructed with a sinc filter:</p>
<p><a href="/images/converted/signal-processing-primer/gibbs_phenomenon.png"><img src="/images/converted/signal-processing-primer/gibbs_phenomenon.png" alt="" title="Gibbs_Phenomenon"></a></p>
<center><i>Gibbs phenomenon resulting from a square wave being reconstructed with a sinc filter</i></center>
<p>Key Takeaway Points:</p>
<ul>
<li>Error resulting from inadequate sampling rate is known as <em>prealiasing.</em> Error introduced through poor filtering is known as <em>postaliasing</em>.</li>
<li>A filter’s ability to limit aliasing can be estimated by observing its frequency domain representation</li>
<li>A filter’s rate of change in the spatial domain is inversely related to its rate of change in the frequency domain</li>
<li>A filter’s spatial domain representation can also have an effect on the quality of the reconstructed signal, with the most notable effect being ringing occurring at discontinuities.</li>
</ul>
<h2 id="references">References</h2>
<p>[1]<a href="http://en.wikipedia.org/wiki/Signal_processing">http://en.wikipedia.org/wiki/Signal_processing</a> <br>
[2]<a href="http://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem">http://en.wikipedia.org/wiki/Nyquist-Shannon_sampling_theorem</a> <br>
[3]Pharr, Matt and Humphreys, Greg. Physically Based Rendering - From Theory to Implementation, 2nd Edition. <br>
[4]Akenine-Möller, Tomas, Haines, Eric, and Hoffman, Naty. Real-Time Rendering, 3rd Edition <br>
[5]Glassner, Andrew. <a href="http://www.realtimerendering.com/Principles_of_Digital_Image_Synthesis_v1.0.1.pdf">Principles of Digital Image Synthesis</a> <br></p>
<p><em>Next article in the series:</em></p>
<p><a href="../applying-sampling-theory-to-real-time-graphics/"><em>Applying Sampling Theory To Real-Time Graphics</em></a></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://mynameismjp.wordpress.com/" title="mpettineo@gmail.com">MJP</a> - <time datetime="2012-10-15 10:24:15">Oct 1, 2012</time></p>
<p>@AmazingFactory - yes you are correct, the Nyquist rate is equal to 2 times the *baseband* bandwidth, where baseband bandwidth is equal to the maximum frequency. I actually described passband bandwidth which is MaxFrequency - MinFrequency, which isn&rsquo;t what you&rsquo;re interested in for determining Nyquist rate. I updated the text to be more clear on this. Thank you for pointing that out!</p>
<hr />
####
[AmazingFactory]( "first10@btinternet.com") - <time datetime="2012-10-27 07:11:59">Oct 6, 2012</time>
<p>Aha! If the texture has a high frequency periodic pattern then you will get a moire pattern - which is an alias Also you can see why the sampling frequency must be GREATER than 2x the highest frequency Imagine sampling a 1 Hz sine wave at 2 Hz &hellip; the resulting signal could be zero (depending on the phase)</p>
<hr />
####
[Jeremy Shopf &raquo; Image Reconstruction and MSAA](http://jshopf.com/blog/?p=304 "") - <time datetime="2012-11-01 17:16:54">Nov 4, 2012</time>
<p>[&hellip;] with Reconstruction Filters for MSAA Resolve”. Even better, he had previously posted a nice sampling theory primer and introduction to MSAA which were also well done. Anti-aliasing was something that was really [&hellip;]</p>
<hr />
####
[Ryan Monroe]( "monroe@jpl.nasa.gov") - <time datetime="2012-10-17 21:11:58">Oct 3, 2012</time>
<p>@AmazingFactory, @MJP Actually, I believe that it&rsquo;s a bit more complicated than that. In an ideal scenario, you can just sample at twice the passband bandwidth, but if that passband is ill-located, you could need more bandwidth to represent it. Remember that signals alias/fold down when under-sampled. So, if you had a signal at, say, 25-75 Hz and were sampling at 100 Hz (twice the bandwidth), you&rsquo;d see the 75-&gt;50 Hz component folding back on the 25 -&gt; 50 Hz component (note the order of the first component, this is intentional). Then, the 0 -&gt; 25 Hz component would still have no signal (being outside the passband and not seeing any components) and the 25-&gt;50 component would contain the mixed-up information of the entire 50 Hz of spectral data. You would have to downconvert the signal to baseband, convert it to an analytic signal or something to be able to use that spectrum efficiently. I hope this has been descriptive! Oh the banes of posting after a glass of wine ;-)</p>
<hr />
####
[Alexander](http://jstroot23.bravejournal.com/entry/128165/ "alexander_peacock@arcor.de") - <time datetime="2013-06-03 13:17:59">Jun 1, 2013</time>
<p>I&rsquo;m not sure where you are getting your information, but good topic. I needs to spend some time learning more or understanding more. Thanks for magnificent info I was looking for this info for my mission.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2013-05-08 12:44:48">May 3, 2013</time>
<p>Thank you Andy, I&rsquo;m glad that you found the article useful! That was indeed a typo, thank you for pointing that out.</p>
<hr />
####
[Andy Southwell](http://www.indygamedev.com "andy@indygamedev.com") - <time datetime="2013-05-08 11:34:44">May 3, 2013</time>
<p>Fantastic article - thank you very much! So many articles on this kind of subject go totally crazy on the maths, leaving mere mortals like me behind. One really minor typo in the final &rsquo;take away points&rsquo; : &ldquo;A filter’s ability to limit aliasing can estimating by observing&rdquo;. I&rsquo;m guessing &rsquo;estimating&rsquo; should be &rsquo;estimate&rsquo;? Anyway, keep up the good work - thanks again!</p>
<hr />
####
[Series of articles on anti-aliasing | Light is beautiful](http://lousodrome.net/blog/light/2012/10/29/series-of-articles-on-anti-aliasing/ "") - <time datetime="2012-10-29 02:07:49">Oct 1, 2012</time>
<p>[&hellip;] Signal Processing Primer [&hellip;]</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-10-24 21:56:14">Oct 3, 2012</time>
<p>Hey Rim, how&rsquo;s it going? Sorry for taking so long to reply. Texture filtering is a sampling/reconstruction process, so it definitely qualifies as a convolution! I didn&rsquo;t mention it because I talk about texture filtering more in-depth in the next article. As for the copies, usually it&rsquo;s explained as occuring due to the properties of the shah function (impulse train). Basically the fourier transform a shah function is another shah function with the inverse period. So if you think of discrete sampling as multiplying a continuous function with a shah function with period equal to your sampling rate, this would mean that you&rsquo;re convolving the frequency-domain representation of that function with a shah function with inverse period (since multiplication in the spatial/time domain is convolution in the frequency domain and vice versa). The &ldquo;inverse period&rdquo; bit is then what leads to the copies being closer together for lower sampling rates. There&rsquo;s a pretty good explanation in Physically Based Rendering if you have that, and it&rsquo;s also mentioned in Principles of Digital Image Synthesis (which is free to download). I suppose that&rsquo;s true regarding information in the frequency domain. I tend to only look at these things from a graphics POV. :P Personally all of the material I&rsquo;ve read has only brought up overshoot in the context of ringing. But if your book talks about it separately than I&rsquo;m sure it&rsquo;s a worthy topic on its own. There&rsquo;s so much I haven&rsquo;t read yet!</p>
<hr />
####
[oh]( "oren.hazi@gmail.com") - <time datetime="2012-10-17 18:58:49">Oct 3, 2012</time>
<p>When you&rsquo;re sampling data for storage in memory, you&rsquo;re not only sampling the signal at a particular instant, you&rsquo;re also discretizing it to fit into your sample slot. For example, 8-bit sampling gives you 256 &ldquo;levels&rdquo; for representing the signal value, so you won&rsquo;t be able to detect small differences in your signal amplitude as well as you could with a 16-bit sampler. If you make certain (reasonable, in some cases) assumptions about your input signal, you can treat the quantization distortion as a stochastic process and actually make useful statements about what this does to your spectrum, and how much headroom you need in various places to do whatever it was you wanted to do with your sampled signal. Not exactly &ldquo;introductory&rdquo; material, but useful when trying to answer questions like &ldquo;how many bits should my analog to digital converter have?&rdquo;</p>
<hr />
####
[Robin Green]( "robin.green@gmail.com") - <time datetime="2012-10-15 08:35:42">Oct 1, 2012</time>
<p>My favorite factoid about sampling: The trade-off between Spatial and Spectral Compactness is exactly Heisenberg&rsquo;s Uncertainty Principal.</p>
<hr />
####
[AmazingFactory]( "first10@btinternet.com") - <time datetime="2012-10-15 03:59:01">Oct 1, 2012</time>
<p>Very enjoyable reading I think Nyquist must be greater than double the highest frequency, not double the bandwidth So to sample audio up to 22khz you need a sampling frequency greater than 44 khz (eg 44.1khz) You can not &ldquo;perfectly reconstruct a signal&rdquo; (unless by chance it happened to be an ideal signal)</p>
<hr />
####
[Rim]( "remigius@netforge.nl") - <time datetime="2012-10-15 04:27:54">Oct 1, 2012</time>
<p>As it happens, I was just reading up on DSP, so I&rsquo;ll chip in. Thanks for the post at any rate, your treatise gives the more theoretical book I&rsquo;m reading some more flavour. I realize I don&rsquo;t have anything really valuable to add, just some ramblings relating your stuff to the book. &ldquo;Working with discrete samples has a lot of advantages.&rdquo; - My DSP book points out the subtlety that discrete samples are the only way to do digital signal processing in the first place. Continuous functions can&rsquo;t be represented by digital equipment. It may sound puritan, but I found it to be a valuable insight. &ldquo;If you’ve ever written a full-screen Gaussian blur shader, then you’ve used convolution.&rdquo; - Wouldn&rsquo;t linear texture filtering (non-point at any rate) already classify as convolution? It&rsquo;s a simple kernel and it&rsquo;s mostly used implicitly to be sure, but I think more folks could relate to that. &ldquo;When this signal is discretely sampled, an infinite number of “copies” of the signal will appear&hellip;&rdquo; - My book spent numerous chapters on why this occurs and I&rsquo;d still be hesitant to say whether this is an artefact from the way computers have to represent signals (periodic discrete, right?) or something more fundamental. Can you share your insight on this? &ldquo;Nyqust rate is &hellip; the minimum sampling rate required to perfectly reconstruct a signal without aliasing&rdquo; - My book makes a big point about this being the theoretical minimum. The cut-off for bandlimiting, your stopband, won&rsquo;t be perfect (it&rsquo;s always a tradeoff IIRC) so it&rsquo;s a good idea to err on the safe side, ie to use a higher rate. &ldquo;The field of filter design is primarily concerned with this process of analyzing a filter’s frequency domain spectrum, and using that to evaluate or approximate that filter’s overall performance.&rdquo; - That&rsquo;d depend on whether your signal&rsquo;s information is encoded in the frequency domain or the time/spatial domain ;) &ldquo;Such filters can produce an effect known as ringing&rdquo; - And overshoot? Two sides of the same coin, or are they as distinct as my book makes them out to be? I&rsquo;m not far into filter design yet, let alone image filtering. For now, my research focusses on toying with the FFT in all its glory. If you&rsquo;re interested in bouncing some ideas back and forth on this topic or DSP in general, please feel free to shoot me an e-mail.</p>
<hr />
####
[Niels Fröhling (@Ethatron)](http://twitter.com/Ethatron "niels@paradice-insight.us") - <time datetime="2012-10-18 10:15:12">Oct 4, 2012</time>
<p>I think it&rsquo;s worth to mention that the theory still works in the analogue sample domain, that is with sample-points of infinite precision. I expect you&rsquo;ll get to quantization in a later article (with lovely blue sky-pics that have banding even with floating-point render-targets :^D).</p>
<hr />
####
[]( "") - <time datetime="2012-10-18 08:55:28">Oct 4, 2012</time>
<p>Ryan - check out the difference between baseband and passband in the MJP posts above - it&rsquo;s always the baseband, or maximum frequency, that determines the Nyquist value. So if you are sampling a signal that is 74-75 Hz, the Nyquist is 150 Hz, not 2 Hz.</p>
<hr />
####
[Ryan Monroe]( "monroe@jpl.nasa.gov") - <time datetime="2012-10-18 09:55:25">Oct 4, 2012</time>
<p>yeah, but in that case, you *could* sample at 2 Hz. You&rsquo;d then be operating in what&rsquo;s known as (in that case) the 75th nyquist zone. You would be able to reproduce the original signal exactly by knowing which frequency band it originated from.</p>
<hr />
####
[AmazingFactory]( "first10@btinternet.com") - <time datetime="2012-10-25 01:15:24">Oct 4, 2012</time>
<p>I still can&rsquo;t see why aliasing (ghosts/clones) is relevant to computer graphics. We don&rsquo;t perform fourier transforms, so those clones are not an issue &hellip; Also we never sample light amplitudes to determine frequency textures are arrays of weights per channel/frequency (red, green or blue) So whilst you can consider a texture as &ldquo;a signal&rdquo; the frequency information is of no interest to us</p>
<hr />
####
[#3 Audio Programming Tutorial: Understanding Digital Audio | Nikkies Tutorials](https://nikkiestutorials.com/2015/01/07/3-audio-programming-tutorial-understanding-digital-audio/ "") - <time datetime="2019-01-02 20:13:28">Jan 3, 2019</time>
<p>[…] More information on sampling (written with graphics in mind, but the same concepts apply to digital audio): <a href="http://mynameismjp.wordpress.com/2012/10/15/signal-processing-primer/">http://mynameismjp.wordpress.com/2012/10/15/signal-processing-primer/</a> […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Upcoming Series on Signal Processing and MSAA</title>
			<link>https://therealmjp.github.io/posts/upcoming-series-on-signal-processing-and-msaa/</link>
			<pubDate>Mon, 15 Oct 2012 08:00:47 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/upcoming-series-on-signal-processing-and-msaa/</guid>
			<description>Aliasing is everywhere in graphics. Almost everything we do uses discrete sampling, which means almost everything can produce a variety of aliasing artifacts. The folks in the film industry have historically taken a “no aliasing allowed” stance in their work, but in real-time graphics we’re still producing games with more sparkling and shimmering than a glitzy prom dress. If we’re going to do anything about that problem, I think it’s important that we all try to have at least a basic understanding of signal processing.</description>
			<content:encoded><![CDATA[<p>Aliasing is everywhere in graphics. Almost everything we do uses discrete sampling, which means almost everything can produce a variety of aliasing artifacts. The folks in the film industry have historically taken a “no aliasing allowed” stance in their work, but in real-time graphics we’re still producing games with more sparkling and shimmering than a glitzy prom dress. If we’re going to do anything about that problem, I think it’s important that we all try to have at least a basic understanding of signal processing. Signal processing is something that I had a lot of experience with during my previous life as an engineering student, but even with that experience it wasn’t always immediately obvious to me how some of the fundamentals applied to common aspects of 3D rendering. You might see people mentioning signal processing terminology when talking about some particular technique, but it can be difficult to use those small pieces to assemble the bigger picture.</p>
<p>Recently I was doing some experimenting with MSAA resolves, and read quite a bit of background material to refresh my knowledge. I starting taking some notes, and after a few pages worth I decided to organize them a bit into (lengthy) article that describes the basics of signal processing when it comes to graphics. Hopefully this material can be useful if you need a refresher yourself, or if you’ve yet to learn these basics at all. I’ve even gone through the trouble of listing a few of the most important points in bullet point form at the end of each section, so if you’re new to this I’d recommend at least skimming through those. After that I’ve also prepared some material on the basics of MSAA, since it can be a confusing topic.</p>
<p>After the articles I’m going to share the results of my experiments with custom MSAA resolves, along with a sample application. So if you’re already an expert (or you just really want to get to the code), then you’ll want to wait a bit so that you can skip ahead to the new material.</p>
<p><a href="../signal-processing-primer/">Article 1: Signal Processing Primer</a></p>
<p><a href="../applying-sampling-theory-to-real-time-graphics/">Article 2: Applying Sampling Theory To Real-Time Graphics</a></p>
<p><a href="../msaa-overview/">Article 3: A Quick Overview of MSAA</a></p>
<p><a href="../msaa-resolve-filters/">Article 4: Experimenting with Reconstruction Filters for MSAA Resolve</a></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22ezschemi@gmail.com%22">Enrico</a> - <time datetime="2012-10-15 11:00:00">Oct 1, 2012</time></p>
<p>That&rsquo;s cool. Thank you for your work!</p>
<hr />
####
[]( "") - <time datetime="2012-10-15 17:24:46">Oct 1, 2012</time>
<p>Awesome! Always like your posts.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>OpenGL Insights</title>
			<link>https://therealmjp.github.io/posts/opengl-insights/</link>
			<pubDate>Mon, 06 Aug 2012 04:50:49 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/opengl-insights/</guid>
			<description>Some time ago Charles de Rousiers adapted my Bokeh Depth of Field sample to OpenGL, and we contributed it as a chapter to the recently-released OpenGL Insights. Bokeh is still an ongoing area of R&amp;amp;D for myself, and hopefully I&amp;rsquo;ll be able to share some more improvements and optimizations once my current project is announced or released.
There&amp;rsquo;s going to be an author meet-up/book signing a the CRC Press SIGGRAPH booth (#929) this Tuesday from 2-3PM, and I&amp;rsquo;ll most likely be stopping by.</description>
			<content:encoded><![CDATA[<p>Some time ago <a href="http://www.derousiers.net/">Charles de Rousiers</a> adapted my <a href="http://mynameismjp.wordpress.com/2011/04/19/bokeh-ii-the-sequel/">Bokeh Depth of Field sample</a> to OpenGL, and we contributed it as a chapter to the recently-released <a href="http://openglinsights.com/index.html">OpenGL Insights</a>. Bokeh is still an ongoing area of R&amp;D for myself, and hopefully I&rsquo;ll be able to share some more improvements and optimizations once my current project is announced or released.</p>
<p>There&rsquo;s going to be an author meet-up/book signing a the CRC Press SIGGRAPH booth (#929) this Tuesday from 2-3PM, and I&rsquo;ll most likely be stopping by. So if you want to talk about Bokeh o(r anything else graphics-related), then feel free to stop by and chat me up!</p>
]]></content:encoded>
		</item>
		
		<item>
			<title>Looking for a job?</title>
			<link>https://therealmjp.github.io/posts/looking-for-a-job/</link>
			<pubDate>Sat, 28 Apr 2012 22:55:10 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/looking-for-a-job/</guid>
			<description>My company, Ready At Dawn Studios, is hiring pretty much across the board. Our jobs page has the full details, but the gist of it is that we&amp;rsquo;re working on a new AAA IP for a next-generation home console. I can&amp;rsquo;t really say more than that about the project, but I will say that we&amp;rsquo;re doing some *really* exciting work in terms of graphics. If you&amp;rsquo;re interested, send your resume to jobs[at]readyatdawn.</description>
			<content:encoded><![CDATA[<p>My company, <a href="http://www.readyatdawn.com/index.php">Ready At Dawn Studios</a>, is hiring pretty much across the board. Our <a href="http://www.readyatdawn.com/jobs.php">jobs page</a> has the full details, but the gist of it is that we&rsquo;re working on a new AAA IP for a next-generation home console. I can&rsquo;t really say more than that about the project, but I will say that we&rsquo;re doing some *really* exciting work in terms of graphics. If you&rsquo;re interested, send your resume to jobs[at]readyatdawn.com or email me directly at matt[at]readyatdawn.com.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22%22">darkfrenesy@yahoo.it</a> - <time datetime="2012-05-31 02:15:12">May 4, 2012</time></p>
<p>No position for &ldquo;junior&rdquo; programmers?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-05-31 09:12:47">May 4, 2012</time>
<p>We&rsquo;re definitely interested in junior programmers, as long as you&rsquo;re talented!</p>
<hr />
####
[]( "") - <time datetime="2012-05-14 21:10:53">May 1, 2012</time>
<p>&rsquo;s missing the coffe-cup refiller slave, or possibly the inventor guy which makes crazy little helper-bots, which clean the finger-prints on the monitor after you pointed at a peculiar piece of code. :^D</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>A quick note on shader compilers</title>
			<link>https://therealmjp.github.io/posts/a-quick-note-on-shader-compilers/</link>
			<pubDate>Sat, 14 Apr 2012 04:56:04 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/a-quick-note-on-shader-compilers/</guid>
			<description>This morning I was wrestling with a particularly complicated compute shader, which was taking just shy of 10 minutes to compile using D3DCompiler_43 from the June 2010 DirectX SDK. After a few failed attempts to speed it up by rearranging the code, I figured I&amp;rsquo;d try it out with the new version of the compiler that comes with the Windows 8 SDK. I wasn&amp;rsquo;t expecting any miracles, but to my surprise it compiled my shader in about 45 seconds!</description>
			<content:encoded><![CDATA[<p>This morning I was wrestling with  a particularly complicated compute shader, which was taking just shy of 10 minutes to compile using D3DCompiler_43 from the June 2010 DirectX SDK. After a few failed attempts to speed it up by rearranging the code, I figured I&rsquo;d try it out with the new version of the compiler that comes with the <a href="http://msdn.microsoft.com/en-us/windows/hardware/hh852363">Windows 8 SDK</a>. I wasn&rsquo;t expecting any miracles, but to my surprise it compiled my shader in about 45 seconds! I figured I would pass along the knowledge, in case anyone else is dealing with a similar problem.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://twitter.com/RCalocaO" title="RCalocaO@twitter.example.com">R Caloca (@RCalocaO)</a> - <time datetime="2012-04-30 09:51:10">Apr 1, 2012</time></p>
<p>Using the Feb 2010 SDK, I&rsquo;ve found that array size directly impacts shader compile time: We had an array for bones with 1K elements, and our compile times for vertex shaders were about 30-45 seconds depending on the shader; after changing that to a one element array (to test this theory), our compile times went down to 0.2, 0.3 secs.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-04-14 15:32:27">Apr 6, 2012</time>
<p>@zproxy I don&rsquo;t think I can say, although I&rsquo;ll tell you that it involved a few instances of an unrolled loop that executed for 49 iterations. @talden I was actually already compiling the shaders at O1, and it was still that slow! But thanks for the tip.</p>
<hr />
####
[zeuxcg](http://zeuxcg.wordpress.com "arseny.kapoulkine@gmail.com") - <time datetime="2012-05-01 08:01:51">May 2, 2012</time>
<p>R Caloca, you should change the array count to 2. If it&rsquo;s 1, compiler always selects the first bone so the generated assembly is incorrect; if it&rsquo;s 2, it does the proper index-based lookup, and you can upload more than 2 bones - assuming that the array is at the end of the constant buffer, of course. This does not generate any D3D Debug warnings and, as far as I&rsquo;m aware, is safe wrt different HW/drivers.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-09-16 18:42:58">Sep 0, 2012</time>
<p>Yes, you basically just need to change the VC++ directories for your include and lib files and then you can use the newer D3D headers and libraries. For VS 2010 you can follow these directions: <a href="http://blogs.msdn.com/b/vcblog/archive/2012/03/25/10287354.aspx">http://blogs.msdn.com/b/vcblog/archive/2012/03/25/10287354.aspx</a> One thing that is different with the Win8 SDK version of D3D is that there is no D3DX anymore. So if you have any dependencies on D3DX you&rsquo;ll need to change your code.</p>
<hr />
####
[talden](http://talden.wordpress.com "dwrightnow@gmail.com") - <time datetime="2012-04-13 22:43:38">Apr 5, 2012</time>
<p>I&rsquo;ve found that with compute shaders in particular, if you drop down to O1 instead of the default O3 optimization level you will get the same instruction count with 2-10x faster compiles.</p>
<hr />
####
[zproxy](http://zproxy.wordpress.com/ "dadeval@gmail.com") - <time datetime="2012-04-13 22:31:55">Apr 5, 2012</time>
<p>What was the shader about?</p>
<hr />
####
[benualdo]( "benualdo@msn.com") - <time datetime="2012-09-17 04:58:18">Sep 1, 2012</time>
<p>Thanks you for your help! I finally did it the &ldquo;GetProcAddress&rdquo; way because I was too lazy to change the vcproj. The shader now compile ~40% faster than before. Some shaders even compile twice faster (compute shaders and pixel shaders with lots of branch and nested loopts). Trivial shaders (like ZPass opaque PS) are a bit slower to compile.</p>
<hr />
####
[djmips](http://djmips.wordpress.com "david.c.galloway@gmail.com") - <time datetime="2012-05-03 11:23:36">May 4, 2012</time>
<p>Is there any chance that the new compiler generates more efficient code?</p>
<hr />
####
[]( "") - <time datetime="2012-10-30 21:03:25">Oct 2, 2012</time>
<p>MJP, is it legal to ship the d3dcompiler.dll with your game or does Microsoft forbid that. If it is forbidden, then how can you ship your game when it pops a missing .dll error if you don&rsquo;t include it? I am compiling ahead of time using VS2012 and so don&rsquo;t see any reason why the executable would require me to dynamically link with the d3dcomipler anyway. For now I&rsquo;ll just include the .dll in my .exe directory, but I feel that this is a hack. Thanks in advance.</p>
<hr />
####
[Dave Ottley]( "the_goosemaster@hotmail.com") - <time datetime="2012-10-30 21:04:26">Oct 2, 2012</time>
<p>By the way, the above post was made by Dave Ottley.</p>
<hr />
####
[benualdo]( "benualdo@msn.com") - <time datetime="2012-09-15 06:48:27">Sep 6, 2012</time>
<p>Could you use the win8 sdk compiler with your win7 application? If so, can you give more details about how you manage to do that? Did you have to compile shader using fxc.exe or compiling from code also worked? thanks!</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-09-15 13:38:29">Sep 6, 2012</time>
<p>Yes, it&rsquo;s possible. You can build Win7 applications using the Win8 SDK, in which case you just include D3DCompiler.h and link to d3dcompiler.lib and you&rsquo;ll use the new compiler. Just make sure that you copy d3dcompiler_46.dll from the Bin folder of your SDK install directory and put it next to your executable, because it won&rsquo;t be present on Win7 machines. If you want to keep using the headers and libs from DirectX SDK and use the compiler from the Win8 SDK, it&rsquo;s a little more tricky. You can&rsquo;t just add both Include paths to your VC++ directories, otherwise VC++ won&rsquo;t know which version of the file you want to include. An alternative would be to just use LoadLibrary and GetProcAddress to call the compile functions in d3dcompiler_46.dll without using the headers or linking to it. Or if you&rsquo;re compiling your shaders ahead of time, using fxc.exe is a simple way to do it.</p>
<hr />
####
[benualdo]( "benualdo@msn.com") - <time datetime="2012-09-16 10:04:54">Sep 0, 2012</time>
<p>Greate new and thanks for your answer, I&rsquo;m fed up with slow shader compilation. I did not know that win8 is not needed to build apps using the win7 SDK. If I understand what you say all I have to do is to install the Win8SDK and include/link against the Directx11 (11.1?) that comes with it instead of DirectX SDK? And I won&rsquo;t have to change anything else in my app to make it run with the win8SDK (as I can&rsquo;t actually run a win8 on win7 anyway)?</p>
<hr />
####
[benualdo]( "benualdo@msn.com") - <time datetime="2012-09-16 10:05:24">Sep 0, 2012</time>
<p>Greate news and thanks for your answer, I’m fed up with slow shader compilation. I did not know that win8 is not needed to build apps using the win8SDK. If I understand what you say all I have to do is to install the Win8SDK and include/link against the Directx11 (11.1?) that comes with it instead of DirectX SDK? And I won’t have to change anything else in my app to make it run with the win8SDK (as I can’t actually run a win8 on win7 anyway)?</p>
<hr />
####
[benualdo]( "benualdo@msn.com") - <time datetime="2012-09-16 10:06:40">Sep 0, 2012</time>
<p>arg sorry for double post I was too excited by faster shader compilation (and cannot switch to win8 atm) :)</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Light Indexed Deferred Rendering</title>
			<link>https://therealmjp.github.io/posts/light-indexed-deferred-rendering/</link>
			<pubDate>Sun, 01 Apr 2012 02:53:53 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/light-indexed-deferred-rendering/</guid>
			<description>There&amp;rsquo;s been a bit of a stir on the Internet lately due to AMD&amp;rsquo;s recent Leo demo, which was recently revealed to be using a modern twist on Light Indexed Deferred Rendering. The idea of light indexed deferred has always been pretty appealing, since it gives you some of the advantages of deferred rendering (namely using the GPU to decide which lights affect each pixel) while still letting you use forward rendering to actually apply the lighting to each surface.</description>
			<content:encoded><![CDATA[<p>There&rsquo;s been a <a href="http://forum.beyond3d.com/showthread.php?t=61487">bit</a> of a <a href="http://www.gamedev.net/topic/622221-revival-of-forward-rending/">stir</a> on the <a href="http://aras-p.info/blog/2012/03/02/2012-theory-for-forward-rendering/">Internet</a> lately due to AMD&rsquo;s recent <a href="http://developer.amd.com/samples/demos/pages/AMDRadeonHD7900SeriesGraphicsReal-TimeDemos.aspx">Leo demo</a>, which was <a href="http://developer.amd.com/gpu_assets/AMD_Demos_LeoDemoGDC2012.ppsx">recently revealed</a> to be using a modern twist on <a href="http://code.google.com/p/lightindexed-deferredrender/">Light Indexed Deferred Rendering</a>. The idea of light indexed deferred has always been pretty appealing, since it gives you some of the advantages of deferred rendering (namely using the GPU to decide which lights affect each pixel) while still letting you use forward rendering to actually apply the lighting to each surface. While there&rsquo;s little doubt at this point that deferred rendering has proven itself as an effective and practical technique, I&rsquo;m sure that plenty of programmers currently maintaining such a renderer have dreamed of a day where they don&rsquo;t have to figure out how to cram every attribute into their G-Buffer using as few bits as possible, or consume 100&rsquo;s of megabytes for MSAA G-Buffer textures.</p>
<p>While the benefits of light indexed deferred were pretty obvious to, I was pretty sure that the performance wouldn&rsquo;t hold up when compared to the state-of-art in traditional deferred rendering. So I decided to make a simple test app where I could toggle between the two techniques for the same scene. For the deferred renderer, I based my implementation very closely on <a href="http://software.intel.com/en-us/articles/deferred-rendering-for-current-and-future-rendering-pipelines/">Andrew Lauritzen&rsquo;s work</a> since he had done quite a bit of work in terms of optimizing it for modern GPU architectures. The only differences were that I used a different G-Buffer layout (normals, specular albedo + roughness, diffuse albedo, and ambient lighting, all 32bpp) and I used an oversized texture instead of a structured buffer for writing out the individual MSAA subsamples from the compute shader.</p>
<p>For the light indexed deferred renderer implementation I used a depth-only prepass to fill the depth buffer, which was then used by a compute shader to compute the list of intersecting lights per-tile. This list was stored in either an R8_UINT or R16_UINT typed buffer (8-bit for &lt; 255 lights, 16-bit otherwise), with enough space pre-allocated in the buffer to store a full light list for each tile. So no bitfields or linked lists or anything fancy like that, just a simple per-tile list terminated by sentinel value. I found that this worked best for the forward lighting pass, since this resulted in the least amount of overheard for reading the list in the forward rendering pass, although there might be better ways to do it. The forward rendering pass then figures out which tile each pixel is in, and applies the list of lights one by one.</p>
<p>In both cases I used normalized Blinn-Phong with fresnel approximation for the lights, so nothing fancy there. I did use a terrible linear falloff for the point lights just so that I could artificially restrict the radius, so please don&rsquo;t judge me for that. I also used the depth-only prepass for both implementations, since it actually resulted in a speed up of around 0.5ms for the G-Buffer pass. For a test scene, I used the ol&rsquo; Sponza atrium.</p>
<p>I gathered some performance numbers for the hardware I have access to, which is an AMD 6970 and an Nvidia GTX 570. For both GPU&rsquo;s I ran at 1920x1080 resolution with VSYNC disabled, and the timings represent total frame time. The Nvidia numbers were pretty much in line with my expectations:</p>
<p>Nvidia GTX 570</p>
<table>
<thead>
<tr>
<th>128 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>6.94ms</td>
<td>6.41ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>7.81ms</td>
<td>7.51ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>8.47ms</td>
<td>9.17ms</td>
</tr>
</tbody>
</table>
<br>
<table>
<thead>
<tr>
<th>256 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>11.67ms</td>
<td>9.43ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>12.987ms</td>
<td>10.75ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>13.88ms</td>
<td>12.34ms</td>
</tr>
</tbody>
</table>
<br>
<table>
<thead>
<tr>
<th>512 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>18.18ms</td>
<td>14.084ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>20.00ms</td>
<td>15.63ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>21.27ms</td>
<td>17.24ms</td>
</tr>
</tbody>
</table>
<br>
<table>
<thead>
<tr>
<th>1024 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>N/A</td>
<td>27.03ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>N/A</td>
<td>29.41ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>N/A</td>
<td>31.25ms</td>
</tr>
</tbody>
</table>
<p>Tile-based deferred rendering wins out and nearly every case, and it only gets worse as you add in more lights.  Light indexed seems to scale a bit better with MSAA, but even with that it&rsquo;s only enough to overcome the overall disadvantage for the 128 light case. For 1024 lights it seemed as though the Nvidia driver or hardware couldn&rsquo;t handle the large buffer I was using for storing the light indices, as I was getting very strange artifacts on the lower half of the screen. However I can only imagine the trend would continue, and it would lag further behind the tile-based deferred renderer.</p>
<p>For the AMD 6970, the results were much more interesting:</p>
<p>AMD 6970</p>
<table>
<thead>
<tr>
<th>128 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>5.26ms</td>
<td>5.71ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>5.98ms</td>
<td>9.43ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>6.49ms</td>
<td>10.75ms</td>
</tr>
</tbody>
</table>
<br>
<table>
<thead>
<tr>
<th>256 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>7.87ms</td>
<td>7.87ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>8.77ms</td>
<td>11.11ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>9.73ms</td>
<td>13.15ms</td>
</tr>
</tbody>
</table>
<br>
<table>
<thead>
<tr>
<th>512 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>11.67ms</td>
<td>11.36ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>12.98ms</td>
<td>14.93ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>13.89ms</td>
<td>16.94ms</td>
</tr>
</tbody>
</table>
<br>
<table>
<thead>
<tr>
<th>1024 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>22.22ms</td>
<td>20.00ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>24.39ms</td>
<td>25.64ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>25.64ms</td>
<td>33.33ms</td>
</tr>
</tbody>
</table>
<p>These results really surprised me. The light indexed renderer actually starts out <em>faster</em> than the deferred renderer, and doesn&rsquo;t really start to fall behind until you hit 1024 lights. However with either 2xMSAA or 4xMSAA the light indexed renderer absolutely blows away the competition. I actually suspected that I did something wrong in my MSAA implementation, until I verified that I got similar results from the Intel sample. Perhaps there&rsquo;s a better way to handle MSAA in a compute shader for AMD hardware? I didn&rsquo;t spend a lot of time experimenting, so perhaps someone else has a few bright ideas. Either way it&rsquo;s clear that forward rendering scales <em>really</em> well with MSAA on this hardware. Even the G-Buffer pass fares pretty well, as it goes from 1ms to 1.2ms to 1.3ms as the MSAA level increases (1.5ms to 1.9ms to 2.1ms without a z prepass).</p>
<p>So, where does this leave us? Even with these numbers we really don&rsquo;t have a complete picture. Really we need some tests run with&hellip;</p>
<p>1. Different scenes, preferably some with even higher poly counts and/or some tessellation
2. More realistic material variety, including different texture configurations, layer blending, decals
3. A variety of complex BRDF&rsquo;s
4. A few different ambient/bounce lighting configurations
5. More lighting types, with different shadowing configurations
6. More hardware to test on</p>
<p>These things have some big implications on what you store in the G-Buffer, forward shading efficiency, and the cost of a z prepass. That last one is important, since it&rsquo;s mandatory for light indexed deferred but optional for traditional deferred. While it can still be cheaper overall to have a z prepass before your G-Buffer pass (as it was in my case), that could change depending on how your vertex processing costs.</p>
<p>So for now, my conclusion is that Light Indexed Deferred is at least in the realm of practical for most cases. Personally I consider even 256 to be a LOT of lights, so I&rsquo;m not too worried about scaling up to thousands of lights anytime soon. But if anyone has access to different GPU&rsquo;s, I would love to get some more numbers so that I can post them here. So if you happen to have a 7970 or GTX 680 lying around, feel free to download my sample and take down some numbers. Originally the number of lights was hard-coded to 128 in the binary, but I uploaded a new version that lets you toggle through the number of lights that I used for my test runs.</p>
<p>You can find the code and binary on GitHub: <a href="https://github.com/TheRealMJP/DX11Samples/releases/tag/v1.0">https://github.com/TheRealMJP/DX11Samples/releases/tag/v1.0</a></p>
<p>Here are a few numbers for a GTX 680 contributed by Sander van Rossen:</p>
<table>
<thead>
<tr>
<th>128 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>2.30ms</td>
<td>2.60ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>2.62ms</td>
<td>3.86ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>2.85ms</td>
<td>4.95ms</td>
</tr>
</tbody>
</table>
<p>And some more numbers for the AM 7970 courtesy of phantom, gathered at 1280x720:</p>
<table>
<thead>
<tr>
<th>128 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>1.80ms</td>
<td>1.90ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>2.00ms</td>
<td>2.72ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>2.30ms</td>
<td>3.60ms</td>
</tr>
</tbody>
</table>
<br>
<table>
<thead>
<tr>
<th>256 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>2.50ms</td>
<td>2.30ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>2.70ms</td>
<td>3.30ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>3.00ms</td>
<td>4.20ms</td>
</tr>
</tbody>
</table>
<br>
<table>
<thead>
<tr>
<th>512 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>3.30ms</td>
<td>2.90ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>3.80ms</td>
<td>4.20ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>4.20ms</td>
<td>5.20ms</td>
</tr>
</tbody>
</table>
<br>
<table>
<thead>
<tr>
<th>1024 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>5.90ms</td>
<td>4.50ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>6.700ms</td>
<td>6.40ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>7.40ms</td>
<td>7.80ms</td>
</tr>
</tbody>
</table>
<p>Radeon 7970 @ 1920x1080, from 3dcgi:</p>
<table>
<thead>
<tr>
<th>128 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>3.03ms</td>
<td>3.34ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>3.52ms</td>
<td>5.12ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>3.96ms</td>
<td>6.84ms</td>
</tr>
</tbody>
</table>
<br>
<table>
<thead>
<tr>
<th>256 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>4.18ms</td>
<td>4.20ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>4.76ms</td>
<td>6.25ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>5.32ms</td>
<td>8.13ms</td>
</tr>
</tbody>
</table>
<br>
<table>
<thead>
<tr>
<th>512 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>5.85ms</td>
<td>5.46ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>6.62ms</td>
<td>8.00ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>7.19ms</td>
<td>10.0ms</td>
</tr>
</tbody>
</table>
<br>
<table>
<thead>
<tr>
<th>1024 Lights</th>
<th>MSAA Level</th>
<th>Light Indexed Deferred</th>
<th>Tile-Based Deferred</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>No MSAA</td>
<td>10.42ms</td>
<td>8.92ms</td>
</tr>
<tr>
<td></td>
<td>2x MSAA</td>
<td>11.63ms</td>
<td>12.66ms</td>
</tr>
<tr>
<td></td>
<td>4xMSAA</td>
<td>12.82ms</td>
<td>15.63ms</td>
</tr>
</tbody>
</table>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22%22">zxaida</a> - <time datetime="2012-05-29 22:44:10">May 2, 2012</time></p>
<p>Radeon 7970 at 1200MHz clocks 1280x720,default setting 128 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 1.70ms 1.80ms 2x MSAA 1.91ms 2.55ms 4xMSAA 6.25ms 3.27ms 256 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 2.15ms 2.08ms 2x MSAA 2.42ms 2.94ms 4xMSAA 7.35ms 3.74ms 512 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 2.84ms 2.51ms 2x MSAA 3.21ms 3.57ms 4xMSAA 8.771ms 4.46ms 1024 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 4.78ms 3.74ms 2x MSAA 5.43ms 5.26ms 4xMSAA 11.9ms 6.53ms</p>
<hr />
####
[Cyrus Rohani]( "crohani@gmx.com") - <time datetime="2012-05-02 15:04:33">May 3, 2012</time>
<p>Just got my first results after switching from light-prepass to tiled deferred:) I&rsquo;m quite happy with it. Have you had time to investigate arbitrary volume or spot light implementations yet? I haven&rsquo;t had time yet since I&rsquo;m dealing with cascade shadow map performance:( Off topic but, from the latest GDC papers, seems DICE is using VS instancing and the GS to select a rendertarget array index. This is to avoid multiple draws per cascade. But you mention texture arrays being lower performance than an atlas in your tests. Any idea why? Also, I have not heard anyone talk about using an atlas with VS instancing and selecting a viewport index. That would eliminate the array but keep the single draw for all cascades.</p>
<hr />
####
[Andrew Lauritzen]( "andrew.lauritzen@gmail.com") - <time datetime="2012-04-27 12:37:35">Apr 5, 2012</time>
<p>Your demo could be doing something different than mine of course, but if you hit &ldquo;F8&rdquo; in mine you can disable G-buffer rendering/updating and last I checked that was the biggest part of the bottleneck on ATI. Of course it would make more sense if it was something in the significantly-more-complex light/shading pass, but that wasn&rsquo;t what I experienced at least in the past :)</p>
<hr />
####
[Andrew Lauritzen]( "andrew.lauritzen@gmail.com") - <time datetime="2012-04-27 13:07:44">Apr 5, 2012</time>
<p>I will note too that I typically prefer to &ldquo;disable parts of the rendering&rdquo;, etc. rather than use queries. Queries are a bit finicky in that they don&rsquo;t necessarily interact with the pipelining in the GPU in a natural way (i.e. are you measuring end-to-end latency of a submitted command? stalling between each command instead? None is a good solution). Of course there&rsquo;s no perfect solution but I find that a somewhat more consistent and predictable way to profile than queries.</p>
<hr />
####
[Nathan Reed](http://reedbeta.com/ "nathaniel.reed@gmail.com") - <time datetime="2012-04-01 16:49:19">Apr 0, 2012</time>
<p>On my GTX 580 at 1280x720: 128 lights 1x: LI 3.77 TD 3.17 2x: LI 4.14 TD 3.58 4x: LI 4.39 TD 4.17 256 lights 1x: LI 5.68 TD 4.37 2x: LI 6.33 TD 4.95 4x: LI 6.80 TD 5.52 512 lights 1x: LI 8.54 TD 6.10 2x: LI 9.62 TD 6.80 4x: LI 10.42 TD 7.35 1024 lights 1x: LI 16.67 TD 10.99 2x: LI 18.87 TD 12.05 4x: LI 20.41 TD 12.82 Similar pattern to your GTX 570. I should also note I disabled the Z prepass for the tilde deferred cases since it was slowing it down a bit. By the way, in multisampling mode with light-indexed are you running lighting per MSAA sample or just per pixel? And have you looked at detecting edges and running the per-sample lighting only for the tiles (or pixels) containing edges? That can be a big optimization for tiled-deferred, maybe less so for light-indexed deferred as it seems you&rsquo;d have to branch in the pixel shader to implement it. Finally, I wonder how CSAA (NVIDIA) or EQAA (AMD) would affect things. I&rsquo;m not sure how you actually turn those on in D3D, though.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-04-15 18:40:35">Apr 0, 2012</time>
<p>Thanks for those cool graphs Cyrus! I realized a few days ago that I was running an older driver on the machine I did the 570 test on, so it was probably just a driver bug that was resolved at some point. Spot lights are pretty tricky. I&rsquo;ve been meaning to dedicate some time investigating efficent ways to cull them per-tile, but haven&rsquo;t gotten around to it yet. A full frustum-frustum test with SAT seems too heavyweight to be done in a single thread (IIRC it&rsquo;s something like 6*8 + 6*8 + 6*6*8 dot products for the full test), so I&rsquo;m thinking a cheaper approximation might be the way to go. I&rsquo;ve been kicking around something I came up with based on plane/cone intersection tests that&rsquo;s alot cheaper, but gives false positives for a few cases. Rasterizing the volume might be another viable option for expensive lights. I can let you know how it goes once I get some time to work on it more.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-04-14 15:23:53">Apr 6, 2012</time>
<p>@directtovideo I suspected the same thing regarding shared memory pressure, so I ran a few experiments where I varied the thread group size but I wasn&rsquo;t able to improve the performance. And you&rsquo;re absolutely right about the scheduling&hellip;it&rsquo;s really the key difference between the two techniques. Ultimately it comes to down to whether the the flexibility you get from shading in a compute shader ends up winning out over the efficiency of hardware scheduling, and taking that into account along with having to render out a G-Buffer (for tiled deferred), or requiring geometry to be rasterized twice (for indexed deferred). @Anonymous For a large number of lights in a scene having a per-pixel list doesn&rsquo;t seem very compelling to me. The problems to me are: A. You&rsquo;d have to compute light intersections per-list rather than per-tile, which means that you can&rsquo;t compute the intersections for many lights in parallel like you can with per-tile lists. You could rasterize the light volumes and append the index to the per-tile linked lists (like in the AMD demo, as you suggested) but I&rsquo;d imagine that would still be much slower. B. Your granularity during the forward lighting phase is limited by branching coherency, so it doesn&rsquo;t seem worth it to do fine-grained light intersection C. You&rsquo;ll consume a lot more memory with per-pixel lists D. If you use a linked list, just reading the light indices in the forward lighting phase is going to be slower. One thing I discovered early on was that just reading indices can be a serious performance drain, so I tried to make it as cheap as possible. For a smaller number of lights it might make sense though, especially if going fine-grained allows you to do a better job culling non-spherical light sources.</p>
<hr />
####
[]( "") - <time datetime="2012-04-14 14:33:19">Apr 6, 2012</time>
<p>MJP - great read. Just wondering, why didn&rsquo;t you try using a per-pixel list (like the AMD order-independent-transparency demo) for the light indices? Do you think that would have worse performance? Thanks.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-04-28 14:41:44">Apr 6, 2012</time>
<p>You definitely have a good point regarding the queries&hellip;I try to only use them to get a rough idea of timings but even then they can be quite off from the delta you get in overall frame time. I put it in a setting to disable G-Buffer rendering, and that shows a delta of about 4-4.5ms with 4xMSAA which is definitely pretty significant and more in line with your findings. I can actually get a similar result from my queries if try to force a sync point with a simple compute shader that reads from the MSAA G-Buffer textures. I would suspect that there might be something else expensive going on here, perhaps an expensive decompression step to allow the shader to sample the MSAA textures. Thank you for your input!</p>
<hr />
####
[Cyrus Rohani]( "") - <time datetime="2012-04-15 08:14:04">Apr 0, 2012</time>
<p>Not sure why you had issues with the GTX 570 at 1920x1080, it worked fine with mine. Here&rsquo;s my results: Windows 7, Intel Q6600, 2.40Ghz NVIDIA GeForce 570 GTX, 296.10 drivers 1280x720: LIDR TBDR No MSAA 4.34 4.03 2x MSAA 4.78 4.54 4x MSAA 5.18 5.05 No MSAA 6.53 5.40 2x MSAA 7.35 6.05 4x MSAA 7.87 6.66 No MSAA 9.90 7.46 2x MSAA 11.23 8.19 4x MSAA 12.04 8.84 No MSAA 19.23 13.15 2x MSAA 21.73 14.49 4x MSAA 23.80 15.38 1920x1080: LIDR TBDR No MSAA 7.24 6.80 2x MSAA 8.00 7.69 4x MSAA 8.47 9.17 No MSAA 11.62 9.90 2x MSAA 12.98 10.98 4x MSAA 13.88 12.19 No MSAA 18.51 14.49 2x MSAA 20.40 15.87 4x MSAA 21.73 17.24 No MSAA 37.03 27.02 2x MSAA 40.00 28.57 4x MSAA 43.47 30.30 I uploaded a graph of results from this page, substituting my GTX 570 results: <a href="http://img543.imageshack.us/img543/4589/lidrvstidr.png">http://img543.imageshack.us/img543/4589/lidrvstidr.png</a> Do you have any idea about the performance difference if using arbitrary light volumes? Or frustum volumes for spot lights? Thanks.</p>
<hr />
####
[Sander van Rossen](http://gravatar.com/logicalerror "sander.vanrossen@gmail.com") - <time datetime="2012-04-01 23:05:21">Apr 0, 2012</time>
<p>It was at the default resolution, I don&rsquo;t know if that&rsquo;s 1280×720.. 128 lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 2.3ms 2.6ms 2x MSAA 2.62ms 3.86ms 4xMSAA 2.85ms 4.95ms 256 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 3.5ms 3.95ms 2x MSAA 3.95ms 4.76ms 4xMSAA 4.3ms 6.28ms 512 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 5.26ms 5.71ms 2x MSAA 5.95ms 7.87ms 4xMSAA 6.45ms 9.61ms 1024 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 10.2ms 12.6ms 2x MSAA 11.62ms 15.15ms 4xMSAA 12.65ms 16.39ms And it&rsquo;s dual gtx680 (so 2x with sli, not a single card). These results make me wonder if SLI is configured correctly &hellip; or if something in the app makes it impossible for the driver to use SLI effectively. It&rsquo;s just hard to believe that dual gtx680&rsquo;s can be beaten so easily heh</p>
<hr />
####
[metatronico]( "niels@frohling.biz") - <time datetime="2012-04-01 17:10:18">Apr 0, 2012</time>
<p>AMD 5870 128 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 3.48ms 3.59ms 2x MSAA 4.03ms 4.60ms 4xMSAA 4.44ms 5.49ms</p>
<hr />
####
[ethatron]( "niels@paradice-insight.us") - <time datetime="2012-04-01 17:30:56">Apr 0, 2012</time>
<p>Continued &hellip; (above the &ldquo;No MSAA&rdquo; is swapped, sorry - yes on the 5870 &ldquo;No MSAA&rdquo; and &ldquo;Some MSAA&rdquo; swaps the rank = 512 indexed can&rsquo;t manage competing) 256 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 5.18ms 4.42ms 2x MSAA 5.78ms 5.95ms 4xMSAA 6.32ms 6.89ms 512 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 7.51ms 5.95ms 2x MSAA 8.40ms 7.63ms 4xMSAA 9.09ms 8.69ms 1024 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 14.28ms 10.20ms 2x MSAA 15.87ms 12.98ms 4xMSAA 17.24ms 14.28ms All default resolution.</p>
<hr />
####
[ethatron]( "niels@paradice-insight.us") - <time datetime="2012-04-01 18:08:26">Apr 0, 2012</time>
<p>LI seems ALU-bound and TB seems at least memory-related on the 5870, overclocking the core yields different speedup for LI and TB respectively (looking at the two extremes): LI speeds up more or less linear on all accounts, that is 850 to 990 (MHz) ^= 3.59 to 3.19 (linear is 3.08) ^= 17.24 to 14.92 (linear is 14.80). TB is stalemate in the &ldquo;No MSAA&rdquo; case for 990, not faster anymore, that is 850 to 990 (MHz) ^= 3.48 to 3.14 (linear is 2.98) ^= 14.28 to 12.65 (linear is 12.26). On the slow extreme it&rsquo;s 13.5% vs. 11.5% speedup from a 14.2% overclock, that is TB gains 85% vs. LI. To me it seems on the GKs it&rsquo;s only that TB is faster because of the large sustainable memory bandwidth. And it is visible, that if I would clock my 5870 at say 2GHz, then TB would never win. LI vs. TB seems a ALU vs. mem tradeoff, or not that relevant if the architecture is somewhere in the middle. But as memory speeds are unlikely to rise much further, and often are below our GDDR5 speeds on medium class cards, and core clock still keeps rising even on medium class cards, I&rsquo;d say LI has a rosier prognosis.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-04-01 18:39:12">Apr 0, 2012</time>
<p>@Nathan, For Light Indexed Deferred it&rsquo;s really just forward lighting, so I just turn on MSAA for the render target and let the hardware do its thing. This means that you only shade multiple times per pixel along triangle edges where the triangle doesn&rsquo;t full cover all subsamples of a pixel. You could certainly use CSAA if you wanted, you just turn it on by using a different quality level. I&rsquo;m not sure about EQAA. @ethatron Thank you for sharing such a detailed analysis! Your findings make sense though, since light indexed deferred tends to be VERY heavy on ALU in the pixel shader.</p>
<hr />
####
[3dcgi](http://3dcgi.com "tmartin@ieee.org") - <time datetime="2012-04-01 19:49:59">Apr 0, 2012</time>
<p>Radeon 7970 at stock clocks full screen on a 1080p monitor with the taskbar hidden so the rendering window is a title bar short of 1080p. 128 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 3.03ms 3.34ms 2x MSAA 3.52ms 5.12ms 4xMSAA 3.96ms 6.84ms 256 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 4.18ms 4.20ms 2x MSAA 4.76ms 6.25ms 4xMSAA 5.32ms 8.13ms 512 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 5.85ms 5.46ms 2x MSAA 6.62ms 8.00ms 4xMSAA 7.19ms 10.00ms 1024 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 10.42ms 8.92ms 2x MSAA 11.63ms 12.66ms 4xMSAA 12.82ms 15.63ms</p>
<hr />
####
[ethatron]( "niels@paradice-insight.us") - <time datetime="2012-04-01 20:53:42">Apr 0, 2012</time>
<p>@mjp Here: &ldquo;No MSAA 3.48ms 3.59ms&rdquo; I accidentally flipped the number, it should be &ldquo;No MSAA 3.59ms 3.48ms&rdquo;. :^)</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-04-01 12:38:24">Apr 0, 2012</time>
<p>Thanks for posting those Sanders! The original binary was running at 128 lights. I just uploaded a new one that lets you switch the number of lights. I would assume that you ran at the default resolution of 1280x720?</p>
<hr />
####
[Nathan Reed](http://reedbeta.wordpress.com/ "nathaniel.reed@gmail.com") - <time datetime="2012-04-02 12:02:32">Apr 1, 2012</time>
<p>@MJP Hah, using the hardware to do what it&rsquo;s designed for - who does that? :) But anyway, it seems that this is a bit of an unfair comparison because light-indexed is mostly shading per-pixel while tiled-deferred is (I presume) shading per sample in all cases. Tiled-deferred with MSAA edge detection could turn things around on the AMD cards. (Of course, the fact that MSAA &lsquo;just works&rsquo; with light-indexed is itself an argument in its favor&hellip;)</p>
<hr />
####
[Nathan Reed](http://reedbeta.wordpress.com/ "nathaniel.reed@gmail.com") - <time datetime="2012-04-02 13:33:37">Apr 1, 2012</time>
<p>Aha, that&rsquo;s great! In that case, yeah, the different MSAA scaling between the two techniques is very interesting.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-04-02 13:02:02">Apr 1, 2012</time>
<p>@Nathan The tile-based deferred renderer does use edge detection. It compares the normal + depth of all subsamples in a pixel, and appends the coordinate of those pixels to a list in shared memory. Then all of the subsamples from those pixels distributed evenly among threads in the thread group so they can be shaded. The comparison is actually pretty conservative in my sample, so you end up doing per-sample shading on significantly fewer pixels than in the forward-rendered case. But even with that optimization the AMD cards take a huge hit from MSAA, which is a bit puzzling to me.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-11-26 14:09:31">Nov 1, 2012</time>
<p>Hi Matt, I&rsquo;ve been using a simple frustum/cone that checks to ensure that some part of the cone is on the positive side of all 6 frustum planes (using the cone/plane test from Real-Time Collision Detection). It certainly works when the cone is entirely on the negative side of one of the 6 planes, but for cones that are rather large relative to the frustum you can get cases where the cone is on the positive side of all 6 planes but still doesn&rsquo;t intersect the actual frustum (you can actually get the same problem with a sphere/frustum test if you do it the same way). Constructing additional planes to test against will help, but doesn&rsquo;t solve the problem entirely. If you&rsquo;re not running into the same issues, then perhaps you&rsquo;re doing something that&rsquo;s a bit more sophisticated?</p>
<hr />
####
[3dcgi](http://3dcgi.com "tmartin@ieee.org") - <time datetime="2012-04-02 05:06:07">Apr 1, 2012</time>
<p>I don&rsquo;t have time to perform a full run, but here are a few 1280x720 numbers for comparison with a stock Radeon 7970. 1024 Lights MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 6.02ms 4.63ms 2x MSAA 6.85ms 6.58ms 4xMSAA 7.52ms 8.00ms One thing I noticed from the other reported numbers is the GTX580 is faster than the 680 at tiled deferred yet the situation changes for index deferred. I&rsquo;m surprised at how much faster the Radeon 7970 is than the GTX 680. At least with 1024 lights.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-04-02 01:05:29">Apr 1, 2012</time>
<p>Yeah, the default resolution is 1280x720. SLI won&rsquo;t kick in unless the driver has a profile for the app (or you use NVAPI to manually select a profile), so I&rsquo;m sure that it&rsquo;s just running on 1 GPU.</p>
<hr />
####
[Sander van Rossen](http://gravatar.com/logicalerror "sander.vanrossen@gmail.com") - <time datetime="2012-04-01 01:04:46">Apr 0, 2012</time>
<p>I couldn&rsquo;t see how many lights your binary was displaying, but these are the results for my ridiculously overpowered dual gtx680&rsquo;s: MSAA Level Light Indexed Deferred Tile-Based Deferred No MSAA 2.3ms 2.6ms 2x MSAA 2.62ms 3.86ms 4xMSAA 2.85ms 4.95ms</p>
<hr />
####
[Andrew Lauritzen]( "andrew.lauritzen@gmail.com") - <time datetime="2012-04-19 11:57:00">Apr 4, 2012</time>
<p>The big hit on AMD cards with MSAA seems to be in the G-buffer rendering phase in my brief testing. Never tracked down why, as the cards have plenty of bandwidth available. Perhaps a ROP throughput bottleneck, I&rsquo;m not sure. Ideally if MSAA compression was &ldquo;perfect&rdquo;, it should be about the same overhead as MSAA with forward as it (roughly) is on NVIDIA.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-04-19 22:04:49">Apr 4, 2012</time>
<p>Hi Andrew, According to my profiling (perfomed via queries) filing the G-Buffer on my AMD 6970 only accounts for 2.09ms at 1920x1080, 4xMSAA with no z prepass. With a z prepass it takes 1.31ms, with 0.4ms for the z prepass. The increase in frame time mostly comes from the lighting compute shader, which goes from 2.7ms with no MSAA to 6.8ms with 4xMSAA for the 128 light case.</p>
<hr />
####
[anteru]( "wordpress@catchall.shelter13.net") - <time datetime="2012-06-06 01:29:35">Jun 3, 2012</time>
<p>GTX 680, 1280x720 (Numbers are light prepass, tile deferred.) 256 Lights 1x 3.58 4.15 2x 4.07 5.32 4x 4.4 6.25 512 Lights 1x 5.46 5.6 2x 6.2 7.75 4x 6.7 9.4 1024 Lights 1x 10.6 12.2 2x 12.04 14.7 4x 13.1 16.1</p>
<hr />
####
[directtovideo](http://directtovideo.wordpress.com "smaash@yahoo.com") - <time datetime="2012-04-05 09:23:34">Apr 4, 2012</time>
<p>MJP: this is all speculation, but one problem I&rsquo;ve found with the tile-based deferred and splitting the samples across threads is the amount of shared memory. I&rsquo;ve had real problems with this especially on geforce - seems real sensitive to sharedmem (and effect on occupancy). The other thing is, with the deferred version (with quite large threadgroups running 16x16 tiles = 256 threads - I actually went for 8x8 here) you&rsquo;re making quite a big statement about your own scheduling / work balancing - the light indexed version is running a pixel shader to do the lighting so the hardware is scheduling the work in its own, probably smart, way. Wonder if that&rsquo;s a part of the difference. Nice comparison though! Very useful to see.</p>
<hr />
####
[Matías N. Goldberg (@matiasgoldberg)](http://twitter.com/matiasgoldberg "matiasgoldberg@twitter.example.com") - <time datetime="2012-10-28 10:36:52">Oct 0, 2012</time>
<p>At least on a AMD Radeon HD 7770, there are artifacts when using 1024 lights (all MSAA settings) with Forward rendering (G-Buffer works fine) Here&rsquo;s an image highlighting the artifacts: <a href="http://i.imgur.com/5Gvl2.jpg">http://i.imgur.com/5Gvl2.jpg</a> IT ONLY APPEARS WHEN LOOKING FROM THAT ANGLE Although it looks very small, it&rsquo;s actually *very* noticeable because it flickers in blocks (tiles) across ALL the roof border; even when the camera is completely still. It works fine with 512 lights. My theory from a quick glance is that those tiles have more lights than what the card allows to hold in the linked list buffer (is there a hard limit? or may be there&rsquo;s a hard limit in the forward rendering loop&hellip;?) and race conditions cause different lights to be dropped each frame; therefore the tile always has a light list that doesn&rsquo;t hold all the needed lights; being always different. Each tile flickers going lighter &amp; darker. I don&rsquo;t have an NVIDIA DX11 card to compare with, unfortunately.</p>
<hr />
####
[HPG 2012 | dickyjim](http://dickyjim.wordpress.com/2012/07/04/hpg-2012/ "") - <time datetime="2012-07-04 04:35:08">Jul 3, 2012</time>
<p>[&hellip;] Clustered Deferred and Forward Shading This was another paper I had read before attending. The value of the clustering for the samples is based on the spatial distribution of a large group of lights and how that interacts with tile based deferred shading where each tile contains a large range of depths. As an additional clustering key, they’ve also looked at normal cone clustering. Although this was expensive in their scenes, the normal cone clustering looked like it would have value in tiles with less depth and normal variance which is more likely in a game environment. One other thing of note, they mentioned during the managing clusters that their code was 2 passes on Fermi but could be 1 on Kepler due to the improved atomic performance. Overall, I think that a large part of the positive results for the test was due to the selective nature of the scenes used to test the technique. I’d like to see the performance results in a wider range of scenes (for example as in Matt Pettineo’s light indexed work). [&hellip;]</p>
<hr />
####
[directtovideo](http://directtovideo.wordpress.com "mattswoboda@yahoo.co.uk") - <time datetime="2012-11-27 01:35:16">Nov 2, 2012</time>
<p>MJP: Looks like I&rsquo;m doing the same as you then. Clearly I just haven&rsquo;t managed to generate a case that breaks it yet, so will look out for that.</p>
<hr />
####
[WIP: Deferred Rendering | Chetan Jags](http://chetanjags.wordpress.com/2014/09/25/wip-deferred-rendering/ "") - <time datetime="2014-09-25 08:05:22">Sep 4, 2014</time>
<p>[…] mentioned in Battlefield3 presentation and in this demo from Intel. Thinking about ForwardPlus or Light Indexed Deferred for transparent […]</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-10-28 23:42:42">Oct 0, 2012</time>
<p>Hi Matias, I haven&rsquo;t seen any similar artifacts myself, but I&rsquo;m not terribly surprised. It certainly wouldn&rsquo;t be the first time that I encountered quirky behavior with compute shaders that use atomics on shared memory variables. There&rsquo;s actually no linked list, each tile has enough room in a buffer to store indices for N lights (where N is the maximum number of lights in the scene). So there *should* be enough room in the buffer to store 1024 lights, as well as in shared memory.</p>
<hr />
####
[Lukas M]( "mjp@lukasmeindl.at") - <time datetime="2012-11-09 14:48:16">Nov 5, 2012</time>
<p>@matiasgoldberg i got the same problem. I got a HD Radeon 7950 - it flickers on that angle and with the 1024 lights activatated. However it works fine with less lights.</p>
<hr />
####
[ozlael](http://ozlael.egloos,com "ozjjangozjjang@gmail.com") - <time datetime="2012-09-02 06:15:28">Sep 0, 2012</time>
<p>always thank for good article :-)</p>
<hr />
####
[Matías N. Goldberg (@matiasgoldberg)](http://twitter.com/matiasgoldberg "matiasgoldberg@twitter.example.com") - <time datetime="2012-11-03 10:04:07">Nov 6, 2012</time>
<p>Hi, thanks for the answer. Yeah, when I was referring to the linked list buffer, I was thinking you probably just used a big per-tile array. This is unsurprising for me either, CS puts more responsibility to developers than pixel shaders, but it&rsquo;s a new tech where driver, compiler &amp; even HW bugs can&rsquo;t be ruled out yet. So, either the driver clamps the buffer size, the HW&rsquo;s atomic operation is malfunctioning, there&rsquo;s a rare race condition somewhere, the tile is somehow overflowing, or the pixel shader in the forward pass is just refusing to read the entire buffer and just parsing it partially. So many possibilities&hellip;. I just wanted to know if someone else was able to reproduce the artifacts (only shows when lightcount = 1024; while looking from that particular angle as in the screenshot)</p>
<hr />
####
[directtovideo](http://directtovideo.wordpress.com "mattswoboda@yahoo.co.uk") - <time datetime="2012-11-26 02:11:22">Nov 1, 2012</time>
<p>MJP: Did you have any joy with spot culling? I&rsquo;m running with the cone/plane test per frustum plane I had on PS3/SPU. Haven&rsquo;t seen any false positives ..</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>10 Things That Need To Die For Next-Gen</title>
			<link>https://therealmjp.github.io/posts/things-that-need-to-die/</link>
			<pubDate>Tue, 06 Dec 2011 09:54:34 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/things-that-need-to-die/</guid>
			<description>Lately I&amp;rsquo;ve been thinking about things in graphics that have long worn out their welcome, and I started a list of techniques that I hope will be nowhere in sight once everyone moves on to next-gen console hardware (or starts truly exploiting high-end PC hardware). Here they are, in no particular order:
Phong/Blinn-Phong - we need more expressive BRDF&amp;rsquo;s for our materials, and these guys are getting in the way. Phong is just plain bad, as it doesn&amp;rsquo;t even produce realistic streaks at glancing angles (due to using the reflection vector and not the halfway vector like with microfacet-based BRDF&amp;rsquo;s).</description>
			<content:encoded><![CDATA[<p>Lately I&rsquo;ve been thinking about things in graphics that have long worn out their welcome, and I started a list of techniques that I hope will be nowhere in sight once everyone moves on to next-gen console hardware (or starts truly exploiting high-end PC hardware). Here they are, in no particular order:</p>
<ol>
<li>
<p><strong>Phong/Blinn-Phong</strong> - we need more expressive BRDF&rsquo;s for our materials, and these guys are getting in the way. Phong is just plain bad, as it doesn&rsquo;t even produce realistic streaks at glancing angles (due to using the reflection vector and not the halfway vector like with microfacet-based BRDF&rsquo;s). <a href="http://renderwonk.com/publications/s2010-shading-course/hoffman/s2010_physically_based_shading_hoffman_b_notes.pdf">Energy-conserving Blinn-Phong</a> with a proper fresnel factor is a <em>huge</em> step in the right direction, but we can still do better. Personally I&rsquo;m a big fan of Cook-Torrance for isotropic materials. It requires quite a bit more math compared to Blinn-Phong, but if there&rsquo;s one thing modern GPU&rsquo;s are good at it&rsquo;s crunching through some ALU-heavy shader code. Anisotropic BRDF&rsquo;s are also really important for a lot of materials, and I think we need to start getting them working side-by-side with Cook-Torrance in our deferred renderers. Another important hurdle to overcome is making sure our pre-rendered specular environment maps match our BRDF. Blurring the mip levels is a good approximation for Phong, but not so much for Blinn-Phong or Cook-Torrance. For anisotropic BRDF&rsquo;s, it&rsquo;s not even close.</p>
</li>
<li>
<p><strong>Specular Aliasing</strong> - during this generation we got pretty good at making things bumpy and shiny. What we didn&rsquo;t get good at was making sure all of that bumpy shiny stuff didn&rsquo;t turn into aliasing hell. Stephen Hill gave a <a href="http://blog.selfshadow.com/2011/07/22/specular-showdown/">great summary</a> of the current lay of the land when it comes to specular antialiasing techniques, as well as new technique for pre-computing them into gloss maps. Unfortunately those techniques are either formulated in terms of  Blinn-Phong (in the case of Toksvig AA), or aim to replace Blinn-Phong (in the case of LEAN/CLEAN). This means that we still have a bit more work to do if we want to move on to Cook-Torrance. However I think these techniques have given us a great starting point, and I&rsquo;m fairly confident that with some information about the variance of a normal map we can tackle the problem for other BRDF&rsquo;s. Selective supersampling is another (expensive) possibility for problematic scenarios, which can even be seamlessly integrated into MSAA on DX11 hardware.</p>
</li>
<li>
<p><strong>SSAO</strong> - I don&rsquo;t think too many would agree with me on this one, but I&rsquo;ve just never been a big fan of SSAO. It was tremendously clever idea when it came out, and it certainly has improved quite a bit since its original inception. However I don&rsquo;t think I&rsquo;ll ever get over the idea that the technique is fundamentally handicapped in terms of the information it has to work with. I&rsquo;d really like to aggressively pursue alternative techniques based on primitive shapes and/or low-resolution representations of scene meshes, with the hopes getting AO that&rsquo;s more stable and captures larger scale occlusion. But I&rsquo;m sure we&rsquo;ll end up still using SSAO to fill in the cracks (pun intended).</p>
</li>
<li>
<p><strong>DXT5 Normal Maps</strong> - this one is a no-brainer, we just need to ditch current consoles and embrace BC5.</p>
</li>
<li>
<p><strong>Geometry Aliasing</strong> - we&rsquo;ve been fighting this one for a long time now, but it still lingers. In fact you could almost argue that this problem has gotten <em>worse</em> due to the widespread use of deferred rendering and HDR rendering. Screen-space techniques like MLAA and FXAA have given us a great big band-aid to throw over the problem, but they are exactly that: a band-aid. They&rsquo;re never going to completely solve the problem on their own, which means that we need to find better ways to make use of MSAA if we really want to solve some of the tougher cases. For deferred rendering this means being smart about which subsamples we shade, as well as how we shade them. <a href="http://software.intel.com/en-us/articles/deferred-rendering-for-current-and-future-rendering-pipelines/">Andrew Lauritzen&rsquo;s work</a> has given us a great starting point, but I&rsquo;d imagine we&rsquo;ll need to specifically tailor our approach for whatever target hardware we&rsquo;re working with. For dealing with HDR, we need to be aware of the problems caused by tone mapping and make sure that we effectively work around them. Humus&rsquo;s approach of individually tone mapping each subsample produces the desired result, however it also means keeping subsamples around until you perform tone mapping (which is the last stop in your post processing pipeline if you&rsquo;re doing everything in HDR). Andrew Lauritzen suggested a <a href="http://forum.beyond3d.com/showpost.php?p=1595484&amp;postcount=1094">clever idea</a> over on the beyond3d forums, which was to apply tone mapping, resolve, then apply the inverse tone mapping operator to get an HDR value back. I tried it and it does work&hellip;at least as long as you&rsquo;re using a tone mapping operator that&rsquo;s easily invertible. The inverse operator can get really nasty in some cases, and won&rsquo;t exist at all if you end up clamping to 1.0 as part of your tone mapping. Speaking of tone mapping&hellip;</p>
</li>
<li>
<p><strong>Crappy Tone Mapping</strong> - no more linear, no more Reinhard. Filmic tone mapping is awesome, and easy to integrate if you use the <a href="http://filmicgames.com/Downloads/GDC_2010/Uncharted2-Hdr-Lighting.pptx">variant proposed by John Hable</a>.</p>
</li>
<li>
<p><strong>No GI <em>-</em></strong> I don&rsquo;t think that we all need to go for real-time GI, since most games don&rsquo;t need completely dynamic lighting or geometry. However it&rsquo;s time to stop faking GI with ambient lights/fill lights/bounce lights/whatever. Having a good GI bake produces smoother, more realistic lighting that&rsquo;s easier for artists to author. And if people <em>do</em> go for real-time GI techniques, I hope that they can do so without taking a severe drop in quality compared to offline solutions.</p>
</li>
<li>
<p><strong>Crappy Depth of Field and Motion Blur</strong> - both of these guys have a lot of room for improvement. Depth of field has gotten some recent attention from the heavy hitters like <a href="http://www.crytek.com/sites/default/files/S2011_SecretsCryENGINE3Tech_0.ppt">Crytek</a>, <a href="http://publications.dice.se/attachments/BF3_NFS_WhiteBarreBrisebois_Siggraph2011.pptx">DICE</a>, and <a href="http://www.nvidia.com/content/PDF/GDC2011/GDC2011EpicNVIDIAComposite.pdf">Epic</a>, which has mostly focused on reproducing iris-shaped blur patterns for realistic bokeh effects. This has resulted in some promising research, but I think the jury is still out on the best way to approach the problem. There&rsquo;s also the issue of foreground blur with proper transparency, which is still something of an elephant in the room. Motion blur, on the other hand, isn&rsquo;t getting quite as much attention. Working only screen-space is extremely limiting for motion blur, in fact I would say it&rsquo;s even more limiting than it is for depth of field. There have been attempts at supplementing screen-space approaches with fins or geometry stretching, but personally I&rsquo;m still not satisfied with the results of my own experiments. What I&rsquo;d really love to do is some sort of cheap, scaled-down version of stochastic rasterization combined with screen-space blurring to help remove the noise. Nvidia has some <a href="http://research.nvidia.com/publication/real-time-stochastic-rasterization-conventional-gpu-architectures">recent research</a> in this area, so I&rsquo;m holding out hope.</p>
</li>
<li>
<p><strong>Sprite-based Lens Flares</strong> - these looked cheesy when they first showed up in games, and they still do. Using <a href="http://www.3dmark.com/wp-content/uploads/2010/12/3DMark11_Whitepaper.pdf">FFT to perform convolutions</a> in frequency space lets you convolve highlights with arbitrary (low-resolution) kernels, which is still limiting but in many ways looks a lot better than sprites. But what we <em>really</em> need is for someone to get <a href="http://www.mpi-inf.mpg.de/resources/lensflareRendering/">this</a> working in a game, so that we can all have awesome physically-based flares. :D</p>
</li>
<li>
<p><strong>Simple Fog</strong> - we&rsquo;ve got shaders now&hellip;we don&rsquo;t need to do the same old depth-based fog. The time has come to upgrade to a physically-based scattering model, such as <a href="http://www.cs.berkeley.edu/~ravir/papers/singlescat/scattering.pdf">this one</a>.</p>
</li>
</ol>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22prayingmantis@toothfairy.com%22">Mantis</a> - <time datetime="2011-12-08 09:48:28">Dec 4, 2011</time></p>
<p>Now, picture this; what if instead of the above, we made better games? Picture if Bethesda diverted $50k from their &ldquo;artists&rdquo; to the QA department; who knows, the main quest might work half the time and the PS3 port might be stable for more than about 20 hours!</p>
<hr />
####
[Mantis]( "prayingmantis@toothfairy.com") - <time datetime="2011-12-08 09:49:41">Dec 4, 2011</time>
<p>Not that better graphics are a bad thing, obviously.</p>
<hr />
####
[default_ex]( "default_ex@live.com") - <time datetime="2011-12-08 13:57:23">Dec 4, 2011</time>
<p>A really good solution to most of these problems is to develop a new color space, one which can carry energy information (like xyY) but is additive in nature (like RGB). Think about it, how many of these effects are energy dependent but how much we substitute energy with luminance: HDR, inscatter, lens flare, self-illumination. Though I think specular work is the saddest of all our rendering tech so far. Specular light in the real world is just reflection of light energy, shape deformed by the surface geometry. Yet we continue the travesty of only conveying the light color, never conveying the light source (bulb) itself in the specular reflection.</p>
<hr />
####
[djmips](http://gravatar.com/djmips "david.c.galloway@gmail.com") - <time datetime="2011-12-08 15:55:30">Dec 4, 2011</time>
<p>How about just get rid of lens flares period? I never liked them in games or in movies&hellip; Why do they have to show up in first person views especially? I&rsquo;ve never had my eyes flare like that. :)</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-12-12 01:20:16">Dec 1, 2011</time>
<p>Nice additions Nathan! I&rsquo;ve always wanted to experiment with Sample Distribution Shadow Maps, although I worry about temporal aliasing as you adjust the split depths. Calculating the adjusted split depths on the GPU also concerns me, as it limits your ability to cull on the CPU. However I really like the general approach, and definitely agree that we need to make better use of shadow resolution and not just use wide filtering kernels (especially if we want plausible soft shadows)</p>
<hr />
####
[default_ex]( "default_ex@live.com") - <time datetime="2011-12-12 11:44:18">Dec 1, 2011</time>
<p>Even something like a pixel shader version of smart filtering (<a href="http://web.archive.org/web/20070624082603/http://www.hiend3d.com/smartflt.html">http://web.archive.org/web/20070624082603/http://www.hiend3d.com/smartflt.html</a>) would be a massive improvement on shadow mapping. The blocky unfiltered shadow maps convey the shape of the shadow well enough, just need to enhance that shape to fit screen resolution. I&rsquo;ve tried using HQX filters recently, but that tends to introduce stray pixels at the better defined portions of the shadow map, which is likely a flaw in my implementation.</p>
<hr />
####
[Rim]( "remigius@netforge.nl") - <time datetime="2011-12-06 02:12:11">Dec 2, 2011</time>
<p>&ldquo;I’ve just never been a big fan of SSAO.&rdquo; I did a paper ages ago concluding SSAO likely wouldn&rsquo;t take off as a common technique. Boy was I wrong, so I&rsquo;ll agree with you here out of spite :)</p>
<hr />
####
[u2bleank](http://u2bleank.tumblr.com/ "dtoyou@bleank.com") - <time datetime="2011-12-07 04:32:16">Dec 3, 2011</time>
<p>11. Simple shader for blending operations</p>
<hr />
####
[Nathan Reed](http://reedbeta.com/ "nathaniel.reed@gmail.com") - <time datetime="2011-12-10 17:40:56">Dec 6, 2011</time>
<p>I&rsquo;d like to add: 11. Shadow Aliasing. The trend has been to blur the $%#! out of the shadow maps to hide it, but that&rsquo;s not always desirable. There&rsquo;s <a href="http://visual-computing.intel-research.net/art/publications/sdsm/">some interesting work</a> on that front from Andrew Lauritzen, always a heavy hitter. 12. Visible polygonization of shapes that should be smooth&mdash;especially at silhouette edges. We have hardware tessellation now; let&rsquo;s use it! 13. Characters whose only hair options are bald, short or tight buns/ponytails/dreadlocks. There has been some great <a href="http://dl.acm.org/citation.cfm?id=1837102">work</a> lately on hair simulation and rendering which has yet to be put to use in a game, to my knowledge.</p>
<hr />
####
[What I&#8217;ve been working on for the past 2 years | The Danger Zone](http://mynameismjp.wordpress.com/2013/06/11/what-ive-been-working-on/ "") - <time datetime="2013-06-11 23:48:51">Jun 2, 2013</time>
<p>[…] most out of next-gen hardware. By my count we’ve crossed off around 8 or so of the things on my list, and hopefully the entire industry will collectively figure out how to make all of them extinct. […]</p>
<hr />
####
[Louis Castricato](http://wirezapp.net "ljcrobotic@yahoo.com") - <time datetime="2011-12-19 03:45:31">Dec 1, 2011</time>
<p>We could just replace ssao with ssdo, or hbao. Both produce much better results and still maintain great framerates. Also, I implemented the improved fog in under 45min. New record =P</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>GPU Profiling in DX11 with Queries</title>
			<link>https://therealmjp.github.io/posts/profiling-in-dx11-with-queries/</link>
			<pubDate>Thu, 13 Oct 2011 08:59:37 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/profiling-in-dx11-with-queries/</guid>
			<description>For profiling GPU performance on the PC, there aren&amp;rsquo;t too many options. AMD&amp;rsquo;s GPU PerfStudio and Nvidia&amp;rsquo;s Parallel Nsight can be pretty handy due to their ability to query hardware performance counters and display the data, but they only work on each vendor&amp;rsquo;s respective hardware. You also might want to integrate some GPU performance numbers into your own internal profiling systems, in which case those tools aren&amp;rsquo;t going to be of much use.</description>
			<content:encoded><![CDATA[<p><a href="/images/converted/profiling-in-dx11-with-queries/queryprofiling_64bit-2011-10-13-01-55-52-65.png"><img src="/images/converted/profiling-in-dx11-with-queries/queryprofiling_64bit-2011-10-13-01-55-52-65_resized_1024.png" alt="" title="QueryProfiling_64bit 2011-10-13 01-55-52-65"></a></p>
<p>For profiling GPU performance on the PC, there aren&rsquo;t too many options. AMD&rsquo;s GPU PerfStudio and Nvidia&rsquo;s Parallel Nsight can be pretty handy due to their ability to query hardware performance counters and display the data, but they only work on each vendor&rsquo;s respective hardware. You also might want to integrate some GPU performance numbers into your own internal profiling systems, in which case those tools aren&rsquo;t going to be of much use.</p>
<p>To get around this, it&rsquo;s possible to use D3D11 timestamp queries to get coarse-grained timing info for different parts of the frame. It&rsquo;s a ways off from the kind of info you get from the vendor-specific tools, but it&rsquo;s a lot better than nothing. It&rsquo;s also pretty easy to implement. To profile a portion of your frame, you need a trio of ID3D11Query objects. Two of them need to have the type D3D11_QUERY_TIMESTAMP, and are used to get the GPU timestamp at the start and end of the block you want to profile. The third needs to have the type D3D11_QUERY_TIMESTAMP_DISJOINT, and it tells you whether your timestamps are invalid as well as the frequency used for converting from ticks to seconds. In practice it goes like this:</p>
<p>When starting a profiling block:</p>
<ul>
<li>Call ID3D11DeviceContext::Begin and pass the DISJOINT query</li>
<li>Call ID3D11DeviceContext::End and pass the start TIMESTAMP query</li>
</ul>
<p>When ending a profiling block:</p>
<ul>
<li>Call ID3D11DeviceContext::End and pass the end TIMESTAMP query</li>
<li>Call ID3D11DeviceContext::End and pass the DISJOINT query</li>
</ul>
<p>After waiting a sufficient amount of time  for the queries to be ready:</p>
<ul>
<li>Call ID3D11DeviceContext::GetData on all 3 queries</li>
<li>Compute the delta in ticks using the timestamps from both TIMESTAMP queries</li>
<li>Use the frequency from the DISJOINT query to convert the delta to a time in seconds</li>
</ul>
<p>Like any query, you need to wait for the GPU to actually execute all of the commands you submitted for the data to be ready. In my sample app, I handle this by keeping an array of queries for each profile block and moving to the next one each frame. Then at the end of the frame, I get the data from the oldest query and use that for outputting the timing data to the screen. So the actual timing data lags behind by a few frames, but that&rsquo;s okay for real-time profiling. For automated benchmarks or performance snapshots you could either use the data from N frames later, or you could just stall at the end of the frame and wait for the query to be ready.</p>
<p>Sample code and binaries are available on GitHub: <a href="https://github.com/TheRealMJP/DX11Samples/releases/tag/v1.2">https://github.com/TheRealMJP/DX11Samples/releases/tag/v1.2</a></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://gravatar.com/djmips" title="david.c.galloway@gmail.com">djmips</a> - <time datetime="2012-02-08 13:47:39">Feb 3, 2012</time></p>
<p>What I have been doing is one disjoint query per frame (start and end) and many matched timestamp tokens in the frame. It seems to work fine but sometimes I do get weird values for the disjoint frequency on some older nVidia cards. Is there anything wrong with my approach?</p>
<hr />
####
[RebelMoogle](http://www.yojimbo.de "chaos.yoji@gmail.com") - <time datetime="2011-12-12 12:42:49">Dec 1, 2011</time>
<p>There&rsquo;s a little snag I hit: I changed the ID3D11Queryptr in the ProfileData struct in the Profiler class to ID3D11Query*, which resulted in not fully initialized arrays, thus containing garbage and breaking the program. If like me, anyone else is to lazy to write a few typedefs, here&rsquo;s what I did: I have changed the struct constructer to the following: struct ProfileData { ID3D11Query* DisjointQuery[QueryLatency]; ID3D11Query* TimestampStartQuery[QueryLatency]; ID3D11Query* TimestampEndQuery[QueryLatency]; BOOL QueryStarted; BOOL QueryFinished; ProfileData() : QueryStarted(FALSE), QueryFinished(FALSE) { ZeroMemory(DisjointQuery, sizeof(ID3D11Query) * QueryLatency); ZeroMemory(TimestampStartQuery, sizeof(ID3D11Query) * QueryLatency); ZeroMemory(TimestampEndQuery, sizeof(ID3D11Query) * QueryLatency); } }; Other than that it works beautifully. ( I changed the code so that you can just drop it into any program without needing the SampleFramework11, if anyone wants it. :) )</p>
<hr />
####
[3dcgi](http://3dcgi.com "tmartin@ieee.org") - <time datetime="2012-04-01 17:29:34">Apr 0, 2012</time>
<p>I don&rsquo;t know about Nvidia, but AMD has a perf API that can be integrated into your engine so you can get PerfStudio like counter data without using PerfStudio. <a href="http://developer.amd.com/tools/GPUPerfAPI/Pages/default.aspx">http://developer.amd.com/tools/GPUPerfAPI/Pages/default.aspx</a></p>
<hr />
####
[Ben]( "forltiko@gmail.com") - <time datetime="2011-10-13 02:34:21">Oct 4, 2011</time>
<p>You need a new test scene :)</p>
<hr />
####
[]( "") - <time datetime="2011-10-16 22:45:25">Oct 0, 2011</time>
<p>You can also do this with D3D9 btw. NVIDIA support timestap queries since GeForce4 I think. ATI since 2xxx IIRC.</p>
<hr />
####
[kore3d]( "kore3d@gmail.com") - <time datetime="2013-05-25 03:51:36">May 6, 2013</time>
<p>Timestamp and Disjoint doesn&rsquo;t work with lower feature levels (i.e. 9.3) at the D3D11. Lower levels support events and occlussions only.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Average luminance calculation using a compute shader</title>
			<link>https://therealmjp.github.io/posts/average-luminance-compute-shader/</link>
			<pubDate>Wed, 10 Aug 2011 09:31:03 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/average-luminance-compute-shader/</guid>
			<description>A common part of most HDR rendering pipelines is some form of average luminance calculation. Typically it&amp;rsquo;s used to implement Reinhard&amp;rsquo;s method of image calibration, which is to map the geometric mean of luminance (log average) to some &amp;ldquo;key value&amp;rdquo;. This, combined with some time-based adaptation, allows for a reasonable approximation of auto-exposure or human eye adaptation.
In the old days of DX9, the average luminance calculation was usually done repeatedly downscaling a luminance texture as if generating mipmaps.</description>
			<content:encoded><![CDATA[<p>A common part of most HDR rendering pipelines is some form of average luminance calculation. Typically it&rsquo;s used to implement <a href="http://www.cs.ucf.edu/%7Ereinhard/cdrom/tonemap.pdf">Reinhard&rsquo;s method of image calibration</a>, which is to map the geometric mean of luminance (log average) to some &ldquo;key value&rdquo;. This, combined with some time-based adaptation, allows for a reasonable approximation of auto-exposure or human eye adaptation.</p>
<p>In the old days of DX9, the average luminance calculation was usually done repeatedly downscaling a luminance texture as if generating mipmaps. Technically DX9 had support for automatic mipmap generation on the GPU, but support wasn&rsquo;t guaranteed so to be safe you had to do it yourself. DX10 brought guaranteed support for mipmap generation of a variety of texture formats, making it viable for use with average luminance calculations. It&rsquo;s obviously still there in DX11, and it&rsquo;s still a very easy and pretty quick way to do it. On my AMD 6950 it only takes about 0.2ms to generate the full mip chain for a 1024x1024 luminance map, which is pretty quick. But with DX11 it&rsquo;s not sexy unless you&rsquo;re doing it with compute shaders, which means we need to ditch that one line call to GenerateMips and replace it with some parallel reductions. Technically a parallel reduction should have much fewer memory reads/writes compared to generating successive mip levels, so there&rsquo;s also some actual sound reasoning behind exploring that approach.</p>
<p>The DirectX SDK actually comes with a sample that implements average luminance calculation with a compute shader parallel reduction (<a href="http://msdn.microsoft.com/en-us/library/ee416569%28v=vs.85%29.aspx">HDRToneMappingCS11</a>), but unfortunately their CS implementation actually performs a fair bit worse than their pixel shader implementation. A few people on gamedev.net had asked about this and I had said that it should definitely be possible to beat successive downscaling with a compute shader if you did it right, and used cs_5_0 rather than cs_4_0 like the sample. When it came up again today I decided to put my money where my mouth is and make a working example.</p>
<p>The implementation is really simple: render the scene in HDR, render log(luminance) to a 1024x1024 texture, downscale to 1x1 using either GenerateMips or a compute shader reduction, apply adaption, then tone map (and add bloom). My first try was to do the reduction in 32x32 thread groups (giving the max of 1024 per thread group), where each thread sampled a single float from the input texture and stored in shared memory. Then the reduction is done in shared memory using the techniques outlined in <a href="http://developer.download.nvidia.com/compute/cuda/1_1/Website/projects/reduction/doc/reduction.pdf">Nvidia&rsquo;s CUDA parallel reduction whitepaper</a>, which helps avoid shared memory conflicts. The first pass used a 32x32 dispatch which resulted in a 32x32 output texture, which was then reduced to 1x1 with one more 1x1 dispatch. Unfortunately this approach took about 0.3ms to complete, which was slower than the 0.2ms taken for generating mips.</p>
<p>For my second try, I decided to explicitly vectorize so that I could take better advantage of the vector ALU&rsquo;s in my AMD GPU. I reconfigured the reduction compute shader to use 16x16 thread groups, and had each thread group take 4 samples (forming a 2x2 grid) from the input texture and store it in a float4 in shared memory. Then the float4&rsquo;s were summed in a parallel reduction, with the last step being to sum the 4 components of the final result. This approach required only 0.08ms for the reduction, meaning I hit my goal of beating out the mipmap generation. After all that work saving 0.1ms doesn&rsquo;t seem like a whole lot, but it&rsquo;s worth it for the cool factor. The performance differential may also become more pronounced at higher resolution, or on hardware with less bandwidth available. I&rsquo;m not sure how the compute shader will fare on Nvidia hardware since they don&rsquo;t use vectorized GPU, so it should be interesting to get some numbers. I&rsquo;d suspect that shared memory access patterns are going to dominate anyway over ALU cost anyway, so it could go either way.</p>
<p>The sample project is up for download here: <a href="https://github.com/TheRealMJP/DX11Samples/releases/tag/v1.3">https://github.com/TheRealMJP/DX11Samples/releases/tag/v1.3</a></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://twitter.com/ElMarcel" title="ElMarcel@twitter.example.com">Daan Nijs (@ElMarcel)</a> - <time datetime="2011-08-10 15:47:16">Aug 3, 2011</time></p>
<p>OK, I get it now,, thanks for the clarification!. That&rsquo;s actual pretty clever, I guess it makes very bright pixels have less of an impact on the average, as well as better temporal stability.</p>
<hr />
####
[Daan Nijs (@ElMarcel)](http://twitter.com/ElMarcel "ElMarcel@twitter.example.com") - <time datetime="2011-08-10 06:54:33">Aug 3, 2011</time>
<p>this might be nitpicking, but you&rsquo;re saying you&rsquo;re rendering log(luminance). Wouldn&rsquo;t GenerateMips give you an incorrect result, as you can&rsquo;t simply lerp between log values?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-08-10 09:15:34">Aug 3, 2011</time>
<p>Mykhailo: indeed it might! But as I said earlier I suspect that shared memory access is the primary bottleneck, so I wouldn&rsquo;t expect too much of a gain. Daan: the formula for geometric mean is exp(avg(log(luminance))). So if you render out log(luminance) and generate mips, the last mip level is equal to avg(log(luminance)). Then you just take the exp() of the value you get when sampling the lowest mip level and you have the geometric mean.</p>
<hr />
####
[Tiago Costa]( "tiago.costav@gmail.com") - <time datetime="2011-08-10 05:27:12">Aug 3, 2011</time>
<p>Nice post. Every 0.1ms count =D Specially when using VSync.</p>
<hr />
####
[Mykhailo Parfeniuk (@sopyer)](http://twitter.com/sopyer "sopyer@twitter.example.com") - <time datetime="2011-08-10 05:25:26">Aug 3, 2011</time>
<p>Just quick thought: would not gather4 instruction improve performance of your code?</p>
<hr />
####
[Tiago Costa]( "tiago.costav@gmail.com") - <time datetime="2011-08-10 08:39:47">Aug 3, 2011</time>
<p>@Daan I use GenerateMips to calculate average luminance in DX 10 and it works correctly&hellip;</p>
<hr />
####
[]( "") - <time datetime="2011-08-14 11:00:35">Aug 0, 2011</time>
<p>I ran this on my personal computer, and got about 312FPS (At the most) (single GTX 460, i7 920 @ 4.1GHz, 3GB of RAM) Not bad but, it stays around 4.2ms. Sadly it doesn&rsquo;t change much between the methods, infact it goes up with your implementation, about 0.2ms</p>
<hr />
####
[Peter Kristof]( "peter.kristof@gmail.com") - <time datetime="2013-01-06 17:45:01">Jan 0, 2013</time>
<p>Actually, I take it back. You do need the GroupMemoryBarrierWithGroupSync() to avoid potential R/W hazards among different warps.</p>
<hr />
####
[Implementing a Physically Based Camera: Automatic Exposure | Placeholder Art](http://placeholderart.wordpress.com/2014/12/15/implementing-a-physically-based-camera-automatic-exposure/ "") - <time datetime="2014-12-15 05:35:34">Dec 1, 2014</time>
<p>[…] exponential feedback loop to smooth the results of scene metering. I first saw this used in the Average luminance calculation using a compute shader1 post by MJP, which was originally from a SIGGRAPH paper about visual adaption […]</p>
<hr />
####
[Anoop Thomas](http://anooprthomas.blogspot.com "anoop.r.thomas@gmail.com") - <time datetime="2013-09-04 17:49:14">Sep 3, 2013</time>
<p>I recently implemented the both pixel shader and compute shader versions for calculating luminance. For the pixel shader version, I use the downscale 2*2 method with a single bilinear tap all the way down to a single pixel (GenerateMipMaps might be doing the same process). For the compute shader, i use the parallel sum method with 2 passes and then took the average using the resolution. If i use a resolution that is not the power of 2, I end up with an incorrect average (when compared to the compute shader). I have verified that the compute shader version returns the right average, and the pixel shader version starts to introduce errors when downscaling from an odd height or odd width ( for eg, downscaling from 5*3, 5*2, or 4*3). Can you suggest a method to get the right average from the Pixel shader version?</p>
<hr />
####
[auto stereo](http://www.crashcarreview.org ".t.homaslim63@gmail.com") - <time datetime="2012-03-23 01:00:30">Mar 5, 2012</time>
<p>Pretty! This has been a really wonderful article. Many thanks for supplying this info.</p>
<hr />
####
[Peter Kristof]( "peter.kristof@gmail.com") - <time datetime="2013-01-05 16:02:33">Jan 6, 2013</time>
<p>You can use GroupMemoryBarrier() inside the parallel reduction&rsquo;s for loop in ReductionCS(). I see 2-35% speedups on different AMD architectures with that for slightly different implementation with no explicit vectorization. Thanks for the articles and samples, much appreciated!</p>
<hr />
####
[CryZe]( "cryze92@gmail.com") - <time datetime="2012-09-12 01:48:31">Sep 3, 2012</time>
<p>Wouldn&rsquo;t the float4&rsquo;s cause bank conflicts on NVidia hardware? Each subsequent 32-bit is stored in the next memory bank. So the 4 floats of a float4 would be stored on 4 different banks. So when the hardware wants to load all gsm[i].x values, only 4 of them can directly be accessed by the half warp. So it would cause a 4-way bank conflict. I don&rsquo;t think the hardware coalesces the memory automatically.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-09-12 11:31:04">Sep 3, 2012</time>
<p>Yes it would definitely have bank conflicts on both Nvidia and AMD hardware. The first (scalar) version of the reduction that I wrote avoided bank conflicts, since it used the techniques illustrated the CUDA parallel reduction whitepaper. But then it appeared that on my AMD hardware the benefit of vectorization outweighed the cost of bank conflicts, so I just stopped there. In hindsight it probably would have been best to leave in the scalar version as an option, since I would suspect that would perform better on Nvidia hardware and the new GCN-based AMD hardware.</p>
<hr />
####
[腕時計 定番](http://www.watchsrealize.com/ "blsdrge@gmail.com") - <time datetime="2013-11-01 00:33:43">Nov 5, 2013</time>
<p>アルマーニ 楽天</p>
<hr />
####
[Automatic Exposure | Krzysztof Narkowicz](https://knarkowicz.wordpress.com/2016/01/09/automatic-exposure/ "") - <time datetime="2016-01-09 07:53:56">Jan 6, 2016</time>
<p>[…] of the screen [Hen14]. Additionally we could also use a compute shader for computing averages [Pet11]. This is usually simpler and more efficient than repeated texture […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>I am officially a published author</title>
			<link>https://therealmjp.github.io/posts/i-am-officially-a-published-author/</link>
			<pubDate>Fri, 05 Aug 2011 06:04:11 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/i-am-officially-a-published-author/</guid>
			<description>I recently collaborated with fellow DX MVP&amp;rsquo;s Jason Zink and Jack Hoxley to write a D3D11-focused book entitled Practical Rendering and Computation with Direct3D 11, which just came up for sale on Amazon today. I wrote the HLSL and Deferred Rendering chapters in particular. All of the code samples are up on the Hieroglyph 3 CodePlex site, if you want to get an idea of the content. Or you can just take my word for it that it&amp;rsquo;s awesome.</description>
			<content:encoded><![CDATA[<p>I recently collaborated with fellow DX MVP&rsquo;s Jason Zink and Jack Hoxley to write a D3D11-focused book entitled <a href="http://www.amazon.com/Practical-Rendering-Computation-Direct3D-11/dp/1568817207/ref=sr_1_1?ie=UTF8&amp;qid=1312513966&amp;sr=8-1">Practical Rendering and Computation with Direct3D 11</a>, which just came up for sale on Amazon today. I wrote the HLSL and Deferred Rendering chapters in particular. All of the code samples are up on the <a href="http://hieroglyph3.codeplex.com/">Hieroglyph 3 CodePlex site</a>, if you want to get an idea of the content. Or you can just take my word for it that it&rsquo;s awesome. :P</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22%22">Jason Zink</a> - <time datetime="2011-08-07 21:15:01">Aug 0, 2011</time></p>
<p>Hi Tiago, I wrote the chapter on water simulation and can give you a little bit of background on it. It is based on a paper written by James F. O&rsquo;Brien and Jessica K. Hodgins titled &ldquo;Dynamic Simulation of Splashing Fluids&rdquo;. I actually implemented this technique before DX11 hardware was available, but it makes efficient use of the compute shader functionality. The sample program implements a 16x16 grid of tiles of 16x16 groups of water tiles, and still runs at good rates. On my laptop, which has a 8600M GT, it runs at approximately 80 fps. Since this is a DX10 level card, that&rsquo;s not too bad. On my main dev machine which has a 5700 AMD card, it runs at ~650 fps. Although you don&rsquo;t need to take my word for it - you can download the code and check it out for yourself! That was a big part of the reason that we chose to use an open source project for our samples - so that people could see what they are getting, and also to keep updating the code base after the book was finished up.</p>
<hr />
####
Tiago Costa - <time datetime="2011-08-08 07:53:13">Aug 1, 2011</time>
<p>Hi JasonZ! Sadly I won&rsquo;t have access to my dev machine until next week, so I can&rsquo;t try the demos&hellip; Anyway I already ordered the book =D</p>
<hr />
####
Jason Zink - <time datetime="2011-08-08 08:21:21">Aug 1, 2011</time>
<p>That&rsquo;s great - I look forward to hearing any feedback (good or bad) on the book, and of course also on the Hieroglyph codebase and samples!</p>
<hr />
####
[smasherprog](http://nolimitsdesigns.com "smasherprog@gmail.com") - <time datetime="2011-08-08 05:09:36">Aug 1, 2011</time>
<p>Where is this code available at to download? I would like to take a look at the examples.</p>
<hr />
####
[smasherprog](http://nolimitsdesigns.com "smasherprog@gmail.com") - <time datetime="2011-08-08 05:10:31">Aug 1, 2011</time>
<p>Opps, just woke up and realized it is on the Hieroglyph 3 CodePlex site. Never mind.</p>
<hr />
####
[]( "") - <time datetime="2011-08-04 22:37:52">Aug 4, 2011</time>
<p>congrats, Mr. Big Author man! :) accomplishing something like that must feel really rewarding. gonna give it a look at the source code first :p then i&rsquo;ll take your word for it. perhaps. probably. always liked your samples/code.</p>
<hr />
####
[]( "") - <time datetime="2011-08-05 02:13:30">Aug 5, 2011</time>
<p>Cheers! Congrats and neat to hear Jolly Jack is still at it :)</p>
<hr />
####
[Daniel](http://twitter.com/_Eclectus_ "_Eclectus_@twitter.example.com") - <time datetime="2011-08-05 05:09:30">Aug 5, 2011</time>
<p>Congratulations MJP - Finishing something like this is to be admired. I continue to follow your work with interest!</p>
<hr />
####
[Tiago Costa]( "") - <time datetime="2011-08-07 13:26:14">Aug 0, 2011</time>
<p>Congrats! I would like to know if the articles about terrain and water simulation use techniques that provide good performance/quality? Or are those articles focused on teaching basic terrain/water rendering?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-08-07 16:30:28">Aug 0, 2011</time>
<p>Hi Tiago, I&rsquo;m going to ask the authors of those sections so that I can get an accurate answer for you.</p>
<hr />
####
[TiagoVCosta](http://tiagovcosta.wordpress.com "tiago.costav@gmail.com") - <time datetime="2011-08-23 04:03:59">Aug 2, 2011</time>
<p>Just received my copy :D Check out this hands-on photo: <a href="http://img829.imageshack.us/img829/728/imag0200o.jpg">http://img829.imageshack.us/img829/728/imag0200o.jpg</a></p>
<hr />
####
[]( "") - <time datetime="2011-08-24 03:07:43">Aug 3, 2011</time>
<p>You do realize that the spine of the book reads: &ldquo;Practical Rendering &amp; Computation with Direct 3D 1&rdquo;. Yes that&rsquo;s right &ldquo;Direct3D 1&rdquo;. Looks like one the digits in 11 has been cropped :)</p>
<hr />
####
[TiagoVCosta](http://tiagovcosta.wordpress.com "tiago.costav@gmail.com") - <time datetime="2011-08-24 03:35:44">Aug 3, 2011</time>
<p>LOL, I didn&rsquo;t noticed that before :D</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Anamorphic lens flares: the lens flare of the 2010&#39;s?</title>
			<link>https://therealmjp.github.io/posts/lens-flares/</link>
			<pubDate>Fri, 10 Jun 2011 06:57:41 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/lens-flares/</guid>
			<description>Since the dawn of time, the greatest struggle of the graphics programmer is to ensure that bright stuff looks really damn bright. We&amp;rsquo;re stuck with displays that have a limited displayable range, which means it&amp;rsquo;s fallen upon us to come up with new hacks and tricks to make sure the player at least feels like he&amp;rsquo;s blinded by the sun, even if we can&amp;rsquo;t really cause physical damage to their eyes (if only!</description>
			<content:encoded><![CDATA[<p>Since the dawn of time, the greatest struggle of the graphics programmer is to ensure that bright stuff looks <em>really damn bright</em>. We&rsquo;re stuck with displays that have a limited displayable range, which means it&rsquo;s fallen upon us to come up with new hacks and tricks to make sure the player at least <em>feels</em> like he&rsquo;s blinded by the sun, even if we can&rsquo;t really cause physical damage to their eyes (if only!).</p>
<p>In the early 2000&rsquo;s, all we had to work with was the lens flare. It was cheesy, and it was everywhere. You probably still remember being bombarded with giant flare sprites whenever you looked up at the sun cresting over the mountains. If not, this should jog your memory:</p>
<p><a href="/images/converted/lens-flares/lensflare.jpg"><img src="/images/converted/lens-flares/lensflare.jpg" alt="" title="lensflare"></a></p>
<p>A few years later, Tron 2.0 came out and brought with it some neat glow effects. Now all that glowly Tron stuffed looked like it was glowing! But pretty soon we got crafty and realized that we didn&rsquo;t just have to blur a glow value stored in the alpha channel, but we could do it to <em>everything</em>. And thus, bloom was born. With bloom not only could we make bright stuff look bright, but we could give the same treatment to the mid tones! And like the lens flare, it turned up everywhere. It got so bad, GameSpot gave it an award for being the most annoyingly overused effect. It wasn&rsquo;t really unwarranted since games looked like this in 2004:
<a href="/images/converted/lens-flares/fable.jpg"><img src="/images/converted/lens-flares/fable.jpg" alt="" title="fable"></a></p>
<p>Fast forward to 2007, and Crysis bestows upon us the glory of god rays. It was at this point that we realized that merely blinding people with bloom was not enough&hellip;we needed to combine it with beams of light shining directly into their brains. Because every game should look like you&rsquo;re walking through a thick, soupy haze!</p>
<p><a href="/images/converted/lens-flares/2011-05-30_00001.jpg"><img src="/images/converted/lens-flares/2011-05-30_00001.jpg" alt="" title="2011-05-30_00001"></a></p>
<p>Now in 2011, lens flares are making a startling comeback! I went to E3 on Tuesday, and the obvious effect du jour was anamorphic lens flares. Anamorphic lens flares are great, because they take a tiny little bright spot and stretch it across the whole damn screen. I mean it&rsquo;s go big or go home, right? Now the player can&rsquo;t possibly miss that tiny spot light in the corner, since we&rsquo;re going to cover their whole damn screen with a giant-ass blue flare. High five!</p>
<p><a href="/images/converted/lens-flares/masseffect2.jpg"><img src="/images/converted/lens-flares/masseffect2.jpg" alt="" title="masseffect2"></a></p>
<h2 id="on-the-subject-of-e3-i-decided-that-im-not-above-pimping-our-gamehttpwwwyoutubecomwatchvswwdxuipfys-on-my-blog-rest-assured-there-is-plenty-of-bloom-p">On the subject of E3, I decided that I&rsquo;m not above pimping <a href="http://www.youtube.com/watch?v=SwwdXuIpfys">our game</a> on my blog. Rest assured, there is plenty of bloom. :P</h2>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22bigmrpoo@yahoo.com%22">Hodgman</a> - <time datetime="2011-06-25 19:28:08">Jun 6, 2011</time></p>
<p>oh, and I can&rsquo;t wait for &ldquo;Physically-Based Real-Time Lens Flare Rendering&rdquo; (SIGGRAPH 2011 Paper) to be implemented in a game!</p>
<hr />
####
[David Neubelt]( "dneubelt@gmail.com") - <time datetime="2011-06-09 23:40:10">Jun 4, 2011</time>
<p>Sweet game!</p>
<hr />
####
[default_ex]( "default_ex@live.com") - <time datetime="2011-06-20 04:12:43">Jun 1, 2011</time>
<p>Can&rsquo;t believe you left out Fable 3, it&rsquo;s the most blinding game EVER! It really should get the &ldquo;most horrible combination of blinding effects of the year&rdquo; award.</p>
<hr />
####
[Hodgman]( "bigmrpoo@yahoo.com") - <time datetime="2011-06-25 19:21:01">Jun 6, 2011</time>
<p>Nice post. I&rsquo;m actually betting on the early 2000&rsquo;s style flares making a comeback soon. With non-2000 hardware, we can do occlusion/coverage tests on the GPU, and read the results in the flare&rsquo;s vertex shader to change opacity/scale factors. We can also use techniques like in your bokeh demo to find light-sources in the image and automatically add a chain of flare sprites at appropriate locations. All done in HDR, with bloom, and anamorphic stretching! ;-)</p>
<hr />
####
[Tiago Costa]( "") - <time datetime="2011-06-10 08:59:32">Jun 5, 2011</time>
<p>What about Bokeh? Now every game has Bokeh :D</p>
<hr />
####
[d@a.com](http://youtube.com/izvarzone "") - <time datetime="2011-09-30 05:56:46">Sep 5, 2011</time>
<p>I hate blurry graphics, some devs still use this technique, it actually makes game look worse, not better.</p>
<hr />
####
[racarate](http://gravatar.com/racarate "ralabate@gmail.com") - <time datetime="2014-06-13 12:36:16">Jun 5, 2014</time>
<p>hilarious, i think this all goes back to the BELIEVE video by cher&hellip;</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Bokeh II: The Sequel</title>
			<link>https://therealmjp.github.io/posts/bokeh-ii-the-sequel/</link>
			<pubDate>Wed, 20 Apr 2011 06:59:20 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/bokeh-ii-the-sequel/</guid>
			<description>After I finished the bokeh sample, there were a few remaining issues that I wanted to tackle before I was ready to call it &amp;ldquo;totally awesome&amp;rdquo; and move on with my life.
Good blur - in the last sample I used either a 2-pass blur on a poisson disc performed at full resolution, or a bilateral Gaussian blur performed at 1/4 resolution (both done in a pixel shader). The former is nice because it gives you variable filter width per-pixel, but you get some ugly noise-like artifacts due to insufficient sampling.</description>
			<content:encoded><![CDATA[<p>After I finished the <a href="http://mynameismjp.wordpress.com/2011/02/28/bokeh/">bokeh sample</a>, there were a few remaining issues that I wanted to tackle before I was ready to call it &ldquo;totally awesome&rdquo; and move on with my life.</p>
<p><strong>Good blur</strong> - in the last sample I used either a 2-pass blur on a poisson disc performed at full resolution, or a bilateral Gaussian blur performed at 1/4 resolution (both done in a pixel shader). The former is nice because it gives you variable filter width per-pixel, but you get some ugly noise-like artifacts due to insufficient sampling. Performance can really take a nose dive with too many samples, especially if your filter radius is very large. Doing two passes helps a lot, but gives you artifacts like the one pictured below:</p>
<p><a href="/images/converted/bokeh-ii-the-sequel/discartifacts.png"><img src="/images/converted/bokeh-ii-the-sequel/discartifacts.png" alt="" title="DiscArtifacts"></a></p>
<p>The traditional &ldquo;gaussian blur at 1/4 res&rdquo; approach sucks even worse. This is because the lower resolution screws up the bilateral filtering, and performance also isn&rsquo;t so great in a pixel shader due to the high amount of texture samples required. But worst of all, it just plain doesn&rsquo;t look good due to using a lerp to blend between blurred and non-blurred pixels to simulate &ldquo;in-focus&rdquo; and &ldquo;out-of-focus&rdquo;. It ends up looking more like soft focus, rather than an image that&rsquo;s gradually coming in or out of focus. On top of that you get aliasing artifacts from working at a lower res, which causes shimmering and swimming.</p>
<p>My solution to this problem was to implement monstrous 21-tap seperable bilateral blur in a compute shader. Wide, seperable blur kernels are a pretty nice fit for compute shaders because you can store the texture samples in shared memory, which allows each thread to take a single texture sample rather than N. Shared memory isn&rsquo;t <em>that</em> quick, but for larger kernels (10 pixels or so) the savings start to win out over a pixel shader implementation. With such a wide blur kernel, I could perform the blurring at full resolution in order to provide nice sharp edges when the foreground is in-focus and the background is out-of-focus. Here&rsquo;s a similar image to the one above, this time artifact-free:</p>
<p><a href="/images/converted/bokeh-ii-the-sequel/csblur.png"><img src="/images/converted/bokeh-ii-the-sequel/csblur.png" alt="" title="CSBlur"></a></p>
<p>I ended up just using a box filter rather than a Gaussian, which normally gives you ugly box-shaped highlights on the bright spots. Fortunately the bokeh does a great job of extracting those points and replacing them with the bokeh shape. To avoid having to lerp between blurred and un-blurred versions of the image, I had the filter kernel reject samples outside of the CoC size of the pixel being operated on. This effectively gives you a variable-sized blur kernel per-pixel, which gives you much nicer transitions for objects moving in or out of focus. It doesn&rsquo;t look quite as good as the transitions for the disc-based blur, but I think it&rsquo;s a fair trade off. The image below shows what the kernel looks like as it transitions from in-focus to out-of-focus:</p>
<p><a href="/images/converted/bokeh-ii-the-sequel/csblurtransition.png"><img src="/images/converted/bokeh-ii-the-sequel/csblurtransition.png" alt="" title="CSBlurTransition"></a></p>
<p>**
Out-of-focus foreground objects** - As in most DOF implementations, foreground objects that were out-of-focus were blurred, but still had hard edges. This can look pretty bad, as the object blurring into the background is a key part of simulating the look. A simple way to rectify this issue is to store your CoC size or blurriness factor in a texture, then blur it in screen space. This is essentially the approach <a href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch28.html">used by Infinity Ward</a> for the past few Call of Duty games produced by them. I went with something similar, except like with the DOF blur I used a compute shader to do a 21-tap seperable blur of the CoC texture. To make this look good you have to make sure you only gather samples coming from a lower depth, and that have a larger CoC than the pixel being operated on by that thread. Here&rsquo;s an out-of-focus foreground object with and without the CoC spreading:</p>
<p>CoC Blur Off:</p>
<p><a href="/images/converted/bokeh-ii-the-sequel/cocspreadoff.png"><img src="/images/converted/bokeh-ii-the-sequel/cocspreadoff.png" alt="" title="CoCSpreadOff"></a></p>
<p>CoC Blur On:</p>
<p><a href="/images/converted/bokeh-ii-the-sequel/cocspreadon.png"><img src="/images/converted/bokeh-ii-the-sequel/cocspreadon.png" alt="" title="CoCSpreadOn"></a></p>
<p><strong>No depth occlusion for bokeh sprites</strong> - I didn&rsquo;t do any sort of occlusion testing for bokeh sprites in the sample, and waved it off by saying it would be simple to use the depth stencil buffer and enable depth testing. While that&rsquo;s true, it turns out that doing it that doesn&rsquo;t really give you great results. The problem is that if a bokeh sprite is covering an area that&rsquo;s totally out of focus, you don&rsquo;t want those out-of-focus pixels occluding the bokeh sprites. Otherwise the bokeh just not blend in at all with the pixels that are blurred by the DOF blur pass. So instead I implemented my own depth occlusion function that attenuates based on depth, but removes the attenuation if the pixel is out-of-focus. This let me keep the same overall look, while removing cases where I was getting &ldquo;halos&rdquo; due to the bokeh sprites rendering on top of in-focus objects. And since I was doing it in the shader anyway, I threw in a &ldquo;soft&rdquo; occlusion function rather than a binary comparison.</p>
<p>Without occlusion:</p>
<p><a href="/images/converted/bokeh-ii-the-sequel/occlusiondisabled.png"><img src="/images/converted/bokeh-ii-the-sequel/occlusiondisabled.png" alt="" title="OcclusionDisabled"></a></p>
<p>With occlusion:</p>
<p><a href="/images/converted/bokeh-ii-the-sequel/occlusionenabled.png"><img src="/images/converted/bokeh-ii-the-sequel/occlusionenabled.png" alt="" title="OcclusionEnabled"></a></p>
<p>Code and binaries are available here: <a href="https://github.com/TheRealMJP/DX11Samples/releases/tag/v1.4">https://github.com/TheRealMJP/DX11Samples/releases/tag/v1.4</a></p>
<p><em>4/22/2011 - Fixed a texture sampling bug on Nvidia hardware</em></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22jayben71@gmail.com%22">Michael</a> - <time datetime="2011-04-24 20:43:25">Apr 0, 2011</time></p>
<p>Hi, Just downloaded the update and it is working fine now with both nVidia and ATI . Just a quick question, how hard would it be to incorporate your Motion blur Sample with this? As I think this will make it a complete DX11 based postfx framework!</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-04-22 21:50:06">Apr 5, 2011</time>
<p>@Michael, Thanks for trying it out, and for saying it looks nice! Fortunately I now have a GTX 470 at work, so I can debug Nvidia issues. It turns out that the blockiness was happening because I was sampling the textures at integer locations rather than at 0.5, and I guess on Nvidia sampling off-center like that can cause issues. @Maciej Wow, that&rsquo;s pretty bad! It runs at around 160fps on my 6950, with the bulk of the frame time actually spent rendering the scene + shadow maps.</p>
<hr />
####
[Maciej]( "msawitus@gmail.com") - <time datetime="2011-04-21 02:55:01">Apr 4, 2011</time>
<p>Very nice article &amp; demo! I only wish performance was a bit better (13 fps on ATI 5650).</p>
<hr />
####
[LieblingsAlgorithmen &#8211; Links | Echtzeitgrafiker Magdeburg](http://echtzeitgrafiker.yojimbo.de/?p=47 "") - <time datetime="2011-05-12 07:56:40">May 4, 2011</time>
<p>[&hellip;] Bokeh II – Fake Bokeh – (mit source) [&hellip;]</p>
<hr />
####
[Louis Castricato]( "ljcrobotic@yahoo.com") - <time datetime="2011-04-26 19:52:49">Apr 2, 2011</time>
<p>Can someone convert the sample code into SlimDX? I don&rsquo;t know anything (yet) about C , and I wouldn&rsquo;t wanna miss out on an amazing sample like this!</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-04-20 08:31:20">Apr 3, 2011</time>
<p>Hey Michael, The link works for me, but Skydrive is terrible so I&rsquo;m not surprised it doesn&rsquo;t work. I uploaded the .zip to a new Codeplex project I made, so give that link a try.</p>
<hr />
####
[Michael]( "jayben71@gmail.com") - <time datetime="2011-04-20 04:59:48">Apr 3, 2011</time>
<p>You are the Man! awesome update, cannot wait to see this however the download link seems broke&hellip;</p>
<hr />
####
[Michael]( "jayben71@gmail.com") - <time datetime="2011-04-20 20:08:36">Apr 3, 2011</time>
<p>Hi MJP, Yep Codeplex is fine I managed to download it. I am running nvidia gtx480 and ATI 6870 windows 7. Looks great with ATI, not so much with nVidia. nvidia looks blocky with major edge aliasing like the final render/color buffer is set at a very low res (1/4?) when Computeshader is enabled, the other variants seem fine. Great improvement overall in quality. Would it be possible to add an option to change the size of the taps when using Computeshader blur?</p>
<hr />
####
[Draft on depth of field resources | Light is beautiful](http://lousodrome.net/blog/light/2012/01/17/draft-on-depth-of-field-resources/ "") - <time datetime="2012-03-13 22:59:39">Mar 2, 2012</time>
<p>[&hellip;] I forgot to mention a second article of Matt Pettineo, where he suggests a combination of techniques to achieve a better result. An example of actual bokeh in a photo of [&hellip;]</p>
<hr />
####
[OpenGL Insights &laquo; The Danger Zone](http://mynameismjp.wordpress.com/2012/08/05/opengl-insights/ "") - <time datetime="2012-08-05 20:50:54">Aug 0, 2012</time>
<p>[&hellip;] time ago Charles de Rousiers adapted my Bokeh Depth of Field sample to OpenGL, and we contributed it as a chapter to the recently-released OpenGL Insights. Bokeh is [&hellip;]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Crashes on Nvidia hardware</title>
			<link>https://therealmjp.github.io/posts/crashes-on-nvidia-hardware/</link>
			<pubDate>Sat, 26 Mar 2011 07:23:30 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/crashes-on-nvidia-hardware/</guid>
			<description>A few people have told me that my past two samples (Bokeh and RadiosityDX11) were crashing on Nvidia GPU&amp;rsquo;s, which I verified myself on my coworker&amp;rsquo;s GTX 470. The crash appears to be a driver bug, since it happens deep in the Nvidia runtime DLL on a worker thread and also because it works fine on AMD hardware and the REF device. This morning we managed to narrow it down to the shadow map filtering shader code (shader code can crash drivers apparently, who knew?</description>
			<content:encoded><![CDATA[<p>A few people have told me that my past two samples (Bokeh and RadiosityDX11) were crashing on Nvidia GPU&rsquo;s, which I verified myself on my coworker&rsquo;s GTX 470. The crash appears to be a driver bug, since it happens deep in the Nvidia runtime DLL on a worker thread and also because it works fine on AMD hardware and the REF device. This morning we managed to narrow it down to the shadow map filtering shader code (shader code can crash drivers apparently, who knew?), and I suspect that it&rsquo;s the the fact that shader makes use of a SampleCmp with an integer offset. Commenting out the filtering and replacing it with a single SampleCmp seems to work, but I think using a regular texture coordinate offset might work as well. Anyone want to try putting this into &ldquo;SampleShadowCascade&rdquo; in Mesh.hlsl, and let me know if it works?</p>
<p> </p>
<pre tabindex="0"><code>[unroll(NumSamples)]
for (int y = -Radius; y &lt;= Radius; y++)
{
    [unroll(NumSamples)]
    for (int x = -Radius; x &lt;= Radius; x++)
    {
        float2 offset = float2(x, y) * (1.0f / ShadowMapSize);
        float2 sampleCoord = shadowTexCoord + offset;
        float sample = ShadowMap.SampleCmp(ShadowSampler, sampleCoord, shadowDepth).x;
        ...
    }
}
</code></pre><hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22%22">rake</a> - <time datetime="2011-03-26 03:49:01">Mar 6, 2011</time></p>
<p>Yes, this fixes the crash in the Bokeh demo on my GTX 480. Finally, I can enjoy your demos. Thanks for the effort!</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-03-27 14:57:26">Mar 0, 2011</time>
<p>Thanks rake, really appreciate it! I&rsquo;ll update the old samples with the new shader code.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>How To Fake Bokeh (And Make It Look Pretty Good)</title>
			<link>https://therealmjp.github.io/posts/bokeh/</link>
			<pubDate>Mon, 28 Feb 2011 08:18:35 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/bokeh/</guid>
			<description>Before I bought a decent DSLR camera and started putting it in manual mode, I never really noticed bokeh that much. I always just equated out-of-focus with blur, and that was that. But now that I&amp;rsquo;ve started noticing, I can&amp;rsquo;t stop seeing it everywhere. And now every time I see depth of field effects in a game that doesn&amp;rsquo;t have bokeh, it just looks wrong. A disc blur or even Gaussian blur is fine for approximating the look of out-0f-focus areas that are mostly low-frequency, but the hot spots just don&amp;rsquo;t look right at all (especially if you don&amp;rsquo;t do it in HDR).</description>
			<content:encoded><![CDATA[<p>Before I bought a decent DSLR camera and started putting it in manual mode, I never really noticed bokeh that much. I always just equated out-of-focus with blur, and that was that. But now that I&rsquo;ve started noticing, I can&rsquo;t stop seeing it everywhere. And now every time I see depth of field effects in a game that doesn&rsquo;t have bokeh, it just looks <em>wrong</em>. A disc blur or even Gaussian blur is fine for approximating the look of out-0f-focus areas that are mostly low-frequency, but the hot spots just don&rsquo;t look right at all (<em>especially</em> if you don&rsquo;t do it in HDR).</p>
<p>So what are our options for getting a decent bokeh look in real time? Here&rsquo;s a short (and by no means complete) list:</p>
<p><strong>1. Render the scene multiple times and accumulate samples using the aperture shape</strong> - this obviously a no-go for real-time rendering.</p>
<p><strong>2. Stochastic rasterization, using scatter</strong> - this becoming more feasible now that we can implement scatter in pixel or compute shaders, but requires a lot of samples to not look like crap (or maybe something like <a href="http://research.nvidia.com/publication/local-image-reconstruction-algorithm-stochastic-rendering">this</a>) and would likely have performance problems due to the non-coherent memory writes</p>
<p><strong>3. Do scatter-as-gather as a post process</strong> - this is doable and could be implemented in a compute shader or even a pixel shader, but it&rsquo;s expensive since you need a huge number of samples for large CoC sizes. Plus it can be tricky to implement, since you really need to be careful about energy conservation. Gamedev user FBMachine actually implemented this approach and documented some of the issues <a href="http://www.gamedev.net/topic/563149-real-time-bokeh-high-quality-dof/">here</a>&hellip;as you can see the results can be nice but the performance isn&rsquo;t so great.</p>
<p><strong>4. Render each pixel as point sprite, using the CoC-size + aperture shape</strong> - this approach initially sounds completely unrealistic, since you&rsquo;re talking about huge bandwidth usage from the blending and massive overdraw. However the guys who make 3DMark actually implemented some optimizations to make it usable for their &ldquo;Deep Sea&rdquo; demo in 3DMark11. They go into a little bit of detail in their <a href="http://www.3dmark.com/wp-content/uploads/2010/12/3DMark11_Whitepaper.pdf">white paper</a>, but the basic gist of it is that they extract pixels with a CoC above a given threshold and append the point into an append/consume buffer (basically a stack that your shaders can push onto or pull from), then render the points at point sprites to one of several render targets. The render targets are successively smaller like mip levels, and they render the larger points to smaller render targets. By doing this they help curb the massive overdraw/blending cost of large points. They also do the point extraction several times, each time from a progressively downsampled version of the input image and with a different CoC threshold. I presume they do this to avoid extracting huge amounts of points.</p>
<p>The guys at Capcom also did something similar to this for the DX10 version of Lost Planet, although I&rsquo;m not too familiar with the details. There&rsquo;s some pictures + descriptions <a href="http://beautifulpixels.blogspot.com/2008/03/higher-fidelity-depth-of-field-effects.html">here</a> and <a href="http://meshula.net/wordpress/?p=124">here</a>.</p>
<p><strong>5. Pick out the bright spots, render those as point sprites using the aperture shape, and do everything else with a &ldquo;traditional&rdquo; blur-based DOF technique -</strong> ideally with this approach we get the nice bokeh effects for that parts where it&rsquo;s really noticeable (the highlights), and use something cheap for everything else. Gamedev.net poster Hodgman took a crack at implementing this approach using point sprites and vertex textures, and documented his results in <a href="http://www.gamedev.net/topic/576380-real-time-lens-blur/">this thread</a>. His main problems were due to flickering + instability, since he had to downscale many times in order to render the grid of point sprites.</p>
<p>For my own implementation, I decided to go for #5. I spent a lot of time staring at out-of-focus images, and decided that it really wasn&rsquo;t necessary to do a more accurate bokeh simulation for most of the image. For instance, take a look at this picture:</p>
<p><img src="http://www.blogcdn.com/www.gadling.com/media/2009/02/notbokeh.jpg" alt="">At least 90% of that image doesn&rsquo;t have a distinctive bokeh pattern, and looks very similar to either a box blur or disc blur with a wide radius. It&rsquo;s only those bright spots that really need the full bokeh treatment for it to look convincing.</p>
<p>With that in mind, I came up with the following approach:</p>
<ol>
<li>Render the scene in HDR</li>
<li>Do a full-res depth + blur generation pass where we sample the depth buffer, and write out linear depth + circle of confusion size to an R16G16_FLOAT buffer</li>
<li>Do a full-res bokeh point extraction pass. For each pixel, compute the average brightness of the 5x5 block surrounding the pixel and compare it with the brightness of the current pixel. If the pixel brightness minus the average brightness is above a certain threshold and the CoC size is above a certain threshold, push the pixel position + color + CoC size onto an append buffer and output a value of 0.0 as the pixel color. If it doesn&rsquo;t pass the threshold, output the input color.</li>
<li>Do a regular DOF pass. I implemented two versions: one that does a  full-res disc-based blur in two passes using 16 samples on a Poisson disc with radius == CoC size, and one that does ye olde 1/4-res Gaussian blur (with edge bleeding reduction) and a full-screen lerp between the un-blurred and blurred version.</li>
<li>Copy the embedded count from the append buffer to another buffer, and use the second buffer as an indirect arguments buffer for a DrawInstancedIndirect call. Basically this lets us draw however many points are in the buffer, without having to copy anything back to the CPU. The vertex shader for each point then samples the position/color/CoC size from the append buffer and passes it to the geometry shader, which expands the point into a quad with size equal to the CoC size. The pixel shader then samples from a texture containing the aperture shape, and additively blends the result into an empty render target. The render target can be either full res, or 1/4 res to save on bandwidth.</li>
<li>Combine the results of the bokeh pass with the  results of the DOF pass by summing them together in a pixel shader.</li>
<li>Pass the result to bloom + tone mapping, and output the image.</li>
</ol>
<p>I actually implemented everything with pixel shaders, since I find they&rsquo;re still quicker for rapid prototyping compared to compute shaders. The bokeh generation step and Guassian blurs probably would have benefited from using shared memory to avoid redundant texture samples, but not so much that penalty is huge. The disc-based blur isn&rsquo;t all that great of a fit either, since I used a very large sampling radius (usually at least 16 pixels).  For the disc blur I also did it in two passes with 16 samples each, in order to avoid some of the nasty banding artifacts that come from using a large sampling radius. This leads to some artifacts around edges, but it&rsquo;s too bad. Either way the DOF part isn&rsquo;t really important, and you could swap it out with whatever cool new technique you want. I also didn&rsquo;t end up using proper lens-based CoC-size calculations, since I found it was a pain in the ass to work with. So I reverted to a very simple linear interpolation  between &ldquo;out-of-focus&rdquo; and &ldquo;in-focus&rdquo; distances, and then multiplied the value by a tweakable maximum CoC size.</p>
<p>As for the bokeh itself, it looks pretty good since it&rsquo;s using a texture and can have whatever shape you want. It&rsquo;s also pretty stable since the extraction is done at full resolution, and so you don&rsquo;t get much flickering or jumping around. I didn&rsquo;t use depth testing when rendering the bokeh sprites&hellip;I had intended on doing it, but then decided it wasn&rsquo;t really necessary. However I&rsquo;d imagine it would probably be desirable if you wanted to render really large bokeh spoints, in which case it would be trivial to implement.</p>
<p>Now for some results. This is with the foreground in focus, and the background out of focus:</p>
<p><a href="/images/converted/bokeh/initial.png"><img src="/images/converted/bokeh/initial.png" alt="" title="Initial"></a></p>
<p>The bokeh isn&rsquo;t too distinctive here since most of the image is in focus, but you can definitely see the hexagon pattern on some of the background geometry.</p>
<p>This one has the whole scene out of focus, and so you can see a lot more of the bokeh effect:</p>
<p><a href="/images/converted/bokeh/hexagon.png"><img src="/images/converted/bokeh/hexagon.png" alt="" title="Hexagon"></a></p>
<p>Now you can really see the bokeh! Here&rsquo;s another with a circle-shaped bokeh:</p>
<p><a href="/images/converted/bokeh/octagon.png"><img src="/images/converted/bokeh/octagon.png" alt="" title="Circle"></a></p>
<p>This one is with the bokeh sprites rendered to a 1/4-resolution texure:</p>
<p><a href="/images/converted/bokeh/downscale.png"><img src="/images/converted/bokeh/downscale.png" alt="" title="Downscale"></a>
Finally, this one is with the brightness threshold set to 0. Basically this means they every out of focus pixel gets drawn as a point sprite, which I used as a sort of &ldquo;reference&rdquo; image.</p>
<p><a href="/images/converted/bokeh/reference.png"><img src="/images/converted/bokeh/reference.png" alt="" title="Reference"></a></p>
<p>I think the earlier shots hold up pretty well in comparison! The biggest issue that I notice is that it can look a bit weird if you DOF blur radius and your bokeh radius don&rsquo;t match up. It starts to become pretty obvious if you crank up the maximum bokeh size, but still use a small radius for blurring everything else. This is because you don&rsquo;t want to be able to clearly discern what&rsquo;s &ldquo;underneath&rdquo; the bokeh sprites&hellip;you want it to pretty much look like a solid color. To help with this I added a parameter to tweak the falloff used for conserving energy as as the bokeh point sprites get larger. Basically it does a pow on the falloff, which is computed by calculating the ratio of area of a circle with radius == CoC and comparing it with the radius of a single pixel. So by setting the falloff tweak to a lower value, the points are artificially brightened and appear more opaque.</p>
<p>If you want to check it out yourself, you can download the source code + binaries here: <a href="https://mynameismjp.files.wordpress.com/2011/02/bokeh3.zip">https://mynameismjp.files.wordpress.com/2011/02/bokeh3.zip</a></p>
<p><em>Updated (3/27/2011): Changed the shadow filtering shader code so that it doesn&rsquo;t cause crashes on Nvidia hardware</em></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://ozlael.egloos.com" title="">ozlael</a> - <time datetime="2011-03-01 06:51:03">Mar 2, 2011</time></p>
<p>Always thank you for useful article.</p>
<hr />
####
[Hodgman]( "bigmrpoo@yahoo.com") - <time datetime="2011-03-19 01:12:28">Mar 6, 2011</time>
<p>Glad to see my post has helped someone! Thanks for sharing your implementation of the effect too :D</p>
<hr />
####
[Louis Castricato]( "ljcrobotic@yahoo.com") - <time datetime="2011-04-26 19:54:30">Apr 2, 2011</time>
<p>SlimDX? :P</p>
<hr />
####
[Michael]( "jayben71@gmail.com") - <time datetime="2011-03-14 05:39:03">Mar 1, 2011</time>
<p>This is great and a very interesting technique, other methods do not have much over this visually (for games) . Also, the app is crashing in windows 7 32-bit 480GTS and works fine with ATI HD5830</p>
<hr />
####
[Bokeh II: The Sequel &laquo; The Danger Zone](http://mynameismjp.wordpress.com/2011/04/19/bokeh-ii-the-sequel/ "") - <time datetime="2011-04-19 22:59:42">Apr 2, 2011</time>
<p>[&hellip;] I finished the bokeh sample, there were a few remaining issues that I wanted to tackle before I was ready to call it [&hellip;]</p>
<hr />
####
[Styves]( "luv-bunneh@hotmail.com") - <time datetime="2011-03-31 03:13:58">Mar 4, 2011</time>
<p>Glad to see a good explanation on it. Looks great. :) Any chance at getting info on how to go about implementing this in a DX10 environment? The StructuredBuffer stuff&rsquo;s got me confused. It would be really useful. :D</p>
<hr />
####
[default_ex]( "default_ex@live.com") - <time datetime="2011-03-07 22:59:32">Mar 1, 2011</time>
<p>Should take a look at research.tri-ace.com, specifically the later half of the slides from their SO4 post processing slides. One of the things they go into is a Bokeh effect that comes pretty close to a cinematic camera. What I really liked about tri-ace approach is that it doesn&rsquo;t require running a gaussian blur pass, just linear down scaling, which your likely already doing for HDR.</p>
<hr />
####
[Gregory]( "gpakosz@yahoo.fr") - <time datetime="2011-03-06 06:41:46">Mar 0, 2011</time>
<p>Alright, thank you for answering I have to confess I didn&rsquo;t check the source code before asking as I sadly don&rsquo;t own a DX11 GPU. Looks like it&rsquo;s time to fix this. Thank you for your time and article.</p>
<hr />
####
[Gregory]( "gpakosz@yahoo.fr") - <time datetime="2011-03-05 05:47:40">Mar 6, 2011</time>
<p>is dx11 really a must for this sample? or is it just your sample framework that requires dx11 in any case?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-03-05 11:13:37">Mar 6, 2011</time>
<p>AppendStructuredBuffer&rsquo;s (and pixel shader UAV&rsquo;s in general) require a FEATURE_LEVEL_11 device, so yeah it&rsquo;s actually required.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-03-07 23:22:30">Mar 1, 2011</time>
<p>The Tri-ace guys did plenty of blurring&hellip;they just did the blurring at multiple mip levels so that they could cheaply approximate wide blur kernels without a ton of texture samples.</p>
<hr />
####
[Corey Smith](http://www.facebook.com/profile.php?id=625125791 "jenius00@yahoo.com") - <time datetime="2012-03-29 18:19:42">Mar 4, 2012</time>
<p>Thank you for putting this together! Good looking real time bokeh is something I have been curious about but I had no place to start and I&rsquo;m not working with a strong background. It&rsquo;s nice to have jumping off point without trying to discern where the important details lie wading through very involved rendering techniques that reach far beyond my knowledge. Will I ever touch this information again? Who knows if I&rsquo;ll find time to actually investigate it, let alone try and apply it. But I appreciate you putting it together! Very interesting.</p>
<hr />
####
[Bokeh depth of field &#8211; going insane! part 1 | Bart Wronski](http://bartwronski.com/2014/04/07/bokeh-depth-of-field-going-insane-part-1/ "") - <time datetime="2014-04-07 16:44:07">Apr 1, 2014</time>
<p>[…] 2. Probably best for “any” shape of bokeh – smart modern DirectX 11 / OpenGL idea of extracting “significant” bokeh sprites by Matt Pettineo. [2] […]</p>
<hr />
####
[seo](http://seo "2578Bosack@gmail.com") - <time datetime="2012-01-31 09:31:12">Jan 2, 2012</time>
<p>Hi there would you mind stating which blog platform you&rsquo;re working with? I&rsquo;m looking to start my own blog soon but I&rsquo;m having a tough time deciding between BlogEngine/Wordpress/B2evolution and Drupal. The reason I ask is because your design seems different then most blogs and I&rsquo;m looking for something unique. P.S Apologies for being off-topic but I had to ask!</p>
<hr />
####
[Andrew Butts]( "andrewbutts@andru.net") - <time datetime="2011-09-05 14:00:03">Sep 1, 2011</time>
<p>Superduper article&hellip; add vignetting :-)</p>
<hr />
####
[]( "") - <time datetime="2012-08-31 18:31:46">Aug 5, 2012</time>
<p>YOU DONT KNOW WTF BOKEH IS</p>
<hr />
####
[Draft on depth of field resources | Light is beautiful](http://lousodrome.net/blog/light/2012/01/17/draft-on-depth-of-field-resources/ "") - <time datetime="2012-01-17 00:22:07">Jan 2, 2012</time>
<p>[&hellip;] Matt Pettineo also discussed a few approaches to fake bokeh [&hellip;]</p>
<hr />
####
[Jason Yu]( "chunwahyu@hotmail.com") - <time datetime="2014-05-17 05:54:06">May 6, 2014</time>
<p>Is the source code still available, I would like to have a look</p>
<hr />
####
[Shaders PT2 | kosmonaut games](https://kosmonautblog.wordpress.com/2017/03/17/shaders-pt2/ "") - <time datetime="2017-03-17 07:32:05">Mar 5, 2017</time>
<p>[…] didn’t use any templates for this, but I have stumbled upon this implementation by MJPhttps://mynameismjp.wordpress.com/2011/02/28/bokeh/It uses a stock blur for most parts of the image and only applies the bokeh effect on extracted […]</p>
<hr />
####
[各种Depth of Field &laquo; Babylon Garden](http://luluathena.com/?p=2279 "") - <time datetime="2018-04-26 07:20:28">Apr 4, 2018</time>
<p>[…] sprite，其他部分就是普通的模糊处理[14]。这篇博客里有详细的介绍和代码提供，效果也是不错的，GPU […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Radiosity, DX11 Style</title>
			<link>https://therealmjp.github.io/posts/radiosity-dx11-style/</link>
			<pubDate>Mon, 31 Jan 2011 08:08:09 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/radiosity-dx11-style/</guid>
			<description>Radiosity isn&amp;rsquo;t exactly new. According to Wikipedia it&amp;rsquo;s been used for rendering since the early 80&amp;rsquo;s, and this page looks like it may have been the first web page on the Internet. The basic premise is dead simple: for each point where you want to bake lighting (typically either a texel in a lightmap, or a vertex in a mesh), render the rest of the scene and any exterior light sources (skydome, area lights, sun, whatever) in all directions within a hemisphere surrounding the surface normal at that point.</description>
			<content:encoded><![CDATA[<p>Radiosity isn&rsquo;t exactly new. According to Wikipedia it&rsquo;s been used for rendering since the early 80&rsquo;s, and <a href="http://freespace.virgin.net/hugo.elias/radiosity/radiosity.htm">this page</a> looks like it may have been the first web page on the Internet. The basic premise is dead simple: for each point where you want to bake lighting (typically either a texel in a lightmap, or a vertex in a mesh), render the rest of the scene and any exterior light sources (skydome, area lights, sun, whatever) in all directions within a hemisphere surrounding the surface normal at that point.  As described in the article I linked, this is typically done by rendering 5 faces of a hemicube so as to play nice with a traditional perspective projection.  It&rsquo;s possible to use a parabolic projection for this (as in dual-paraboloid shadow mapping), but there are problems you can run into which are outlined <a href="http://the-witness.net/news/?p=244">here</a>. Once you&rsquo;ve fully rendered your hemisphere, you then integrate your computed radiance about the hemisphere with a cosine kernel to compute irradiance, which you can use to determine the diffuse reflectance for that point. You can then store this diffuse lighting value in your vertex or lightmap texel, and you&rsquo;re ready to render your geometry fully lit by direct lighting. Typically you repeat the process many times, rendering the geometry scene geometry as lit by the results of your previous iteration. This effectively allows you to compute lighting for each successive bounce off the scene geometry, adding an indirect lighting term. With enough iterations, you eventually begin to converge on a full global illumination solution for diffuse lighting. The nice part about the technique is that it&rsquo;s pretty simple to implement&hellip;if you can rasterize a mesh you&rsquo;re already most of the way there, and you can even co-opt an existing real-time rendering engine to do it. Taking the latter approach has the added benefit that any material or lighting feature you add to your engine benefits your radiosity baker by default&hellip;so for instance if you have some complex pixel shader that blends multiple maps to determine the final material albedo, you don&rsquo;t need to implement an equivalent solution in a ray tracer or photon mapper. You can even take advantage of your lighting and shadowing pipeline, if you do it right. The major downside is that it&rsquo;s usually pretty slow, even if you implement it all on the GPU. This is because you typically have to serialize rendering of the scene for each vertex or lightmap texel, rather than baking many vertices/texels in parallel (which is possible with ray tracing implementations, particularly if you do it on the GPU using Cuda/Optix). Recently when I was trying to get myself familar with GI techniques, I decided to implement my own radiosity baker (with a lot of help from my coworker Dave). However to make it cool and hip for the DX11 age, I deviated from the &ldquo;standard&rdquo; radiosity process in 3 ways:</p>
<ol start="2">
<li>
<p>Rather than producing a single diffuse color per sample point, I baked down to a set of 1st-order <a href="http://www.cg.tuwien.ac.at/research/publications/2010/Habel-2010-EIN/">H-basis</a> coefficients representing irradiance about the hemisphere. This lets you use normal mapping with your baked values, which adds higher fidelity to your precomputed lighting. This is similar to what Valve, Bungie, and Epic do for their lightmaps, except I&rsquo;m using a different basis. If you&rsquo;re not familiar with H-basis, they&rsquo;re similar to spherical harmonics except that they&rsquo;re only defined on the  upper hemisphere. This allows you to get better quality with less coefficients, for situations where you only need to store information about a hemisphere rather than a full sphere.</p>
</li>
<li>
<p>Instead of baking direct lighting for all light sources, I bake direct + indirect lighting for a skydome and indirect lighting only for the sun. This is similar to what Naughty Dog does in Uncharted. The advantage is that you can add in the direct sun lighting at runtime using a directional light, and you get nice high-frequency visibility from your shadow maps. This lets you avoid having to use an area light or environment map for representing your sun, which can be difficult to tune if you&rsquo;re used to traditional analytical directional light sources. Plus you can light your dynamic geometry the same way and have the lighting match, and also have their dynamic shadows only remove the direct lighting term. Another additional advantage is that your baked lighting term generally only contains low-frequency information, since it doesn&rsquo;t need to represent high frequency shadow visibility from the direct term. So if your scene is decently tessellated you can get away with computing it per-vertex, which is what I did.</p>
</li>
<li>
<p>I used compute shaders for integrating the radiosity hemicube down to H-basis coefficients. This not only made the integration really really fast, but it let me keep everything on the GPU and avoid messing with CPU-GPU memory transfers.</p>
</li>
</ol>
<h1 id="setup">Setup</h1>
<p>To prepare for baking, the scene and all of its individual meshes are loaded and processed. As part of the processing, I calculate a per-vertex tangent frame using a modified version of <a href="http://www.terathon.com/code/tangent.html">this approach</a>. The tangents are needed for normal mapping, but they&rsquo;re also used as a frame of reference for baking each vertex. This is because I store H-basis irradiance in tangent space. Tangent space provides a natural frame of reference for the hemisphere about the normal, and is also consistent across the vertices of a triangle. This lets me interpolate the coefficients across a triangle, which wouldn&rsquo;t be possible if each vertex used a different frame of reference during integration. It also allows for a simple irradiance lookup with the tangent space normal sampled from a normal map, or (0, 0, 1) if normal mapping isn&rsquo;t used.</p>
<h1 id="baking">Baking</h1>
<p>The baking loop looks something like this:</p>
<pre tabindex="0"><code>for each Iteration
    for each Mesh
        Extract vertex data
        for each Vertex
            for each Hemicube Face
                if 1st iteration
                    Render the scene, depth only
                    Render the skydome
                else if 2nd iteration
                    Render the scene with baked lighting + shadowed diffuse from the sun
                else
                    Render the scene with baked lighting
            Integrate
     Sum the result of current iteration with result of previous iteration
</code></pre><p>Basically we do N + 1 iterations, where N is the number of indirect bounces we want to factor in. For the first iteration we add in all direct light sources (the skydome), for the second we add in the bounce lighting from the first pass plus the indirect-only lighting (the sun), and in all subsequent passes we only render the scene geometry with baked lighting.</p>
<h2 id="vertex-baking">Vertex Baking</h2>
<p>For each vertex, we need to determine the radiance emitted from each surface visible around the hemisphere.  This hemisphere of radiance values is known as the <em>field-radiance function.</em> Determining the radiance value for any surface is simple: we just render the corresponding mesh and evaluate the BRDF in the pixel shader, which in our case means sampling the albedo texture and using it to modulate the diffuse lighting.  Since we&rsquo;re going do it using rasterization, we&rsquo;ll render to a hemicube for the reasons mentioned previously. To represent my hemicube, I used 5  R16G16B16A16_FLOAT render target textures storing HDR color values. To keep things simple I made them all equal-sized, and rendered each face as if it were a full cube map rather than a hemicube. However I used the scissor test to scissor out the half of the cube face that would not be needed, for all faces other than the first. Initially I used 256x256 textures for the render targets, but eventually lowered it to 64x64. Increasing the resolution does increase the quality slightly, but gains become diminishing very quickly past 64x64. This is because the irradiance integration filters out the high-frequency components, so any small details missed due to the small render target size have very little effect on the end result. For the first pass, the scene is rendered with color writes disabled. This is because the mesh surfaces do not yet have incident lighting, and thus do not emit any radiance. Conceptually you can imagine this as though all light sources just began emitting light, and the light has yet to reach the mesh surfaces. So essentially we just render the mesh geometry to the depth buffer, in order &ldquo;block out&rdquo; the sky and determine the overall visibility for that vertex. Once we&rsquo;ve done this we render the skydome with a depth value of 1.0, so that any rendered geometry occludes it. Thus we &ldquo;fill in&rdquo; the rest of the hemicube texels with radiance values emitted by the skydome. For the skydome I used the CIE Clear Sky model, which is simple and easy to implement in a pixel shader. The final result in the hemicube textures looks like this: <a href="/images/converted/radiosity-dx11-style/hemicube_firstpass.png"><img src="/images/converted/radiosity-dx11-style/hemicube_firstpass.png" alt="" title="Hemicube_FirstPass"></a> For the second pass, we use the results of the first pass as the incident lighting light for each surface pixel. This effectively causes the skydome lighting to &ldquo;bounce&rdquo; off the surface, adding indirect lighting. We also evaluate the diffuse contribution from the sun for each pixel, so that we get an indirect contribution from the sun as well. This contribution is calculated using a simple N (dot) L with the interpolated vertex normal, and the sun direction. A shadow visibility term is also added using a shadow map, which is rendered as a low-resolution cascaded shadow map. Then the sum of the baked lighting and and the sun light are modulated with the diffuse albedo, which is sampled from a texture. So the final exit radiance value for a pixel is computed like this:</p>
<pre tabindex="0"><code>radiance = (bakedLighting + sunLight * sunVisibility) * diffuseAlbedo
</code></pre><p>After the scene is rendered, the skydome is omitted since it&rsquo;s contribution was already handled in the first pass. Thus the final hemicube looks like this: <a href="/images/converted/radiosity-dx11-style/hemicube_secondpass.png"><img src="/images/converted/radiosity-dx11-style/hemicube_secondpass.png" alt="" title="Hemicube_SecondPass"></a> For all subsequent passes, only the baked vertex irradiance is used for computing the exit radiance of each pixel. This is because the contribution from both of our light sources have already been added in previous passes, and we only need to further compute indirect lighting terms.</p>
<h2 id="integration">Integration</h2>
<p>Once we&rsquo;ve rendered the scene to all 5 sides of the hemicube, we have a full field-radiance function for the hemisphere stored in a texture map. At this point we could now compute a full irradiance distribution function for the hemisphere, which would provide us with an irradiance value for any possible surface normal. Such a function would be computed by convolving our field radiance with a cosine kernel, which is done by evaluating the following integral:</p>
<p>$$ I(p,N_{p})=\int_{\Omega}L(p,\omega_{i})(N_{p}\circ\omega_{i})d\omega_{i} $$</p>
<p>Unfortunately, a full irradiance distribution function in a texture map isn&rsquo;t all that useful since it&rsquo;s too much data to store per-vertex. So instead we&rsquo;ll  represent the irradiance map using 2nd-order spherical harmonics, using the method outlined in the paper &ldquo;<a href="http://graphics.stanford.edu/papers/envmap/">An Efficient Representation for Irradiance Environment Maps</a>&rdquo;. The basic procedure is to first convert the radiance map to a spherical harmonic representation by integrating against the spherical harmonic basis functions, and then convolve the result with a cosine kernel to compute irradiance. The following integral is used for projecting onto SH:</p>
<p>$$ L_{lm}=\int_{\theta=0}^{\pi}\int_{\phi=0}^{\pi}L(\theta,\phi)Y_{lm}(\theta,\phi)sin{\theta}d{\theta}d{\phi} $$</p>
<p>For radiance stored in a texture map, we can implement this integration by using the method described in Peter-Pike Sloan&rsquo;s <a href="http://www.ppsloan.org/publications/StupidSH36.pdf">Stupid Spherical Harmonics Tricks</a>. For our purposes we&rsquo;ll modify the algorithm by first converting each texel&rsquo;s SH radiance to irradiance by convolving with a cosine kernel, and then converting the SH coefficients to 1st-order H-basis representation. This allows us to sum 12 values per texel, rather than the 27 required for 2nd-order SH. The algorithm looks something like this:</p>
<pre tabindex="0"><code>for each Hemicube Face
    for each Texel
        Sample radiance
        Calculate direction vector for the texel
        Project the direction onto SH and convolve with cosine kernel
        Multiply SH coefficients by sampled radiance
        Convert from SH to H-basis
        Weight the coefficients by the differential solid angle for the texel
        Add the coefficients to a running sum
</code></pre><p>What this essentially boils down to is bunch of per-texel math, followed by sum of all results. Sounds like a job for compute shaders! The first part is simple, since the per-texel math operations are completely independent of one another. The second part is a bit tougher, since it requires a parallel reduction to be efficient. Essentially we need to efficiently share results between different threads in order to avoid heavy bandwidth usage, while properly exploiting the GPU&rsquo;s massively parallel architecture by sharing the workload across multiple minimally divergent threads and thread groups. Basically it&rsquo;s pretty simple to implement naively, and tricky to do it with good performance.  Fortunately Nvidia has a bunch of data-parallel algorithms that are part of their cuda SDK, and one of them happens to be a <a href="http://developer.download.nvidia.com/compute/cuda/1_1/Website/projects/reduction/doc/reduction.pdf">parallel reduction</a>. I won&rsquo;t go into the details, but their whitepaper outlines the basic process as well as a series of improvements that can be made to the naive algorithm in order to improve performance. These improvements are a mix of algorithmic and hardware-specific optimizations, and pretty much all of them are easily applicable to compute shaders.</p>
<p>My  implementation ended up being 2 passes: the first performing the conversion to H-basis irradiance and reducing each row of each face texture to a single set of RGB coefficients, and the second reducing to only 1 set of RGB coefficients. In the first pass, the threads are dispatched in 1x64x5 thread groups, with each group containing 64x1x1 threads. The following diagram shows how the threads are distributed relative to the hemicube textures for the first 2 faces:</p>
<p><a href="/images/converted/radiosity-dx11-style/integration.png"><img src="/images/converted/radiosity-dx11-style/integration.png" alt="" title="Integration"></a></p>
<p>The projection onto SH and cosine kernel convolution can be implemented pretty easily in HLSL, using values taken from the irradiance environment mapping paper. My HLSL code looks like this:</p>
<pre tabindex="0"><code>void ProjectOntoSH(in float3 n, in float3 color, out float3 sh[9])
{
    // Cosine kernel
    const float A0 = 3.141593f;
    const float A1 = 2.095395f;
    const float A2 = 0.785398f;

    // Band 0
    sh[0] = 0.282095f * A0 * color;

    // Band 1
    sh[1] = 0.488603f * n.y * A1 * color;
    sh[2] = 0.488603f * n.z * A1 * color;
    sh[3] = 0.488603f * n.x * A1 * color;

    // Band 2
    sh[4] = 1.092548f * n.x * n.y * A2 * color;
    sh[5] = 1.092548f * n.y * n.z * A2 * color;
    sh[6] = 0.315392f * (3.0f * n.z * n.z - 1.0f) * A2 * color;
    sh[7] = 1.092548f * n.x * n.z * A2 * color;
    sh[8] = 0.546274f * (n.x * n.x - n.y * n.y) * A2 * color;
}
</code></pre><p>Converting that to H-basis is also simple, and is expressed as a matrix multiplication. The values for the transformation matrix are given in the source paper. This is the shader code that I used:</p>
<pre tabindex="0"><code>void ConvertToHBasis(in float3 sh[9], out float3 hBasis[4])
{
    const float rt2 = sqrt(2.0f);
    const float rt32 = sqrt(3.0f / 2.0f);
    const float rt52 = sqrt(5.0f / 2.0f);
    const float rt152 = sqrt(15.0f / 2.0f);
    const float convMatrix[4][9] =
    {
        { 1.0f / rt2, 0, 0.5f * rt32, 0, 0, 0, 0, 0, 0 },
        { 0, 1.0f / rt2, 0, 0, 0, (3.0f / 8.0f) * rt52, 0, 0, 0 },
        { 0, 0, 1.0f / (2.0f * rt2), 0, 0, 0, 0.25f * rt152, 0, 0 },
        { 0, 0, 0, 1.0f / rt2, 0, 0, 0, (3.0f / 8.0f) * rt52, 0 }
    };

    [unroll(4)]
    for(uint row = 0; row &lt; 4; ++row)
    {
        hBasis[row] = 0.0f;

        [unroll(9)]
        for(uint col = 0; col &lt; 9; ++col)
            hBasis[row] += convMatrix[row][col] * sh[col];
    }
}
</code></pre><p>After the first pass, we&rsquo;re left with a single buffer containing 64x5x3 float4 values, where each consecutive set of 3 float4 values represents the sum of all RGB H-basis coefficients for that row.  To reduce to a single set of coefficients, we dispatch a reduction pass containing 1x3x1 groups of 64x1x5 threads.  With this setup each group sums all 64 of the R, G, or B coefficients for a particular hemicube face and stores the result in shared memory. Once this has completed, the first thread of each group sums the 5 values for each hemicube to produe a single set of H-basis coefficients. This last step is somewhat sub-optimal since only a single thread performs the work, however for summing only 5 values I didn&rsquo;t think it was worth it to try anything fancy or split the reduction into another pass. The following diagram shows the thread layout:</p>
<p><a href="/images/converted/radiosity-dx11-style/reduction.png"><img src="/images/converted/radiosity-dx11-style/reduction.png" alt="" title="Reduction"></a></p>
<p>The final result of this process is 3 sets of 4 H-basis coefficients (1 for each RGB channel) representing the irradiance across the hemisphere around the vertex normal, oriented in tangent space. After vertices are baked in this manner, I sum the vertex coefficients for each mesh with the results from the previous iteration in order to sum the bounces (which I do with a really simple compute shader). After the desired number of iterations, the coefficients are ready to be used at runtime and combined with direct sun lighting. Evaluating the H-basis coefficients to compute irradiance for a normal is pretty simple. I use the following code in my pixel shader, which takes a tangent space normal and the interpolated coefficients from the vertices:</p>
<pre tabindex="0"><code>float3 GetHBasisIrradiance(in float3 n, in float3 H0, in float3 H1, in float3 H2, in float3 H3)
{
    float3 color = 0.0f;

    // Band 0
    color += H0 * (1.0f / sqrt(2.0f * 3.14159f));

    // Band 1
    color += H1 * -sqrt(1.5f / 3.14159f) * n.y;
    color += H2 * sqrt(1.5f / 3.14159f) * (2 * n.z - 1.0f);
    color += H3 * -sqrt(1.5f / 3.14159f) * n.x;

    return color;
}
</code></pre><h1 id="performance">Performance</h1>
<p>To enable profiling with quick iteration, I made a very simple test scene containing a single mesh and 12,400 vertices. My initial implementation was pretty slow, baking only 545 vertices per second for the 1st pass, 292 vps for the 2nd pass, and close to 545 for all subsequent passes. For the first pass, I determined that the integration step was slowing things down considerably. Initially I had implemented integration using pixel shaders, which converted to H-basis and then reduced each hemicube face by a 1/4 each pass. This resulted in lots of unnecessary render target reads and writes, degrading performance. Moving to my current compute shader implementation brought the first pass to 1600 vps, and the second pass to 325 vps. When I analyzed the second pass, GPU PerfStudio revealed that I was spending a significant amount of time in the geometry shader during the main rendering and shadow map rendering phases. I had used a geometry shader so that I could create the 5 hemicube faces as a texture array (or 4 shadow map cascades for the shadow map), and use SV_RenderTargetArrayIndex to specify the output array slice without having to switch render targets multiple times. I had known that this sort of geometry shader amplification performed poorly on Dx10 hardware and had been hoping that it wouldn&rsquo;t be so bad on my 5830, but unfortunately this was not the case. Ditching the geometry shader  and setting the render target slices one by one brought me up to 1760 vps for the first pass and 480 vps for the second pass. Further performance was gained by switching the cascaded shadow map implementation to use an old-school texture atlas rather than a texture array, which brought me to 625 vps for the second pass. This was disappointing, since texture arrays are a totally natural and convenient way to implement cascaded shadow maps. Texture atlases are so DX9. Even after that the shadow map rendering was still really slowing down the 2nd pass, so I cut it down to 2 cascades (from 4) and reduced the resolution from 2048x2048 per cascade to 512x512. This got me to 850 vps for the test scene, about 600 vps for the broken tank scene from the SDK, and about 180 vps for the powerplant scene from the SDK. In its current state, the GPU is currently spending a portion of each vertex bake idling due to processing so many commands and having multiple render target switches. It could definitely benefit from some reduction in overall amount of API commands and state changes, and batching during the shadow map rendering. It would also probably benefit from using an approach similar to <a href="http://the-witness.net/news/?p=244">Ignacio&rsquo;s</a>, where the shadow map is only rendered once for a group of vertices.</p>
<p>Now for some pictures! These were all taken with only a single bounce, because I&rsquo;m impatient.</p>
<p>Test scene: baked lighting only, baked lighting with normal maps, baked lighting + direct sunlight, baked light + direct with normal maps, final</p>
<p><a href="/images/converted/radiosity-dx11-style/testscene_bakenonm.png"><img src="/images/converted/radiosity-dx11-style/testscene_bakenonm_resized_600.png" alt="" title="TestScene_BakeNoNM"></a> <a href="/images/converted/radiosity-dx11-style/testscene_bake.png"><img src="/images/converted/radiosity-dx11-style/testscene_bake_resized_600.png" alt="" title="TestScene_Bake"></a> <a href="/images/converted/radiosity-dx11-style/testscene_noalbedononm.png"><img src="/images/converted/radiosity-dx11-style/testscene_noalbedononm_resized_600.png" alt="" title="testscene_NoAlbedoNoNM"></a> <a href="/images/converted/radiosity-dx11-style/testscene_noalbedo.png"><img src="/images/converted/radiosity-dx11-style/testscene_noalbedo_resized_600.png" alt="" title="TestScene_NoAlbedo"></a> <a href="/images/converted/radiosity-dx11-style/testscene_all.png"><img src="/images/converted/radiosity-dx11-style/testscene_all_resized_600.png" alt="" title="TestScene_All"></a></p>
<p>Tank scene: baked only, baked with normal mapping, baked + direct, final, alternate final, another alternate final</p>
<p><a href="/images/converted/radiosity-dx11-style/tankscene_nm.png"><img src="/images/converted/radiosity-dx11-style/tankscene_nm_resized_600.png" alt="" title="TankScene_NM"></a> <a href="/images/converted/radiosity-dx11-style/tankscene_bakenm.png"><img src="/images/converted/radiosity-dx11-style/tankscene_bakenm_resized_600.png" alt="" title="TankScene_BakeNM"></a> <a href="/images/converted/radiosity-dx11-style/tankscene_bakedirect.png"><img src="/images/converted/radiosity-dx11-style/tankscene_bakedirect_resized_600.png" alt="" title="TankScene_BakeDirect"></a> <a href="/images/converted/radiosity-dx11-style/tankscene_final.png"><img src="/images/converted/radiosity-dx11-style/tankscene_final_resized_600.png" alt="" title="TankScene_Final"></a> <a href="/images/converted/radiosity-dx11-style/tankscene_final2.png"><img src="/images/converted/radiosity-dx11-style/tankscene_final2_resized_600.png" alt="" title="TankScene_Final2"></a> <a href="/images/converted/radiosity-dx11-style/tankscene_final3.png"><img src="/images/converted/radiosity-dx11-style/tankscene_final3_resized_600.png" alt="" title="TankScene_Final3"></a></p>
<p>Powerplant scene: direct only, baked only, baked + direct, baked w/o normal mapping, baked w/ normal mapping, alternate baked + direct, final</p>
<p><a href="/images/converted/radiosity-dx11-style/powerplant_nobake.png"><img src="/images/converted/radiosity-dx11-style/powerplant_nobake_resized_600.png" alt="" title="Powerplant_NoBake"></a> <a href="/images/converted/radiosity-dx11-style/powerplant_nodirect.png"><img src="/images/converted/radiosity-dx11-style/powerplant_nodirect_resized_600.png" alt="" title="Powerplant_NoDirect"></a> <a href="/images/converted/radiosity-dx11-style/powerplant_noalbedo.png"><img src="/images/converted/radiosity-dx11-style/powerplant_noalbedo_resized_600.png" alt="" title="Powerplant_NoAlbedo"></a> <a href="/images/converted/radiosity-dx11-style/powerplant_bakenonm.png"><img src="/images/converted/radiosity-dx11-style/powerplant_bakenonm_resized_600.png" alt="" title="Powerplant_BakeNoNM"></a> <a href="/images/converted/radiosity-dx11-style/powerplant_bakewithnm.png"><img src="/images/converted/radiosity-dx11-style/powerplant_bakewithnm_resized_600.png" alt="" title="Powerplant_BakeWithNM"></a> <a href="/images/converted/radiosity-dx11-style/powerplant_bakedirect.png"><img src="/images/converted/radiosity-dx11-style/powerplant_bakedirect_resized_600.png" alt="" title="Powerplant_BakeDirect"></a> <a href="/images/converted/radiosity-dx11-style/powerplant_all.png"><img src="/images/converted/radiosity-dx11-style/powerplant_all_resized_600.png" alt="" title="Powerplant_All"></a></p>
<p>Source code and binaries are available here:</p>
<p><a href="http://mynameismjp.files.wordpress.com/2014/04/radiositydx11.zip">http://mynameismjp.files.wordpress.com/2014/04/radiositydx11.zip</a></p>
<p><em>Updated (3/27/2011): Changed the shadow filtering shader code so that it doesn&rsquo;t cause crashes on Nvidia hardware</em></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22ljcrobotic@yahoo.com%22">Louis Castricato</a> - <time datetime="2011-04-26 20:02:52">Apr 2, 2011</time></p>
<p>At this point I&rsquo;m starting to think you are an ATI fan boy &gt;.&lt;</p>
<hr />
####
[Ignacio](http://castano.ludicon.com/blog/ "castano@gmail.com") - <time datetime="2011-02-01 00:21:07">Feb 2, 2011</time>
<p>Good work! I wish we could port our baker to D3D11. I think that an interesting approach would be to combine the use of texture atlases with geometry shaders by using SV_ViewportArrayIndex. For this to be really effective you really need to use a view independent shadowing method and render multiple hemicubes in the same render target.</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-02-02 22:34:21">Feb 3, 2011</time>
<p>Yeah&hellip;you&rsquo;re never going to get decent AO with a vertex bake unless you carefully stitch your geometry in the right places, and that&rsquo;s just not really practical. You could definitely do a better job with light maps, since your texel density is decoupled from the geometry and you could redistribute it as needed. Or you could just use baked lighting for the large-scale GI effects, and rely on SSAO and/or AO maps to provide the small-scale bits.</p>
<hr />
####
[rake]( "") - <time datetime="2011-01-31 01:22:28">Jan 1, 2011</time>
<p>Thanks for the great post! However, the demo crashes on my NVIDIA GTX480 directly after the baking. I get an access violation at RSSetViewports in the nvwgf2um.dll. It looks like the latest NVIDIA drivers screws something up.</p>
<hr />
####
[Marco]( "marco-we@freenet.de") - <time datetime="2011-01-31 01:38:09">Jan 1, 2011</time>
<p>Fantastic article, loved it! Great results!</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-02-01 00:43:21">Feb 2, 2011</time>
<p>@rake - Sorry to hear that! My coworker had the same problem on his work PC running a GTX465. Unfortunately I don&rsquo;t have access to a similar GPU at home and my work PC only has a DX10 GPU, so I&rsquo;ll have to try to get my coworker to debug it for me. @Marco - Thanks! @Ignacio - I&rsquo;ll have to try that and see what sort of performance I get. I had tried rendering the geometry 5 times and each time specifying a different value for SV_RenderTargetArray index, and even then I was still spending a significant amount of time in the geometry shader (the time seemed to be proportional to the amount of per-vertex data being emitted).</p>
<hr />
####
[Some interesting news&#8230; | eteractions](http://eteractions.wordpress.com/2011/02/01/some-interesting-news/ "") - <time datetime="2011-02-01 00:51:31">Feb 2, 2011</time>
<p>[&hellip;] And also this one: http://mynameismjp.wordpress.com/2011/01/31/radiosity-dx11-style/ [&hellip;]</p>
<hr />
####
[Tweets that mention Radiosity, DX11 Style « The Danger Zone -- Topsy.com](http://topsy.com/mynameismjp.wordpress.com/2011/01/31/radiosity-dx11-style/?utm_source=pingback&utm_campaign=L2 "") - <time datetime="2011-01-31 17:03:05">Jan 1, 2011</time>
<p>[&hellip;] This post was mentioned on Twitter by Martin Ecker, indickczar. indickczar said: Radiosity, DX11 Style « The Danger Zone <a href="http://bit.ly/eQPrTQ">http://bit.ly/eQPrTQ</a> あとで、じっくり読む。 [&hellip;]</p>
<hr />
####
[Daniel](http://talden.com "dwrightnow@gmail.com") - <time datetime="2011-02-02 19:56:29">Feb 3, 2011</time>
<p>Very cool post. I think your quality is heavily limited by not having enough resolution (or regular enough data points) to reproduce good AO in the indirect areas though.</p>
<hr />
####
[]( "") - <time datetime="2011-10-19 07:40:14">Oct 3, 2011</time>
<p>Can this be adjusted to calculate PRT?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-10-19 09:32:26">Oct 3, 2011</time>
<p>Yeah, absolutely. In the first pass you would just need to project the sky visibility onto SH, then in subsequent passes you would add in the bounces modulated by the albedo color.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2014-04-13 14:04:14">Apr 0, 2014</time>
<p>The zip file is now hosted directly on this site, which you can find at the updated link at the bottom of the post. Let me know if you still have problems.</p>
<hr />
####
[this programmer]( "andresiraola@gmail.com") - <time datetime="2014-04-13 11:55:39">Apr 0, 2014</time>
<p>would you please reupload the files? I would like to take a look at it but it seems like they are not online anymore or office live is down. thanks!</p>
<hr />
####
[First Post &#8211; All About Radiosity &laquo; wirezapp](http://wirezapp.wordpress.com/2011/10/28/first-post-all-about-radiosity/ "") - <time datetime="2011-10-27 16:18:45">Oct 4, 2011</time>
<p>[&hellip;] <a href="http://mynameismjp.wordpress.com/2011/01/31/radiosity-dx11-style/">http://mynameismjp.wordpress.com/2011/01/31/radiosity-dx11-style/</a> [&hellip;]</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2014-11-28 19:29:48">Nov 5, 2014</time>
<p>No, I didn&rsquo;t do anything special to avoid interpolation problems in the demo. The test scene was pretty rough, so you can definitely see some issues if you look closely. I&rsquo;ve never personally implemented anything like that paper, although it definitely seems to get great results. I believe they shipped with it for the vertex AO bakes in Destiny. At work we use lightmaps, which makes it much easier to control your allocation of sample points but brings its own headaches. For this sample I used per-vertex baking because it was easy to implement, although for newer projects I have a framework that works with lightmaps instead.</p>
<hr />
####
[Foogywoo](http://dontnormalize.me "foogywoo@gmail.com") - <time datetime="2014-11-28 17:14:16">Nov 5, 2014</time>
<p>Nice article! Did you have to do anything particular to avoid radiosity interpolation problem between vertex? Something like this <a href="http://www.seas.upenn.edu/~ladislav/kavan11least/kavan11least.pdf">http://www.seas.upenn.edu/~ladislav/kavan11least/kavan11least.pdf</a></p>
<hr />
####
[]( "") - <time datetime="2017-07-26 10:14:17">Jul 3, 2017</time>
<p>I have built this in vs2013 and vs2015 and everything except for the sky comes out black once vertex baking completes. Your pre-built binary executables work fine, so it must be some compile setting when vs converted from vs2010. Any tips on what compile settings for vs2013 or vs2015 for this to work properly?</p>
<hr />
####
[Lightmapper &#8211; Bei Kun&#039;s Private Home](http://www.bxs.moe/?p=542 "") - <time datetime="2016-11-17 23:50:50">Nov 4, 2016</time>
<p>[…] <a href="https://mynameismjp.wordpress.com/2011/01/31/radiosity-dx11-style/">https://mynameismjp.wordpress.com/2011/01/31/radiosity-dx11-style/</a> […]</p>
<hr />
####
[]( "") - <time datetime="2017-08-06 14:13:01">Aug 0, 2017</time>
<p>I must had something corrupt. Fresh download and conversion now work beautifully. Thank you for this.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Position From Depth in GLSL</title>
			<link>https://therealmjp.github.io/posts/position-from-depth-glsl-style/</link>
			<pubDate>Sun, 09 Jan 2011 01:47:45 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/position-from-depth-glsl-style/</guid>
			<description>Commenter &amp;ldquo;Me&amp;rdquo; was kind enough to share his GLSL implementation of a deferred point light shader, which makes use of one of the methods I previously posted for reconstructing position from depth. So I figured I&amp;rsquo;d post it here, for all of you unfortunate enough to be stuck with writing shaders in GLSL. :P
// deferred shading VERTEX (GEOMETRY) varying vec3 normalv, posv; void main( void ) { normalv = ( gl_NormalMatrix * gl_Normal ).</description>
			<content:encoded><![CDATA[<p>Commenter &ldquo;Me&rdquo; was kind enough to share his GLSL implementation of a deferred point light shader, which makes use of one of the methods I <a href="http://mynameismjp.wordpress.com/2010/09/05/position-from-depth-3/">previously posted</a> for reconstructing position from depth. So I figured I&rsquo;d post it here, for all of you unfortunate enough to be stuck with writing shaders in GLSL. :P</p>
<pre tabindex="0"><code>// deferred shading VERTEX (GEOMETRY)
varying vec3 normalv, posv;

void main( void ) {
    normalv = ( gl_NormalMatrix * gl_Normal ).xyz;
    posv = ( gl_ModelViewMatrix * gl_Vertex ).xyz;
    gl_Position = ftransform();
}

// deferred shading FRAGMENT (GEOMETRY)
varying vec3  normalv, posv;
uniform vec2  nfv;

void main( void ){
    gl_FragData[0] = vec4( normalize( normalv ) * 0.5 + 0.5, -posv.z / nfv.y );
}

// deferred shading VERTEX (LIGHTING: POINT)
varying vec3 posv;

void main( void ){
    posv = ( gl_ModelViewMatrix * gl_Vertex ).xyz;
    gl_Position = ftransform();
    gl_FrontColor = gl_Color;
}

// deferred shading FRAGMENT (LIGHTING: POINT)
varying vec3 posv;
uniform float lradius;
uniform vec3 lcenter;
uniform vec2 nfv, sic;
uniform sampler2D geotexture;

void main( void ){
    vec2 tcoord = gl_FragCoord.xy * sic;
    vec4 geometry = texture2D( geotexture, tcoord );
    vec3 viewray = vec3( posv.xy * ( -nfv.y / posv.z ), -nfv.y );
    vec3 vscoord = viewray * geometry.a;
    float dlight = length( lcenter - vscoord );
    float factor = 1.0 - dlight/lradius;
    if( dlight &gt; lradius ) discard;
    gl_FragData[0] = vec4( gl_Color.rgb, factor );
}
</code></pre><hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22mail@lukasmeindl.at%22">Lukas Meindl</a> - <time datetime="2011-03-09 12:46:50">Mar 3, 2011</time></p>
<p>This is a good code basis for a deferred spotlight shader. To make it more understandable I would like to add: - &ldquo;uniform vec2 nfv;&rdquo; should be &ldquo;uniform float farClipDistance&rdquo; - Replace &ldquo;nfv.y&rdquo; by &ldquo;farClipDistance&rdquo;, x is never used and i can&rsquo;t think of a way of using it here. - I would change &ldquo;vec2 tcoord = gl_FragCoord.xy * sic;&rdquo; to &ldquo;vec4 ex_persp_position = gl_FragCoord / gl_FragCoord.w; vec2 texCoord = ex_persp_position.xy * 0.5 + vec2(0.5);&rdquo; This should work- the variable &ldquo;sic&rdquo; can be removed then&hellip;</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Conservative Depth Output (and Other Lesser-Known D3D11 Features)</title>
			<link>https://therealmjp.github.io/posts/d3d11-features/</link>
			<pubDate>Mon, 15 Nov 2010 02:24:48 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/d3d11-features/</guid>
			<description>D3D11 came with a whole bunch of new big-ticket features that received plenty of attention and publicity. Things like tessellation, compute shaders, and multithreaded command submission have the subject of many presentations, discussion, and sample apps. However D3D11 also came with a few other features that allow more &amp;ldquo;traditional&amp;rdquo; rendering approaches to benefit from the increased programmability of graphics hardware. Unfortunately most of them have gone relatively unnoticed, which isn&amp;rsquo;t surprising when you consider that most of them have little or no documentation, (much like some of the cool stuff that came in D3D10.</description>
			<content:encoded><![CDATA[<p>D3D11 came with a whole bunch of new big-ticket features that received plenty of attention and publicity. Things like tessellation, compute shaders, and multithreaded command submission have the subject of many presentations, discussion, and sample apps. However D3D11 also came with a few other features that allow more &ldquo;traditional&rdquo; rendering approaches to benefit from the increased programmability of graphics hardware. Unfortunately most of them have gone relatively unnoticed, which isn&rsquo;t surprising when you consider that most of them have little or no documentation, (much like some of the <a href="http://www.humus.name/index.php?page=News&amp;ID=230">cool stuff</a> that came in D3D10.1). Not too long ago one of these neat tricks came to my attention by way of <a href="http://filmicgames.com/archives/507#more-507">John Hable&rsquo;s blog</a>, which inspired me to dig around a bit and try out some of other neat tricks I was missing out on. Quite a few are briefly described in <a href="http://www.slideshare.net/hgruen/dx11-performance-tipstricks">this presentation</a> from GDC. Here&rsquo;s a few of my favorites, in no particular order:</p>
<p>1. Conservative depth output: this is something you use for pixel shaders that manually output a depth value. Basically rather than using SV_Depth, you use a variant that also specifiea an inequality. For instance SV_DepthGreater, or SV_DepthLessEqual. The depth you output from the shader must then satisfy the inequality relative to the interpolated depth of the rasterized triangle (if you don&rsquo;t, the depth value is clamped for you). This allows the GPU to still use early-z cull, since it can still trivially reject pixels for cases where the depth test will always fail for the specified upper/lower bound. So for instance if you render a quad and output DepthGreaterEqual, the GPU can cull pixels where the quad&rsquo;s depth is greater than the depth buffer value. Don&rsquo;t bother looking for this one in the documentation&hellip;it&rsquo;s not in there.</p>
<p>2. SV_Coverage as an  input: D3D10.1 added the feature to let you <em>output</em> to SV_Coverage in order to manually specify the MSAA coverage mask (which controls how the pixel shader output gets written to the subsamples). In D3D11 you also can take it as an input to your pixel shader to know which of the sample points passed the triangle coverage test. This is really handy for deferred rendering, since you&rsquo;ll want to mark off edge pixels as those are the only pixels that require you to sample all of the subsamples in the G-Buffer. In D3D10 you could do this with the <a href="http://www.humus.name/index.php?page=3D&amp;ID=81">centroid sampling trick</a>, but it&rsquo;s much nicer to just skip the intermediate step and get coverage directly. Plus the rules for centroid sampling are a little loosely defined, so I don&rsquo;t really like relying on it.</p>
<p>3. Programmable interpolation: D3D10/D3D10.1 already had some modifiers you could use for pixel shader attributes that controlled how they were interpolated. For instance you had linear, noPerspective, and centroid. In D3D11 you still have those, but you also have a series of EvaluateAttributeAt* instrinsics that allow you to evaluate an attribute using a specified interpolation mode. Probably the most useful of the bunch of EvaluateAttributeAtSample, which interpolates the attribute to the MSAA sample point for the specified index. Probably the most obvious use case is for selective supersampling&hellip;using that intrinsic you could evaluate your BRDF at each subsample location. You can also sample alpha-tested textures multiple times, effectively antialiasing the edges. I whipped up a little test case where I rendered a billboarded quad to an MSAA target, and in the pixel shader I did a simple ray-cast into a sphere located at the quad center. I took SV_Coverage as an input to determine if the pixel was an edge pixel (all sample points weren&rsquo;t full covered), and in that case I did a ray-cast per-sample using EvaluateAttributeAtSample to snap the interpolated view-space position to each sample point. This basically gives you selective super-sampling, so that you get anti-aliased edges without relying on rasterization. Cool stuff!</p>
<p>4. Read-only depth-stencil views: D3D10 let you bind depth-stencil buffers as shader resource views so that you could sample them in the pixel shader, but came with the restriction that you couldn&rsquo;t have them simultaneously bound to the pipeline as both views simultaneously. That&rsquo;s fine for SSAO or depth of field, but not so fine if you want to do deferred rendering and use depth-stencil culling. D3D10.1 added the ability to let you copy depth buffers asynchronously using the GPU, but that&rsquo;s still not optimal. D3D11 finally makes everything right in the world by letting you create depth-stencil views with a read-only flag. So basically you can just create two DSV&rsquo;s for your depth buffer, and switch to the read-only one when you want to do depth readback.</p>
<p>5. Unordered access views for pixel shaders: UAV&rsquo;s are essentially buffers or textures that give you both random read access *and* random write access. They&rsquo;re usually mentioned in the context of compute shaders, but they&rsquo;re actually usable for pixel shaders too. I haven&rsquo;t really dug into this use case, but it seems as though you could implement scatter or fully programmable blending.</p>
<p>After doing some research, I came up with a quick sample app so that I could try out conservative depth output and see the performance results. I ended up basing it off the SoftParticles sample from the SDK, since depth sprites are probably the most obvious use-case for that particular feature. Here&rsquo; s some numbers I got running on my machine (Radeon HD 5830) at 1280x720 resolution, with the particles covering most of the viewport:</p>
<p>Basic billboarding: 8.7ms</p>
<p>Depth output enabled: 11.76ms</p>
<p>Conservative depth enabled: 9.52ms</p>
<p>Soft particles w/depth output: 23.25ms</p>
<p>Soft particles w/conservative depth: 18.18ms</p>
<p>So overall it looks like it gets you about halfway back to the performance you get with no depth output, which is pretty nice (especially considering how easy it is to use). In addition to that, I also used a read-only depth-stencil buffer for the soft particles so that I could keep depth testing active.</p>
<p>If you want to run it yourself or check out the code, I uploaded the code + binaries here: <a href="https://mynameismjp.files.wordpress.com/2010/11/depthsprites.zip">https://mynameismjp.files.wordpress.com/2010/11/depthsprites.zip</a></p>
<p>I&rsquo;ll also leave you off with a picture of my totally sweet fire/smoke effect. I should quit my job and become an effects artist.</p>
<h2 id="imagesconvertedd3d11-featuresdepthsprites-2010-11-14-16-40-46-18png-depthsprites-2010-11-14-16-40-46-18imagesconvertedd3d11-featuresdepthsprites-2010-11-14-16-40-46-18png"><a href="/images/converted/d3d11-features/depthsprites-2010-11-14-16-40-46-18.png"><img src="/images/converted/d3d11-features/depthsprites-2010-11-14-16-40-46-18.png" alt="" title="DepthSprites 2010-11-14 16-40-46-18"></a></h2>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://recreationstudios.blogspot.com" title="mccarthp@gmail.com">Patrick</a> - <time datetime="2010-11-14 19:18:54">Nov 0, 2010</time></p>
<p>Great post! It looks like the conservative depth is quite useful. I&rsquo;ll have to look more into it. I wrote a simple Compute Shader in SlimDX a couple months ago that used an Unordered Access View to draw primitives like lines and circles. UAVs are very powerful. It&rsquo;s located here, for anyone interested: <a href="http://recreationstudios.blogspot.com/2010/04/simple-compute-shader-example.html">http://recreationstudios.blogspot.com/2010/04/simple-compute-shader-example.html</a></p>
<hr />
#### 
[Daniel](http://talden.com "dwrightnow@gmail.com") - <time datetime="2010-11-14 21:37:13">Nov 0, 2010</time>
<p>Pure awesome information. Don&rsquo;t forget about texture cube arrays being new! Jk - not really sure what those are useful for. Oh and your smoke effect - don&rsquo;t quit your day job =)</p>
<hr />
#### 
[Direct3D 11 Conservative Depth Output Details and Demo - 3D Tech News, Pixel Hacking, Data Visualization and 3D Programming - Geeks3D.com](http://www.geeks3d.com/20101220/direct3d-11-conservative-depth-output-details-and-demo/ "") - <time datetime="2010-12-20 00:29:02">Dec 1, 2010</time>
<p>[&hellip;] You can find the complete article (covering also other lesser-known D3D11 features) as well as the demo HERE. [&hellip;]</p>
<hr />
#### 
[Jeremy](http://www.jshopf.com/blog "jshopf@gmail.com") - <time datetime="2010-11-16 06:29:15">Nov 2, 2010</time>
<p>I agree, the &ldquo;small&rdquo; features are great: <a href="http://www.jshopf.com/blog/?p=166">http://www.jshopf.com/blog/?p=166</a>. Granted I wrote that before there was were dx11 drivers available..</p>
<hr />
#### 
[Linear Depth | The Devil In The Details](http://dev.theomader.com/linear-depth/ "") - <time datetime="2013-12-23 02:05:12">Dec 1, 2013</time>
<p>[…] loose the early Z unless you use the variant SV_DepthGreater, or SV_DepthLessEqual. Check out this blog post for more details. In most cases though I would argue that non linear depth is just […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Position From Depth 3: Back In The Habit</title>
			<link>https://therealmjp.github.io/posts/position-from-depth-3/</link>
			<pubDate>Mon, 06 Sep 2010 07:11:52 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/position-from-depth-3/</guid>
			<description>A friend of mine once told me that you could use &amp;ldquo;back in the habit&amp;rdquo; as the subtitle for any movie sequel. I think it works.
So a lot of people still have trouble with reconstructing position from depth thing, judging by the emails I get and also the threads I see in the gamedev forums made by people who read my earlier blog posts. Can&amp;rsquo;t say I blame them&amp;hellip;it&amp;rsquo;s pretty tricky, and easy to screw up.</description>
			<content:encoded><![CDATA[<p>A friend of mine once told me that you could use &ldquo;back in the habit&rdquo; as the subtitle for any movie sequel. I think it works.</p>
<p>So a lot of people still have trouble with <a href="http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/">reconstructing position from depth</a> thing, judging by the emails I get and also the threads I see in the gamedev forums made by people who read my earlier blog posts.  Can&rsquo;t say I blame them&hellip;it&rsquo;s pretty tricky, and easy to screw up.  So I&rsquo;m going to take it again from the beginning and try to explain some of the actual math behind the code, in hopes that a more generalized approach will help people get through the little quirks they&rsquo;ll have to deal with in their own implementations. And I&rsquo;ll throw some shader code in there too for good measure.  Oh and I should mention that I&rsquo;m going to do everything with a left-handed coordinate system, but it shouldn&rsquo;t be hard to convert to right-handed.</p>
<p>Let&rsquo;s start with the basics of a perspective projection. For every pixel on the screen there&rsquo;s a  direction vector associated with it.  You can figure out this direction vector by using the screen-space XY position of the pixel to lerp between the positions of the frustum corners, subtracting the camera position, and normalizing (you don&rsquo;t have to subtract the camera position if you&rsquo;re doing this in view space, since the camera position is 0). If geometry is rasterized at that pixel position, that means the surface at that pixel lies somewhere along that vector. The distance along that vector will vary depending on how far that geometry is from the camera, but the direction is always the same.  This should sound familiar to anyone who&rsquo;s written a ray tracer before, because it&rsquo;s the exact concept used for primary rays: for a pixel on the near or far clip plane, get the direction from the camera to that pixel and check for intersections.  What this ultimately means is that if we have the screen space pixel position and the camera position, we can figure out the position of the triangle surface if have the distance from the camera to the surface.  Here&rsquo;s an artfully-crafted diagram showing how this works:</p>
<p><a href="/images/converted/position-from-depth-3/view-ray.png"><img src="/images/converted/position-from-depth-3/view-ray_resized_300.png" alt="" title="View Ray"></a>With this is mind, you might be thinking that if we stored the distance from the camera to the triangle surface in a G-Buffer pass then it would be really easy to reconstruct position from it.  And you&rsquo;d be totally right.  The basic steps go like this:</p>
<ol>
<li>In the pixel shader of the G-Buffer pass, calculate the distance from the camera to the surface being shaded  and write it out to the depth texture</li>
<li>In the vertex shader of the light pass, calculate the direction vector from the camera position to the vertex (we&rsquo;ll call it the view ray).</li>
<li>In the pixel shader, normalize the view ray vector</li>
<li>Sample the depth texture to get the distance from the camera to the G-Buffer surface</li>
<li>Multiply the sampled distance with the view ray</li>
<li>Add the camera position</li>
</ol>
<p>This is simple, cheap, and works in both view space and world space.  In view space it&rsquo;s a little cheaper and easier because the camera position is (0,0,0), so you can simplify the math.  Plus the view ray is just the normalized view space position of the pixel.  For a full-screen quad, you can get the view space position of the quad vertices either by directly mapping the verts to frustum corners, or by applying the inverse of your projection matrix.  Then from there you can go back to world space if you want by applying the inverse of your view matrix (the camera world matrix).  Here&rsquo;s what the code might look like for doing it in world space (since people seem to like sticking to world space, despite the advantages of view space):</p>
<pre tabindex="0"><code>// G-Buffer vertex shader
// Calculate view space position of the vertex and pass it to the pixel shader
output.PositionVS = mul(input.PositionOS, WorldViewMatrix).xyz;

// G-Buffer pixel shader
// Calculate the length of the view space position to get the distance from camera-&gt;surface
output.Distance.x = length(input.PositionVS);

// Light vertex shader
#if PointLight || SpotLight
    // Calculate the world space position for a light volume
    float3 positionWS = mul(input.PositionOS, WorldMatrix);
#elif DirectionalLight
    // Calculate the world space position for a full-screen quad (assume input vertex coordinates are in [-1,1] post-projection space)
    float3 positionWS = mul(input.PositionOS, InvViewProjMatrix);
#endif

// Calculate the view ray
output.ViewRay = positionWS - CameraPositionWS;

// Light Pixel shader
// Normalize the view ray, and apply the original distance to reconstruct position
float3 viewRay = normalize(input.ViewRay);
float viewDistance = DistanceTexture.Sample(PointSampler, texCoord);
float3 positionWS = CameraPositionWS + viewRay * viewDistance;
</code></pre><p>Like I said it&rsquo;s piece of cake, and I suspect that for a lot of people it&rsquo;s efficient enough. But we&rsquo;re not done yet, since we can still optimize things further if we stick to view space. We also want  may want to use a hardware depth buffer as opposed to manually storing a distance value.  So let&rsquo;s dig deeper.  Here&rsquo;s a diagram showing another way of looking at the problem:</p>
<p><a href="/images/converted/position-from-depth-3/view-space-geo.png"><img src="/images/converted/position-from-depth-3/view-space-geo_resized_300.png" alt="" title="View Space Geo"></a></p>
<p><a href="/images/converted/position-from-depth-3/view-space-quad.png">
</a>This time instead of using a normalized direction vector for the view ray, we extrapolate the ray all the way back until it intersects with the far clip plane.  When we do this, it means that the position at the end of the view ray is at a known depth relative to the camera position and the direction the camera is looking (the depth is the far clip plane distance).  In view space it means that the view ray has a Z component equal to the far clip plane.  Since the Z component is a known value we no longer need to normalize the view ray vector.  Instead we can multiply by a value that scales along the camera&rsquo;s z-axis to get the final reconstructed position.  In the case where Z = FarClipDistance, we want to scale by a ratio of the original surface depth relative to the far clip plane.  In other words, the surface&rsquo;s view space Z divided by the far clip distance.  In code it looks like this:</p>
<pre tabindex="0"><code>// G-Buffer vertex shader
// Calculate view space position of the vertex and pass it to the pixel shader
output.PositionVS = mul(input.PositionOS, WorldViewMatrix).xyz;

// G-Buffer pixel shader
// Divide view space Z by the far clip distance
output.Depth.x = input.PositionVS.z / FarClipDistance;

// Light vertex shader
#if PointLight || SpotLight
    // Calculate the view space vertex position
    output.PositionVS = mul(input.PositionOS, WorldViewMatrix);
#elif DirectionalLight
    // Calculate the view space vertex position (you can also just directly map the vertex to a frustum corner
    // to avoid the transform)
    output.PositionVS = mul(input.PositionOS, InvProjMatrix);
#endif

// Light Pixel shader
#if PointLight || SpotLight
    // Extrapolate the view space position to the  far clip plane
    float3 viewRay = float3(input.PositionVS.xy * (FarClipDistance / input.PositionVS.z), FarClipDistance);
#elif DirectionalLight
    // For a directional light, the vertices were already on the far clip plane so we don&#39;t need to extrapolate
    float3 viewRay = input.PositionVS.xyz;
#endif

// Sample the depth and scale the view ray to reconstruct view space position
float normalizedDepth = DepthTexture.Sample(PointSampler, texCoord).x;
float3 positionVS = viewRay * normalizedDepth;
</code></pre><p>As you can see this is a bit cheaper, especially for the full-screen quad case.  One thing to be aware of is that since with this we store normalized depth, it&rsquo;s always in the range [0,1].  This means you can store it in a normalized integer format (such as DXGI_FORMAT_R16_UNORM) without having to do any rescaling after you sample it.  A floating point format will obviously handle it just fine as well.</p>
<p>Now let&rsquo;s say we want to sample a hardware depth buffer instead of writing out our own depth or distance value to the G-Buffer.  This makes sense in a lot of cases, since you&rsquo;re already using the memory and bandwidth to fill the depth buffer so you might as well make use of it.  A hardware depth buffer will store the post-projection Z value divided by the post-projection W value, where W is equal to the view-space Z component of the surface position (for more information see <a href="http://www.sjbaker.org/steve/omniv/love_your_z_buffer.html">this</a>).  This makes the value initially unsuitable for our needs, but fortunately it&rsquo;s possible to recover the view-space Z from this using the parameters of the perspective projection.  Once we do that, we can convert it to a normalized depth value if we want and proceed normally.  However this is unnecessary.  Instead of extrapolating the view ray to the far clip plane, if we instead clamp it to the plane at Z = 1 we can then scale it by the view space Z without having to manipulate it first.  Here&rsquo;s the code:</p>
<pre tabindex="0"><code>// Light vertex shader
#if PointLight || SpotLight
    // Calculate the view space vertex position
    output.PositionVS = mul(input.PositionOS, WorldViewMatrix);
#elif DirectionalLight
    // For a directional light we can clamp in the vertex shader, since we only interpolate in the XY direction
    float3 positionVS = mul(input.PositionOS, InvProjMatrix);
    output.ViewRay = float3(positionVS.xy / positionVS.z, 1.0f);
#endif

// Light Pixel shader
#if PointLight || SpotLight
    // Clamp the view space position to the plane at Z = 1
    float3 viewRay = float3(input.PositionVS.xy / input.PositionVS.z, 1.0f);
#elif DirectionalLight
    // For a directional light we already clamped in the vertex shader
    float3 viewRay = input.ViewRay.xyz;
#endif

// Calculate our projection constants (you should of course do this in the app code, I&#39;m just showing how to do it)
ProjectionA = FarClipDistance / (FarClipDistance - NearClipDistance);
ProjectionB = (-FarClipDistance * NearClipDistance) / (FarClipDistance - NearClipDistance);

// Sample the depth and convert to linear view space Z (assume it gets sampled as
// a floating point value of the range [0,1])
float depth = DepthTexture.Sample(PointSampler, texCoord).x;
float linearDepth = ProjectionB / (depth - ProjectionA);
float3 positionVS = viewRay * linearDepth;
</code></pre><p>It&rsquo;s also possible to use a hardware depth buffer with the first method, if you want to work in an arbitrary coordinate space.  The trick is to project the view ray onto the camera&rsquo;s z axis (AKA the camera&rsquo;s forward vector or lookAt vector), and use that to figure out a proper scaling value.  The light pixel shader goes something like this:</p>
<pre tabindex="0"><code>// Normalize the view ray
float3 viewRay = normalize(input.ViewRay);

// Sample the depth buffer and convert it to linear depth
float depth = DepthTexture.Sample(PointSampler, texCoord).x;
float linearDepth = ProjectionB / (depth - ProjectionA);

// Project the view ray onto the camera&#39;s z-axis
float viewZDist = dot(EyeZAxis, viewRay);

// Scale the view ray by the ratio of the linear z value to the projected view ray
float3 positionWS = CameraPositionWS + viewRay * (linearDepth / viewZDist);
</code></pre><p>Alright, so I think that&rsquo;s about it!  If anything is unclear or if I made any mistakes, go ahead and let me know.  For an actual working sample showing some of these techniques, you can have a look at the sample for my <a href="http://mynameismjp.wordpress.com/2010/03/22/attack-of-the-depth-buffer/">article on depth precision</a>.</p>
<p><em>02/23/1985 - Fixed typo in view space volume reconstruction</em></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://skytiger.wordpress.com" title="corbin@skymo.com">skytiger</a> - <time datetime="2010-11-30 13:12:34">Nov 2, 2010</time></p>
<p>and another: &ndash; Vertex Shader &ndash; Output.Ptrick = Pproj.xyw; &ndash; Pixel Shader &ndash; float4x4 matProj; // standard projection matrix static const float2 Clever = 1.0 / matProj._11_22; Ptrick.xy /= Ptrick.z; Ptrick.xy *= Clever * LinearZ; Ptrick.z = LinearZ;</p>
<hr />
####
[richard osborne](http://www.kreationsedge.com "rosborne255@yahoo.com") - <time datetime="2010-10-04 13:24:47">Oct 1, 2010</time>
<p>Ah, now that makes perfect sense. I&rsquo;ve been sticking with DirectX 9 primarily. I&rsquo;ll have to look into playing around with DirectX 11</p>
<hr />
####
[skytiger](http://skytiger.wordpress.com "corbin@skymo.com") - <time datetime="2010-11-30 12:47:55">Nov 2, 2010</time>
<p>Here is another approach, using VPOS: // Effect Parameters float4x4 matProj; // standard projection matrix float2 fTargetSize; // rendertarget pixel dimensions // Can calculate this on CPU side static const float3x3 matVPosToView = float3x3( 2.0 / fTargetSize.x / matProj._11, 0, 0 , 0, -2.0 / fTargetSize.y / matProj._22, 0 , -1.0 / matProj._11, 1.0 / matProj._22, 1 );//float3x3 // Usage in Pixel Shader: struct PixIn { float2 vpos : VPOS; };//struct float3 Pview; Pview.xy = mul(float3(Input.vpos, 1), matVPosToView).xy; Pview.xy *= LinearZ; Pview.z = LinearZ;</p>
<hr />
####
[When Position-From-Depth Goes Bad &laquo; gpubound](http://gpubound.wordpress.com/2011/01/30/when-position-from-depth-goes-bad/ "") - <time datetime="2011-01-30 11:27:26">Jan 0, 2011</time>
<p>[&hellip;] 0 If you’ve ever had to reconstruct position from depth, you’ve probably come across one of these methods where you send an interpolated view-ray from the vertex shader down to the pixel shader, where you [&hellip;]</p>
<hr />
####
[Me]( "egea.hernando@gmail.com") - <time datetime="2011-01-08 13:20:02">Jan 6, 2011</time>
<p>Thank you very much indeed!! I got deferred lighting working perfectly thanks to your article. I save the normals and linear depth in a 32 BIT FLOAT and I reconstruct thee ViewSpace coordinates from it. I&rsquo;m new in GLSL so it took me some hits of my head against the wall. I like the idea of the deferred lighting for dynamic lights, pretty simple. There are a lot of subtleties, but here come my GLSL shaders: <a href="http://pastebin.com/XmXGx2rz">http://pastebin.com/XmXGx2rz</a> And a screenshot: <a href="http://img824.imageshack.us/i/pantallazovg.png/">http://img824.imageshack.us/i/pantallazovg.png/</a></p>
<hr />
####
[Position From Depth, GLSL Style &laquo; The Danger Zone](http://mynameismjp.wordpress.com/2011/01/08/position-from-depth-glsl-style/ "") - <time datetime="2011-01-08 17:47:49">Jan 6, 2011</time>
<p>[&hellip;] his GLSL implementation of a deferred point light shader, which makes use of one of the methods I previously posted for reconstructing position from depth. So I figured I’d post it here, for all of you [&hellip;]</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2010-12-17 16:39:05">Dec 5, 2010</time>
<p>No, that&rsquo;s correct. You want your linear view-space Z value divided by the distance to your far clipping plane.</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2010-10-04 13:19:58">Oct 1, 2010</time>
<p>Hi Richard, I&rsquo;ve been using D3D11 lately, and that lets you natively access a depth buffer without any vendor hacks through shader resource views and typeless formats. I do actually use that technique in the &ldquo;attack of the depth buffer&rdquo; sample. If you&rsquo;re using the D3D9 vendor hacks (INTZ and friends) the math in the shader code ends up being exactly the same, since those also give you the depth as a [0,1] floating point value.</p>
<hr />
####
[quantumryan](http://ryangogame.blogspot.com/ "quantumryan@gmail.com") - <time datetime="2010-11-22 08:46:49">Nov 1, 2010</time>
<p>I just wanted to say thank you. Although this wasn&rsquo;t exactly what I was looking for, you&rsquo;re figures gave me the ah-ha moment I needed. I wrote a two color depth shader and I dump a stream of images of my 3D scene. I wrote python script to convert pixel data into camera local position data. My trig was all right, but I was using the actual computed depth instead of the projected image depth (the far). Doh! Awesome stuff. I&rsquo;m following your blog now. Keep it up please!</p>
<hr />
####
[richard osborne](http://www.kreationsedge.com "rosborne255@yahoo.com") - <time datetime="2010-10-04 12:51:05">Oct 1, 2010</time>
<p>Hey MJP- just curious how are you getting access to the hardware depth buffer? Are you using driver hacks to access it (for instance, with nvidia cards I thought we had to use a depth texture)? Cant recall if you code sample included with &ldquo;Attack of the Depth Buffer&rdquo; included this</p>
<hr />
####
[Tiago Costa]( "tiago.costav@gmail.com") - <time datetime="2010-09-11 12:19:18">Sep 6, 2010</time>
<p>I calculate the corners following a website that you posted a link to: float Hfar = 2 * tan(fov / 2) * zFar; float Wfar = Hfar * ratio; D3DXVECTOR3 farCenter = mPosition + mLook * zFar; D3DXVECTOR3 farTopLeft = farCenter + (mUp * Hfar/2) - (mRight * Wfar/2); D3DXVECTOR3 farTopRight = farCenter + (mUp * Hfar/2) + (mRight * Wfar/2); D3DXVECTOR3 farDownLeft = farCenter - (mUp * Hfar/2) - (mRight * Wfar/2); D3DXVECTOR3 farDownRight = farCenter - (mUp * Hfar/2) + (mRight * Wfar/2); Can I use this frustum corners to reconstruct pixel position in view-space or world-space?</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2010-09-12 11:37:19">Sep 0, 2010</time>
<p>Hey Tiago, Like I said in the post the frustum corners are only useful if you want to save a little bit of vertex calculation for a full screen quad. You can get the same results just by using the inverse of your projection matrix, which is easier and less error-prone. And since you have only 4 verts per quad, the extra cost is negligable.</p>
<hr />
####
[Tiago Costa]( "tiago.costav@gmail.com") - <time datetime="2010-09-12 13:44:27">Sep 0, 2010</time>
<p>I know that the extra cost in negligible, but as my game engine is GPU bound Im trying to move some calculations to the CPU&hellip;</p>
<hr />
####
[Tiago Costa]( "tiago.costav@gmail.com") - <time datetime="2010-09-11 08:49:30">Sep 6, 2010</time>
<p>Thank you for at least trying to help people that like me have found that reconstruct the pixel position from depth can be really hard&hellip; I&rsquo;m already able to reconstruct the pixel position following your hold posts but the results arent completely correct&hellip; Right now I&rsquo;m doing a major update in my game engine, and once I finish it I will re-implement pixel position reconstruction following this post and maybe it will work correctly this time :D</p>
<hr />
####
[empiricu]( "") - <time datetime="2011-01-02 11:51:15">Jan 0, 2011</time>
<p>I don´t know why the next code not work; vertex_position.z is the z in view-space. float3 eye; IN.coord.x = ( IN.coord.x * 2.0 ) / screeWidth - 1.0; IN.coord.y = ( IN.coord.y * 2.0 ) / screenHeight - 1.0; eye.x = IN.coord.x * ( WFarClip / 2.0 ); eye.y = -IN.coord.y * ( HFarClip / 2.0 ); eye.z = Far; eye = normalize(eye); // normalize viewray // vertex_position.xyz = a * eye.xyz; // a = vertex_position.z / eye.z vertex_position.x = ( vertex_position.z / eye.z ) * eye.x; vertex_position.y = ( vertex_position.z / eye.z ) * eye.y;</p>
<hr />
####
[kaos]( "") - <time datetime="2010-12-17 16:30:48">Dec 5, 2010</time>
<p>output.PositionVS = mul(input.PositionOS, WorldViewMatrix).xyz; output.Depth.x = input.PositionVS.z / FarClipDistance; WorldViewMatrix not is WorldViewProjectionMatrix so I think input.PositionVS.z / FarClipDistance not is correct.</p>
<hr />
####
[]( "") - <time datetime="2012-05-30 03:59:44">May 3, 2012</time>
<p>Multiply your result with the inverse view matrix.</p>
<hr />
####
[lipsryme]( "") - <time datetime="2012-02-24 16:43:51">Feb 5, 2012</time>
<p>Hi, I&rsquo;ve stored my depth in linear view space using viewpos.z / farClip and retrieving viewspace position with the viewRay * Depth but my point lights still move around with the camera and appear/vanish when I get closer/farther away. Normals are in view space. Works flawless when I use z/w depth but I can&rsquo;t get it to work with linear view space depth. Any suggestions ?</p>
<hr />
####
[Pretender]( "") - <time datetime="2011-12-15 11:50:27">Dec 4, 2011</time>
<p>Hi I&rsquo;m in trouble with fullscreen quads worldpos reconstruct. My quad&rsquo;s vertices are in [{-1,-1};{1,1}] and when I transform this position with the invViewProj, the result is wrong. What&rsquo;s the problem? What is the input position&rsquo;s z coordinate? Thanks</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-12-15 12:06:46">Dec 4, 2011</time>
<p>Z and W should both be 1.0 for a fullscreen quad.</p>
<hr />
####
[EO]( "dominicstreeter@hotmail.com") - <time datetime="2014-04-20 18:29:48">Apr 0, 2014</time>
<p>Thank you for your depth tutorials they are a great starting point but I am still working through them, one of the weirdest things is how many different linearisation algorithms I have seen just for openGL: <a href="http://www.geeks3d.com/20091216/geexlab-how-to-visualize-the-depth-buffer-in-glsl/">http://www.geeks3d.com/20091216/geexlab-how-to-visualize-the-depth-buffer-in-glsl/</a> That one worked for me and by extrapolation from your second from last code snippet: projectionMatrixNFLinearScalar.set((far + near) / (far - near), (2 * near) / (far - near)); - gave me: float linearDepth = projNFLinearScalarB / (projNFLinearScalarA - depth); I have no idea why working solution differs so much from the others I have seen, projections and notation being identical&hellip; I am for now baffled.</p>
<hr />
####
[Tyler Wozniak](http://gravatar.com/ttw6741 "ttw6741@rit.edu") - <time datetime="2014-04-07 06:00:40">Apr 1, 2014</time>
<p>So I&rsquo;m currently working on a deferred rendering engine in D (you can find it at <a href="https://github.com/Circular-Studios/Dash">https://github.com/Circular-Studios/Dash</a>, but that&rsquo;s not that important). I&rsquo;m trying to rebuild the Z position using the technique you described in your second-last code sample, with the hardware depth buffer and the two projection constants. However, I was getting a final viewspace position that I had to invert in order to properly calculate the eye-direction for my specular component. So I went to the website you linked here <a href="http://www.sjbaker.org/steve/omniv/love">http://www.sjbaker.org/steve/omniv/love</a>_your_z_buffer.html to check the origin of the projection constants, and solving for Z in the equation there, it seems like the final depth formula should be ProjectionB / ( ProjectionA - depth ), rather than the way you wrote it. Am I missing something here? Switching the two values gave specular highlights that seem correct for me, so it seems like it worked, but I want to make sure I&rsquo;m understanding correctly. Also, thanks for the great tutorials!</p>
<hr />
####
[Performance update with tutorial - Celelej Game Engine](http://celelej.com/performance-update-1/ "") - <time datetime="2015-02-16 06:45:58">Feb 1, 2015</time>
<p>[…] so why don’t peek data from it? Here is very good tutorial how reconstruct position from it: link. When talking about GBuffer itself, I must also say something about normal vectors storage. As I […]</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2014-04-08 13:18:56">Apr 2, 2014</time>
<p>Hi Tyler, I&rsquo;m pretty sure the issue here is just LH vs RH conventions for OpenGL and DX. By convention, in DirectX you usually have your view space coordinate system setup such that Z+ is in front of the camera, whereas in OpenGL most people will set up their projection such that Z- is in front of the camera. Consequently in the projection matrix there&rsquo;s an extra negative for the OpenGL case to make sure that the result ends up being positive, which is why the depth equation is slightly different if you write it out in terms of ProjectionA and ProjectionB. If you don&rsquo;t want to make assumptions about your coordinate system inside your shader code, you should be able to do things directly in terms of your actual projection matrix instead of using the A and B constants: linearZ = Projection._43 / (zw - Projection._33) assuming that you&rsquo;re using a row-major projection matrix.</p>
<hr />
####
[Linear Surface Features on Dynamic Terrain | Phillip Hamlyn](http://philliphamlyn.wordpress.com/2014/04/08/linear-surface-features-on-dynamic-terrain/ "") - <time datetime="2014-04-08 11:45:53">Apr 2, 2014</time>
<p>[…] already been drawn. The maths of this method are covered in the excellent series of articles by MJP – Position From Depth. The basic method […]</p>
<hr />
####
[EO]( "dominicstreeter@hotmail.com") - <time datetime="2014-04-21 07:44:08">Apr 1, 2014</time>
<p>So my solution was this : projectionMatrixNFLinearScalar.set((2 * near) / (far - near), (far + near) / (far - near)); x = 0.000266702, y = 1.0002667 Projection matrix looks like this (near 2 far 15000): 0.95209867 0.0 0.0 0.0 0.0 1.428148 -0.0 0.0 0.0 0.0 -1.0002667 -4.0005336 0.0 0.0 -1.0 0.0 Literal extrapolation of values from projection based on Projection._43 / (zw – Projection._33): projectionMatrixNFLinearScalar.set((projectionMatrix.m32+2*near)*-1, projectionMatrix.m22*-1); The above works as a quick fix, but my first solution yields a marginally different first value. Here is the code used to create the projection: public static void createProjection(Matrix4f projectionMatrix, float fov, float aspect, float znear, float zfar) { float scale = (float) Math.tan((Math.toRadians(fov)) * 0.5f) * znear; float r = aspect * scale; float l = -r; float t = scale; float b = -t; projectionMatrix.m00 = 2 * znear / (r-l); projectionMatrix.m01 = 0; projectionMatrix.m02 = 0; projectionMatrix.m03 = 0; projectionMatrix.m10 = 0; projectionMatrix.m11 = 2 * znear / (t-b); projectionMatrix.m12 = 0; projectionMatrix.m13 = 0; projectionMatrix.m20 = (r + l) / (r-l); projectionMatrix.m21 = (t+b)/(t-b); projectionMatrix.m22 = -(zfar + znear) / (zfar-znear); projectionMatrix.m23 = -1; projectionMatrix.m30 = 0; projectionMatrix.m31 = 0; projectionMatrix.m32 = -2 * zfar * znear / (zfar - znear); projectionMatrix.m33 = 0; } Shader: float linearDepth = projNFLinearScalarA / (projNFLinearScalarB - depth); This has me questioning the legitimacy of my projection matrix, projection works perfectly and I can solve linear depth but I am afraid I may hit further issues down the line trying to solve world space using methods you have shown. Note that I am using right handed notation in openGL, can anyone qualify any flaw in the code above? Thanks, EO</p>
<hr />
####
[Screen Space Glossy Reflections | Roar11.com](http://roar11.com/2015/07/screen-space-glossy-reflections/ "") - <time datetime="2015-08-20 07:19:48">Aug 4, 2015</time>
<p>[…] [6] Matt Pettineo.  https://mynameismjp.wordpress.com/2010/09/05/position-from-depth-3/ […]</p>
<hr />
####
[opengl &#8211; Calculating shadowmap coordinates for cubemap in shading pass? | Asking](http://www.faceyoutub.com/game-development/opengl-calculating-shadowmap-coordinates-for-cubemap-in-shading-pass.html "") - <time datetime="2015-10-10 14:35:10">Oct 6, 2015</time>
<p>[…] use a cube map for shadows you need to recreate the world position of the pixel you are rendering and from that get the normal that points at that world position from […]</p>
<hr />
####
[[Перевод] Learn OpenGL. Урок 5.10 – Screen Space Ambient Occlusion - Новини дня](http://ukr-news.ml/2018/08/%d0%bf%d0%b5%d1%80%d0%b5%d0%b2%d0%be%d0%b4-learn-opengl-%d1%83%d1%80%d0%be%d0%ba-5-10-screen-space-ambient-occlusion "") - <time datetime="2018-08-27 01:55:34">Aug 1, 2018</time>
<p>[…] магии, что описано, например, у Matt Pettineo в блоге. Это, конечно, требующий бОльших затрат на расчеты […]</p>
<hr />
####
[How can I find the pixel space coordinates of a 3D point &#8211; Part 3 &#8211; The Depth Buffer &#8211; Nicolas Bertoa](https://nbertoa.wordpress.com/2017/01/21/how-can-i-find-the-pixel-space-coordinates-of-a-3d-point-part-3-the-depth-buffer/ "") - <time datetime="2017-01-21 13:51:01">Jan 6, 2017</time>
<p>[…] position from depth in deferred shading techniques (Matt Pettineo explains this in detail in this article). Here you have an hlsl code to get Zv from […]</p>
<hr />
####
[DirectX 12 Engine- Updates + Demo &#8211; Nicolas Bertoa](https://nbertoa.wordpress.com/2016/10/28/directx-12-engine-updates-demo/ "") - <time datetime="2016-10-28 01:26:16">Oct 5, 2016</time>
<p>[…] I was creating an additional geometry buffer to store depth in view space. I followed an article to reuse the already existent depth stencil buffer […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Deferred MSAA</title>
			<link>https://therealmjp.github.io/posts/deferred-msaa/</link>
			<pubDate>Mon, 16 Aug 2010 08:57:37 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/deferred-msaa/</guid>
			<description>A long while ago I was looking into morphological antialiasing (MLAA) to see if I could somehow make it practical for a GPU that isn&amp;rsquo;t the latest monster from Nvidia or ATI. With MLAA most people talk about how nicely it cleans up edges (which it certainly does), but for me the really cool part is how it&amp;rsquo;s completely orthogonal to the technique used to render the image. It could have been rasterized and forward rendered, it could be the product of a deferred rendering, or it could even be ray-traced: in all cases the algorithm works the same.</description>
			<content:encoded><![CDATA[<p>A long while ago I was looking into <a href="http://visual-computing.intel-research.net/publications/papers/2009/mlaa/mlaa.pdf">morphological antialiasing</a> (MLAA) to see if I could somehow make it practical for a GPU that isn&rsquo;t the latest monster from Nvidia or ATI. With MLAA most people talk about how nicely it cleans up edges (which it certainly does), but for me the really cool part is how it&rsquo;s completely orthogonal to the technique used to render the image.  It could have been rasterized and forward rendered, it could be the product of a deferred rendering, or it could even be ray-traced: in all cases the algorithm works the same.  This seems pretty important to me, since in real-time rendering we&rsquo;re getting further and further away from the old days of forward rendering to the backbuffer (AKA, the case where MSAA actually works properly).</p>
<p>Anyway I never really got anywhere (although someone took a <a href="http://igm.univ-mlv.fr/~biri/mlaa-gpu/MLAAGPU.pdf">stab at it</a> in the meantime), but what <em>DID</em> happen is that I spent some time thinking about the downsides of MLAA and how they could be avoided.  In particular, I&rsquo;m talking about the inability of MLAA to handle temporal changes to polygon edges due to no sub-pixel information being available.  MSAA doesn&rsquo;t have the same problem, since it at handles depth testing and triangle coverage at a sub-pixel level.  So I thought to myself: &ldquo;wouldn&rsquo;t it be nice to use MSAA to get coverage info, and then use that to some smart filtering in a post process?&rdquo;  Turns out it&rsquo;s not too difficult to pull it off, and the results aren&rsquo;t too bad.  I&rsquo;m calling my sample &ldquo;Deferred MSAA&rdquo; even though that&rsquo;s a terrible name for it, but I can&rsquo;t really think of anything better. Here&rsquo;s the basic premise:</p>
<ol start="2">
<li>
<p>Render all scene geometry in a depth-only prepass, with 4xMSAA enabled</p>
</li>
<li>
<p>Render the scene normally with no MSAA to an HDR render target</p>
</li>
<li>
<p>Apply tone mapping and other post processes</p>
</li>
<li>
<p>Apply a single &ldquo;deferred AA&rdquo; pass that samples the MSAA depth buffer from step #1, uses it to calculate pixel coverage, and filters based on the coverage</p>
</li>
</ol>
<p>To &ldquo;calculate pixel coverage&rdquo;, all I do is read all 4 depth subsamples and compare them (with a small tolerance) with the depth value from the non-MSAA pass.  The idea is to try to figure out which depth subsamples came from rasterizing the same triangle that was rasterized in the non-MSAA pass.  Then, using knowledge of the points in the sample pattern, I come up with a sample location where it&rsquo;s likely that we&rsquo;ll a color on the other side of the triangle edge.  The method I use to do this is really simple: if a subsample fails the equivalence test, add a vector from the pixel center to the sample location to a summed offset vector. Then after all subsamples are compared, average the offsets and take a single bilinear sample at that offset.  Check out the diagram below for a visual representation:</p>
<p><a href="/images/converted/deferred-msaa/offset.png"><img src="/images/converted/deferred-msaa/offset.png" alt="" title="Offset"></a></p>
<p>As I see it, this approach has these things going for it:</p>
<ol start="2">
<li>
<p>Like with MLAA, AA no longer has anything to do with how you shade a pixel. So if your going deferred, there&rsquo;s no &ldquo;lighting the subsamples&rdquo; nonsense.  It also means you don&rsquo;t necessarily need to render your G-Buffer with MSAA, which can save a lot in terms of memory usage and bandwidth.</p>
</li>
<li>
<p>Also like MLAA, AA is applied <em>after</em> tone mapping. So you don&rsquo;t have to <a href="http://www.humus.name/index.php?page=3D&amp;ID=77">tone map each subsample</a> to avoid aliasing where there&rsquo;s high contrast.</p>
</li>
<li>
<p>Unlike MLAA, it&rsquo;s simple and straightforward to implement on a GPU in a pixel shader.</p>
</li>
<li>
<p>Unlike MLAA, it uses sub-pixel information which helps it handle temporal changes a lot better.</p>
</li>
</ol>
<p>For comparison, here&rsquo;s a screenshot from my sample app (click for full size):</p>
<p><a href="/images/converted/deferred-msaa/comparison.png"><img src="/images/converted/deferred-msaa/comparison.png" alt="" title="Comparison"></a></p>
<p>That particular shot shows off the weakness of both standard MSAA and my approach.  If you look at the high-contrast area whether the tank overlaps with the bright sky, you can see how standard MSAA totally fails to do anything about the edge aliasing.  This is because the MSAA resolve is done on the HDR pixel values, and tone mapping is a non-linear operation.  However in the darker areas towards the bottom MSAA does just fine.  In contrast (pun not intended), deferred MSAA holds up really well in the silhouettes. The AA quality is surprisingly pretty decent for a edge filtering-based approach, and since it&rsquo;s done after tone mapping there&rsquo;s no non-linearity to worry about. However if you look carefully down in the tank, you&rsquo;ll spot edges that don&rsquo;t get the AA treatment at all. This is because the depth values are similar enough to pass the comparison test, despite coming from different triangles.  You can see it most clearly in the grate on the front of the tank.  The sample app has a tweakable threshold value for the depth comparison, and setting it lower causes more edges to be properly distinguished. However it also starts to cause false positives in triangles nearly parallel to the camera.  The image below shows the same scene with a default threshold, and with a low threshold:</p>
<p><a href="/images/converted/deferred-msaa/threshold.png"><img src="/images/converted/deferred-msaa/threshold.png" alt="" title="Threshold"></a></p>
<p>Even with a low threshold, some edges are still missed since only the normal or material properties will change between two triangles and not the depth.  For those cases more information would be required, such as the scene normals or an ID buffer.  But you could probably make the case that edges with depth discontinuities are more likely to have high contrast, and the high-contrast areas are where AA makes the biggest difference.</p>
<p>Aside from the quality issues, the other obvious concern with this approach is performance.  Even with depth only, an MSAA prepass isn&rsquo;t going to be cheap on a lot of hardware.  It makes more sense if the depth prepass isn&rsquo;t wasted and only used for the post process&hellip;for instance it&rsquo;s certainly possible to do downscale the MSAA depth buffer with a MAX operation and use it to pre-populate the non-MSAA depth buffer.  However it may be hard to get this to play nice with early z-cull, depending on the hardware.  You could also possibly lay out your G-Buffer for light prepass with MSAA enabled, but then you might have trouble using depth values to reconstruct position.</p>
<p>You can get the sample code and binaries here: <a href="https://mynameismjp.files.wordpress.com/2016/11/dmsaa1.zip">https://mynameismjp.files.wordpress.com/2016/11/dmsaa1.zip</a>.  This one requires a D3D_FEATURE_LEVEL_11_0-capable device.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22maxest@gct-game.net%22">maxest</a> - <time datetime="2011-08-03 11:16:02">Aug 3, 2011</time></p>
<p>One question: what does 4xMSAA mean? In DX9 you don&rsquo;t have access to subsamples, right? So by 4xMSAA you simply mean creating a 4x bigger 1-sample render target?</p>
<hr />
####
[directtovideo](http://directtovideo.wordpress.com "smaash@yahoo.com") - <time datetime="2010-08-16 02:47:26">Aug 1, 2010</time>
<p>This is actually quite similar actually to what I did here: <a href="http://directtovideo.wordpress.com/2009/11/13/deferred-rendering-in-frameranger/">http://directtovideo.wordpress.com/2009/11/13/deferred-rendering-in-frameranger/</a> (see the end of the post) I do this: - render the gbuffers to non-MSAA, light them and composite them and so on. - render a pass to a MSAA RGBA8 colour target. pass up depth, normal, object ID and material ID from the vertex shader. In the fragment shader, read the GBuffer depth+normals and calculate weights, and use these weights to perform a bilateral upsample of the colour buffer. - finally resolve that MSAA colour buffer with a stretchrect. As an optimisation - perform an edge detect pass on the gbuffers to lay down stencil to mask out pixels in the MSAA pass. The result is pretty nice actually, and it works on DX9. As you said, it&rsquo;s a lot nicer if you use normal and ID information too - depth doesnt give you great results. Normal is probably the most important feature.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-08-31 22:04:37">Aug 3, 2016</time>
<p>Sorry about that, I re-uploaded the file and updated the link.</p>
<hr />
####
[]( "") - <time datetime="2016-08-30 03:55:57">Aug 2, 2016</time>
<p>links dead :(</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>MSAA Sample Pattern Detector</title>
			<link>https://therealmjp.github.io/posts/msaa-sample-pattern-detector/</link>
			<pubDate>Wed, 07 Jul 2010 08:42:23 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/msaa-sample-pattern-detector/</guid>
			<description>Recently I&amp;rsquo;ve been experimenting with AA techniques, and one of the avenues I was pursuing required me to read back subsamples and use them to compute coverage. However I quickly ran into the problem that I didn&amp;rsquo;t know the sample position for a given subsample index. With FEATURE_LEVEL_10_1 and FEATURE_LEVEL_11 there are standard MSAA patterns you can use, but unfortunately I&amp;rsquo;m still stuck on a 10-level GPU so that wasn&amp;rsquo;t an option.</description>
			<content:encoded><![CDATA[<p><a href="/images/converted/msaa-sample-pattern-detector/samplepattern1.png"><img src="/images/converted/msaa-sample-pattern-detector/samplepattern1.png" alt="" title="SamplePattern"></a></p>
<p>Recently I&rsquo;ve been experimenting with AA techniques, and one of the avenues I was pursuing required me to read back subsamples and use them to compute coverage.  However I quickly ran into the problem that I didn&rsquo;t know the sample position for a given subsample index.  With FEATURE_LEVEL_10_1 and FEATURE_LEVEL_11 there are standard MSAA patterns you can use, but unfortunately I&rsquo;m still stuck on a 10-level GPU so that wasn&rsquo;t an option.  After some thinking I realized that it wouldn&rsquo;t be too hard to write an app that could determine the sample positions within a reasonable degree of accuracy, so that&rsquo;s what I did!  The basic process is like this:</p>
<ol>
<li>Create a 1x1 render target with the desired MSAA sample count + quality</li>
<li>Render a grid of sub-pixel quads onto the render target (I used 25x25). The output of each quad is just the position of the quad in pixel space.</li>
<li>Render N pixel-sized quads onto an Nx1 non-MSAA render target, where N is the number of subsamples. For each quad, a pixel shader samples one of the subsamples from the MSAA render target and outputs the value.</li>
<li>Copy the Nx1 render target to a staging texture , and then map it to retrieve the contents on the CPU. Each texel is the XY position of the subsample.</li>
</ol>
<p>It was a piece of cake to implement, and it seems to actually work! So I added a super-sweet visualizer (as you can see in the picture above), and packaged it up so that you guys can enjoy it as well.  Download it here: <a href="https://mynameismjp.files.wordpress.com/2014/06/samplepattern.zip">http://mynameismjp.files.wordpress.com/2014/06/samplepattern.zip</a> (includes source and binaries, requires a DX10-capable GPU).</p>
<p><strong>Update 9/13/2015:</strong> I updated this project while working on a blog post about programmable sample points, and uploaded it to <a href="https://github.com/TheRealMJP/SamplePattern">GitHub</a>. The new version has more accurate sample point detection, and displays the positions using the conventions established by the D3D Sample Coordinate System. It also can optionally link to NVAPI, in which case it can show a demonstration of how to setup programmable sample points that vary across a 2x2 quad of pixels.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22castano@gmail.com%22">castano</a> - <time datetime="2010-07-07 09:31:52">Jul 3, 2010</time></p>
<p>You could achieve the same thing in OpenGL with the explicit_multisample extension: <a href="http://www.opengl.org/registry/specs/NV/explicit">http://www.opengl.org/registry/specs/NV/explicit</a>_multisample.txt float pos[2]; glGetMultisamplefvNV(GL_SAMPLE_POSITION, index, pos);</p>
<hr />
####
[James Bird](http://www.jb101.co.uk/ "james.anthony.bird@gmail.com") - <time datetime="2010-08-04 01:54:17">Aug 3, 2010</time>
<p>I have also recently been experimenting with anti-aliassing techniques on DirectX 10. At the 2009 GDC, there was a talk called the &ldquo;A to Z of DX10 Performance&rdquo;. On the topic of &ldquo;Resolving MSAA Buffers&rdquo;, the slides advise not to create the back buffer with MSAA, but I cannot figure out why. I can only assume that this is so that the graphics programmer has explicit control over when they perform an MSAA resolve (as this operation isn&rsquo;t free), but I suspect there could be other reasons. I was wondering if you knew anything about this, and if so, if you could share your thoughts?</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2010-08-05 10:00:26">Aug 4, 2010</time>
<p>There&rsquo;s certainly a lot of cases where you might want to have a custom MSAA resolve&hellip;for instance if you use HDR you can get significantly better results if you tonemap the subsamples individually. However I think they&rsquo;re saying that because pretty much any DX10 game is going to be doing some sort of post-processing, and thus will want to render their geometry to a render target texture and not the back buffer. In that case there&rsquo;s no reason to make the back buffer MSAA, since you only need MSAA when rendering scene geometry.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2014-06-01 02:01:39">Jun 0, 2014</time>
<p>I re-hosted the zip file here at wordpress, and updated the link.</p>
<hr />
####
[]( "") - <time datetime="2014-07-24 03:06:25">Jul 4, 2014</time>
<p>Thanks! Great tool =)</p>
<hr />
####
[Stairway To (Programmable Sample Point) Heaven | The Danger Zone](https://mynameismjp.wordpress.com/2015/09/13/programmable-sample-points/ "") - <time datetime="2015-09-13 19:31:44">Sep 0, 2015</time>
<p>[…] the samples would be located. Without that, you either had to use shader intrinsics or resort to wackier methods for figuring out the sample positions. In theory it also gives you a very limited ability to […]</p>
<hr />
####
[somemail256](http://ndotl.wordpress.com "somemail256@gmail.com") - <time datetime="2014-05-31 08:44:06">May 6, 2014</time>
<p>The link says &ldquo;service unavailable&rdquo;&hellip; can you please reupload it?</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>A Closer Look At Tone Mapping</title>
			<link>https://therealmjp.github.io/posts/a-closer-look-at-tone-mapping/</link>
			<pubDate>Fri, 30 Apr 2010 08:47:17 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/a-closer-look-at-tone-mapping/</guid>
			<description>A few months ago my coworker showed me some slides from a presentation by tri-Ace regarding their game &amp;ldquo;Star Ocean 4&amp;rdquo;. The slides that really caught my eye were pages 90 to 96, where they discussed their approach to tone mapping. Instead of using the standard Reinhard tone mapping operator that everybody is so fond of, they decided to instead use curves based on actual specifications from different film types and CMOS sensors.</description>
			<content:encoded><![CDATA[<p>A few months ago my coworker showed me some slides from a <a href="http://research.tri-ace.com/Data/SO4_flexible_shader_managment_and_postprocessing.ppt">presentation</a> by tri-Ace regarding their game &ldquo;Star Ocean 4&rdquo;.  The slides that really caught my eye were pages 90 to 96, where they discussed their approach to tone mapping. Instead of using the standard Reinhard tone mapping operator that everybody is so fond of, they decided to instead use curves based on actual specifications from different film types and CMOS sensors. This not only produced some really nice results (the screenshots in the slides speak for themselves), but it fit very nicely into their &ldquo;virtual camera&rdquo; approach towards post processing.  While I was intrigued by their approach, it wasn&rsquo;t until I read through <a href="http://johnhable.com/">John Hable</a>&rsquo;s recent <a href="http://www.gdcvault.com/play/1012459/Uncharted_2__HDR_Lighting">presentation</a> on gamma and HDR lighting that I decided to start doing my own research.  His presentation gave an overview of Uncharted 2&rsquo;s approach to tone mapping, which (like Star Ocean 4) eschews Reinhard&rsquo;s operator in favor of mimicking a filmic response curve. Once again the images in the slides speak for themselves, and they intrigued me enough to make me dig deeper.</p>
<p>Like always, I started off by making a test application that would let me try out different approaches and observe their results. Initially my app started out with approach taken by pretty much <a href="http://developer.download.nvidia.com/SDK/10.5/direct3d/samples.html#HDRRendering">every</a> <a href="http://msdn.microsoft.com/en-us/library/ee417769%28VS.85%29.aspx">other</a> <a href="http://mynameismjp.wordpress.com/samples-tutorials-tools/xna-hdr/">HDR sample</a> out there: render a model and a skybox to a floating-point texture, calculate the log luminance of the scene and repeatedly downsample to determine a single log-average luminance value, and then use that value in Reinhard&rsquo;s tone mapping equations to scale pixel values down to the visible range (if you&rsquo;re not familar, this &ldquo;standard&rdquo; approach is outlined in detail <a href="http://wiki.gamedev.net/index.php/D3DBook:High-Dynamic_Range_Rendering">here</a>). At this point I thought I would just copy over Hable&rsquo;s equations and I would have something nice&hellip;but after some ugly results I realized I needed to take a step back and rethink the process a bit. After some experimentation and  a bit of reading through <a href="http://www.amazon.com/High-Dynamic-Range-Imaging-Acquisition/dp/0125852630">High Dynamic Range Imaging</a>, I started to think of the whole process in terms of a more generalized approach:</p>
<p>1. Run a (simplified) light transport simulation, and determine the amount of incoming light energy for each pixel. This is done by rendering all objects in the scene, and determining the energy reflected off an object&rsquo;s surface towards the camera/eye. Ideally for this step we would use radiometric units (radiance/irradiance) to represent light intensity and we would also maintain the distribution of that energy across the entire visible spectrum, but to actually make this feasible on graphics hardware we run the simulation for 3 discrete wavelengths (red, green, and blue).  In my app, this step is performed by rendering a single mesh and sampling an HDR environment map to determine the amount of light reflected off the surface.  For a background the environment is sampled directly by a skybox.</p>
<p>2. Scale the incoming light to determine the amount that would hit the film/sensor/retina. This step is referred to as &ldquo;calibration.&rdquo;  One possible way to implement this stuff is to model a camera, where the total amount of light that hits the film is affected by the focal length of the lens, the aperture size(f-number), and the shutter speed. Together they can be manipulated to scale range of incoming light intensities such that the important parts of the scene are neither under-exposed nor over-exposed. In my app I kept things simple, and exposed three different methods for calibration:</p>
<ul>
<li>
<p>Manual exposure: a slider lets you choose values between -10 to 10. The HDR pixel value is then scaled by 2^exposure.</p>
</li>
<li>
<p>Geometric mean of luminance: this is pretty much the exact approach outlined in Reinhard&rsquo;s paper, where the geometric mean (log average) of scene luminance is calculated and used to scale the luminance of each pixel. With this approach a &ldquo;key value&rdquo; is user-controlled, and is meant to be chosen based on whether the scene is &ldquo;high-key&rdquo; (bright, low contrast) or &ldquo;low-key&rdquo; (dark, high contrast).</p>
</li>
<li>
<p>Geometric mean, auto key value: Same as above, except that the  key value is automatically chosen using Equation 10 from <a href="http://wiki.gamedev.net/index.php/D3DBook:High-Dynamic_Range_Rendering">this page</a>.</p>
</li>
</ul>
<p>To calculate the geometric mean, I simply calculate the log of luminance and write the results to a 1024x1024 texture. I then call GenerateMips to automatically generate the full mip-map chain. At that point I can apply exp() to the last mip level to get a full log-average of the scene. One extra trick I added to my app was a slider that let you choose the mip level that would be sampled when scaling the pixel intensities. Doing this allows you to essentially use local averages rather than a global average, which lets you have different exposure values for different parts of the image.  In my app, there&rsquo;s a display below the tone curve that shows the average luminance value being used for each part of the image.</p>
<p>3. Map calibrated light intensities to display values by applying a tone curve to either RGB values or luminance values. This curve can have a significant impact on not only which details are visible in the final image, but also the overall visual characteristics. Because of this I find it difficult selecting the right curve for a particular scene&hellip;in some cases you can pretty objectively determine that one curve is better than another at making details visible, but at the same time some curves will subjectively look better to my eyes due to their resulting  levels of contrast and saturation.  My app offers a variety of curves to choose from, including:</p>
<ul>
<li>Linear</li>
<li>Logarithmic</li>
<li>Exponential</li>
<li><a href="http://www.mpi-inf.mpg.de/resources/tmo/logmap/logmap.pdf">Drago&rsquo;s Adaptive Logarithmic</a></li>
<li><a href="http://www.cs.utah.edu/~reinhard/cdrom/tonemap.pdf">Reinhard</a> (Equation 3)</li>
<li><a href="http://www.cs.utah.edu/~reinhard/cdrom/tonemap.pdf">Modified Reinhard</a> (Equation 4)</li>
<li>Filmic (Haarm-Pieter Duiker&rsquo;s curve, using the ALU-only version from Hable&rsquo;s presentation)</li>
<li>Uncharted 2 (customizable filmic curve)</li>
</ul>
<p>Now for the exciting part: pictures! For this first set of shots, I used an an HDR environment map taken from the Ennis House. I liked this map because it gave a great test case for detail preservation: a mostly-dark room, with an extremely bright window through which a landscape is visible. For reference, this is what the shot looks like with no exposure or tone mapping applied:</p>
<p><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-manual.png" alt=""></p>
<p>Here&rsquo;s what the shot looks like for each tone mapping curve, with &ldquo;auto-exposure&rdquo; applied using a global geometric mean:</p>
<p><a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-2.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-2_resized_300.png" alt=""></a><a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-3.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-3_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-4.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-4_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-5.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-5_resized_300.png" alt=""></a></p>
<p><a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-6.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-6_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-7.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-7_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-8.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-8_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-1.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-1_resized_300.png" alt=""></a></p>
<p>Both Drago and Reinhard look pretty decent in this case, while with filmic you pretty much lose everything in the darks and in the brights. The Uncharted 2 curve doesn&rsquo;t have such a strong toe so the blacks aren&rsquo;t crushed, and the contrast is a bit better than in Reinhard. But you do lose the coloring in the sky with both filmic curves, since those curves are applied to the RGB channels which means color ratios aren&rsquo;t preserved like they are when you tone map luminance.  However I think the sky looks rather unnatural in Drago and Reinhard, despite the colors being preserved.</p>
<p>For this next set, I sampled the 9th mip level which essentially gives you a 2x2 grid of local luminance averages. This essentially applies a higher exposure to the left portion of the image, and lower exposure to the right portion.</p>
<p><a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-2.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-2_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-3.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-3_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-4.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-4_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-5.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-5_resized_300.png" alt=""></a></p>
<p><a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-6.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-6_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-7.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-7_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-8.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-8_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-1.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-auto-mip-1_resized_300.png" alt=""></a></p>
<p>Using local averages works pretty well for the filmic techniques. Areas that used to be underexposed or overexposed now clearly show more detail, and the overall the image has a nice level of contrast and saturation. Reinhard and Drago, on the other hand, look more washed-put than they did previously.</p>
<p>Here&rsquo;s some other assorted screenshots I took using other environment maps, and with bloom enabled:</p>
<p><a href="/images/converted/a-closer-look-at-tone-mapping/ennis-misc-2-2.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-misc-2-2_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-misc-2-3.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-misc-2-3_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-misc-2-4.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-misc-2-4_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/ennis-misc-2-1.png"><img src="/images/converted/a-closer-look-at-tone-mapping/ennis-misc-2-1_resized_300.png" alt=""></a></p>
<p><a href="/images/converted/a-closer-look-at-tone-mapping/grace-2.png"><img src="/images/converted/a-closer-look-at-tone-mapping/grace-2_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/grace-3.png"><img src="/images/converted/a-closer-look-at-tone-mapping/grace-3_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/grace-4.png"><img src="/images/converted/a-closer-look-at-tone-mapping/grace-4_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/grace-1.png"><img src="/images/converted/a-closer-look-at-tone-mapping/grace-1_resized_300.png" alt=""></a></p>
<p><a href="/images/converted/a-closer-look-at-tone-mapping/doge-2.png"><img src="/images/converted/a-closer-look-at-tone-mapping/doge-2_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/doge-3.png"><img src="/images/converted/a-closer-look-at-tone-mapping/doge-3_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/doge-4.png"><img src="/images/converted/a-closer-look-at-tone-mapping/doge-4_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/doge-1.png"><img src="/images/converted/a-closer-look-at-tone-mapping/doge-1_resized_300.png" alt=""></a></p>
<p><a href="/images/converted/a-closer-look-at-tone-mapping/glacier-2.png"><img src="/images/converted/a-closer-look-at-tone-mapping/glacier-2_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/glacier-3.png"><img src="/images/converted/a-closer-look-at-tone-mapping/glacier-3_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/glacier-4.png"><img src="/images/converted/a-closer-look-at-tone-mapping/glacier-4_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/glacier-1.png"><img src="/images/converted/a-closer-look-at-tone-mapping/glacier-1_resized_300.png" alt=""></a></p>
<p><a href="/images/converted/a-closer-look-at-tone-mapping/pisa-2.png"><img src="/images/converted/a-closer-look-at-tone-mapping/pisa-2_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/pisa-3.png"><img src="/images/converted/a-closer-look-at-tone-mapping/pisa-3_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/pisa-4.png"><img src="/images/converted/a-closer-look-at-tone-mapping/pisa-4_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/pisa-1.png"><img src="/images/converted/a-closer-look-at-tone-mapping/pisa-1_resized_300.png" alt=""></a></p>
<p><a href="/images/converted/a-closer-look-at-tone-mapping/uffizi-2.png"><img src="/images/converted/a-closer-look-at-tone-mapping/uffizi-2_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/uffizi-3.png"><img src="/images/converted/a-closer-look-at-tone-mapping/uffizi-3_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/uffizi-4.png"><img src="/images/converted/a-closer-look-at-tone-mapping/uffizi-4_resized_300.png" alt=""></a> <a href="/images/converted/a-closer-look-at-tone-mapping/uffizi-1.png"><img src="/images/converted/a-closer-look-at-tone-mapping/uffizi-1_resized_300.png" alt=""></a></p>
<p>Overall I like the look of the filmic curves. It might just be that I watch too many movies and I&rsquo;m used to that kind of look, but I just think the image looks more natural. I&rsquo;m sure plenty of people would disagree with me though, especially since Reinhard and Drago are much better at preserving details across a wide range of intensities.</p>
<p>If you&rsquo;d like to play around with the app itself, I&rsquo;ve uploaded the code, content, binaries, and VS2010 project here:</p>
<p><a href="http://cid-538e432ea49f5bde.skydrive.live.com/self.aspx/Public/Samples%20And%20Tutorials/DX11/ToneMapping.part01.rar">ToneMapping.part01.rar</a>
<a href="http://cid-538e432ea49f5bde.skydrive.live.com/self.aspx/Public/Samples%20And%20Tutorials/DX11/ToneMapping.part02.rar">ToneMapping.part02.rar</a>
<a href="http://cid-538e432ea49f5bde.skydrive.live.com/self.aspx/Public/Samples%20And%20Tutorials/DX11/ToneMapping.part03.rar">ToneMapping.part03.rar</a></p>
<p>Sorry about it being in 3 parts&hellip;together they total 174MB and SkyDrive has a 50MB limit per file. If you&rsquo;re wondering why the app is so big, it&rsquo;s because I ran the HDR environment maps through ATI&rsquo;s CubeMapGen to generate some really nice mipmaps (it does proper angular extent filtering so that there&rsquo;s no seams in lower mip levels) and that app can only save HDR cube maps in uncompressed floating point formats. But on the upside they have really nice mips&hellip;in fact I use a low mip level for faking diffuse lighting on the mesh.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://www.realtimerendering.com" title="">Naty Hoffman</a> - <time datetime="2010-04-30 07:43:19">Apr 5, 2010</time></p>
<p>For games, the goal is not academic &ldquo;tone mapping&rdquo; (preserve as much detail as possible) but rather &ldquo;image rendition&rdquo;; to get an output image that evokes a similar or creatively enhanced perceptual response as the original scene would in an observer, for the salient details. For film &ldquo;salient details&rdquo; and &ldquo;similar&rdquo; are in the eyes of the cinematographer; &ldquo;creatively enhanced&rdquo; is defined by the cinematographer and director. In games these roles are played by the lighting artist and art director. The S-shaped tone curve of film stock has been carefully engineered over almost two centuries (!) to achieve this result (in combination with appropriate lighting); games could do far worse than to model their image rendition on film.</p>
<hr />
####
[John Hable](http://filmicgames.com "") - <time datetime="2010-05-03 21:00:41">May 1, 2010</time>
<p>Nice post! I like the trick of multiplying by the 2x2 mipmap level. For your Reinhard, I assume you are converting RGB to HSL, applying Reinhard to luminance, and converting back to RGB. It&rsquo;s great to see a good discussion about tonemapping. My take is that I don&rsquo;t care which one you use as long as you have a good reason for doing it.</p>
<hr />
####
[Rim](http://xnainfo.com "") - <time datetime="2010-05-03 22:41:38">May 1, 2010</time>
<p>&gt; However I think the sky looks rather unnatural in Drago &gt; and Reinhard, despite the colors being preserved. It&rsquo;s all subjective of course, but I actually do like the effect. The sky colors aren&rsquo;t that pronounced when you naturally focus on the statue, but when &rsquo;looking out the window&rsquo; seeing the colorful sky gives a sense of depth filmic seems to lack. Overall though I do like the crisp look of filmic better. I&rsquo;d wish I&rsquo;d have something useful to contribute technically, but alas I only have my opinion to share :)</p>
<hr />
####
[Brad Roepstorff]( "tostador@gmail.com") - <time datetime="2010-05-01 20:20:52">May 6, 2010</time>
<p>Just worked on this very thing a few weeks back. Yeah, the differences between these curves (at least for games) can be subtle and very scene dependent. And the &ldquo;quality&rdquo; is definitely subjective. And it&rsquo;s kind of a wash if you apply some form of artist-driven color correction to the image after the HDR tone mapping (e.g. <a href="http://http.developer.nvidia.com/GPUGems/gpugems">http://http.developer.nvidia.com/GPUGems/gpugems</a>_ch22.html). We were left kind of scratching our heads which was the best, so in the end we just went with whatever was fastest (a cheap polynomial approximation to Hable&rsquo;s filmic curve), and let the artists tweak saturation and contrast manually via photoshop color curves.</p>
<hr />
####
[-]( "") - <time datetime="2010-05-02 11:10:05">May 0, 2010</time>
<p>@Naty, I don&rsquo;t understand why you wouldn&rsquo;t want to try and preserve as much detail as possible? I would imagine this is the direction you want to go, 1) Simulate radiance in a scene. 2) Tone map the radiance to give the most similar representation of the human visual system. This includes preserving details, and retaining correct contrast ratios. 3) Finally, allow the art director/lighting artist to tweak the contrast ratios, saturation levels, smash the darks, blow out the whites, change the hue, etc&hellip; The last step is where the creativity comes in to play and can define the artistic look for the game. The S-shaped tone curve was engineered under the limitations that cameras couldn&rsquo;t record high dynamic ranges. Modern cameras are just beginning to record higher ranges in brightness and I suspect these &lsquo;academic&rsquo; methods will be incorporated into modern film. Games dynamic range is only limited by the encoding methods of our textures, back buffer, and bandwidth.</p>
<hr />
####
[defaultex](http://defaultex.wordpress.com "default_ex@live.com") - <time datetime="2010-05-05 11:29:25">May 3, 2010</time>
<p>Thanks for putting in the work on showing all three under such controlled situation. I&rsquo;ve been trying to decide which method I liked enough to implement, while I still haven&rsquo;t decided fully I do really like that adjustable filmic curve, I could see artist having fun with it an churning out some really pretty scenes.</p>
<hr />
####
[Blog added &#8211; The Danger Zone &laquo; GameDevBlogs](http://www.gamedevblogs.net/2010/07/blog-added-the-danger-zone/ "") - <time datetime="2010-07-29 23:51:19">Jul 4, 2010</time>
<p>[&hellip;] blogs, the site is heavy on tutorials and samples, and has some very in-depth articles (such as this one about tone mapping), so it will no doubt prove very useful to people interested in that [&hellip;]</p>
<hr />
####
[Balkan Forest](http://balkanforest.com "contact@balkanforest.com") - <time datetime="2010-08-23 03:54:30">Aug 1, 2010</time>
<p>Nice post, very good information, thanks.</p>
<hr />
####
[Ruud van Gaal](http://www.racer.nl "ruud@racer.nl") - <time datetime="2010-08-26 04:15:49">Aug 4, 2010</time>
<p>Interesting. I did my own downsampling and it turned out to be slightly faster than using glGenerateMipmap(). Haven&rsquo;t touched too many tonemapping tuning yet though; such a world of lighting info to read up on&hellip;</p>
<hr />
####
[José Schneider](http://www.facebook.com/jischneider "jischneider@hotmail.com") - <time datetime="2012-04-19 05:28:00">Apr 4, 2012</time>
<p>I want to improve the tau value calculation to contemplate rod and cones adaptation velocity. <a href="http://www.mpi-inf.mpg.de/resources/hdr/peffects/krawczyk05sccg.pdf">http://www.mpi-inf.mpg.de/resources/hdr/peffects/krawczyk05sccg.pdf</a> said that a value of 0.4 sec for rods and 0.1 sec for cones are good values. But knowing the proportion of the two values using a non-calibrated average luminance is tricky. Rods work between 10^-6 and 10^1 cd/m2 and cones work between 10^-1 and 10^8 cd/m2. But, what is the estimated unit of the average luminance? I’m not talking about a precise conversion, it is almost impossible without calibrated data and physical correct materials. But what you will do? Using a slider to control the range? or directly assuming that an average luminance value of 1 equals… x candelas?</p>
<hr />
####
[José Schneider](http://www.facebook.com/jischneider "jischneider@hotmail.com") - <time datetime="2012-04-24 10:24:22">Apr 2, 2012</time>
<p>Matt, I think I find an error in the Drago operator. You write: toneMappedLuminance /= log10(2 + 8 * ((pixelLuminance / WhiteLevel) * log10(Bias) / log10(0.5f))); But it has to be: toneMappedLuminance /= log10(2 + 8 * (pow((pixelLuminance / WhiteLevel), log10(Bias) / log10(0.5f)))); Because the Perlin and Hoffert’s bias function is defined as: biasp(x) = pow(x, log(p)/ log(0.5)) Any ideas about my previous question?</p>
<hr />
####
[Remko]( "remko@act-3d.com") - <time datetime="2012-03-19 14:50:59">Mar 1, 2012</time>
<p>You&rsquo;re right! It&rsquo;s the strange order of operators that threw me off. The local adaptation does not really work that well because it just ends up as ghosting. Maybe it can work for scenes without moving objects. I wonder if it is even possible to have slowly adapting local exposures. Probably it will always end in ghosting. A single exposure value for the entire screen seems to work best. Another nice addition to the curves you tried might be actual real-world curves. I looked up some film curves from Kodakchrome 25 and other film types and approximated their curves with a polynomal. The process is really simple. I just used an online least squares approximations utility to convert tables of values to functions. Of course the easiest way is to use a lookup texture. Actual film curves do not only change the luminance but they also add some style because they warp the colors.</p>
<hr />
####
[Remko]( "remko@act-3d.com") - <time datetime="2012-03-19 12:32:58">Mar 1, 2012</time>
<p>There&rsquo;s a small error in the code. Log10 is used to generate the average log of the image. To get back to a value exp() is used which has a base of e and not 10. It does not matter which base you use as long as the are the same: you can: use log10(x) for downsampling and pow(10,x) to get the geometric mean or: use log(x) for downsampling and exp(x) to get the geometric mean</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2012-03-19 13:08:31">Mar 1, 2012</time>
<p>Hi Remko, The sample doesn&rsquo;t use log10 for computing the geometric mean. The log is performed at the end of the &ldquo;AdaptLuminance&rdquo; pixel shader, which adapts the luminance value based on the luminance of the previous frame. Usually you do the adaptation after you&rsquo;ve finished computing the geometric mean, but for this sample I did the adaptation first at full resolution so I could try out my wacky idea of using localized geometric means for adaptive auto-exposure. In all of my other samples I do it the &ldquo;normal&rdquo; way and immediately take the log of luminance right when I calculate it per-pixel. The only log10&rsquo;s in the shader code are either in tone mapping operators (logarithmic and Drago logarithmic), or in the bit of code that attempts to guess at a good key value to use for auto exposure.</p>
<hr />
####
[Readings on color management | Light is beautiful](http://lousodrome.net/blog/light/2012/09/11/readings-on-color-management/ "") - <time datetime="2012-09-10 21:55:48">Sep 1, 2012</time>
<p>[&hellip;] A Closer Look At Tone Mapping: another follow up, where the author makes various tone mapping experiments (also find more links there). [&hellip;]</p>
<hr />
####
[SteveM]( "steve.marton@gmail.com") - <time datetime="2012-10-11 19:05:17">Oct 4, 2012</time>
<p>Thanks for such an excellent and insanely thorough article! Whoever is not doing some sort of nonlinear tone mapping is just shooting themselves in the foot and missing out ;) Here&rsquo;s my super simple curve. float a = 10.0; /// Mid float b = 0.3; /// Toe float c = 0.5; /// Shoulder float d = 1.5; /// Mid return (x * (a * x + b)) / (x * (a * x + c) + d); Essentially it&rsquo;s John Hable&rsquo;s curve, but I wanted to remove the extraneous pow 2.2, and I wanted my whites to get white faster. Snow looked grey without it, and in general things were a bit less saturated than I liked. Put some sliders on those parameters and got exactly what I wanted. Whites are white, highlights are still very well controlled, little more contrast, little darker toe. The point is that it&rsquo;s a very powerful function, and you can get pretty much anything you want out of it. Try it. One more thing. I thought I would want to do tone mapping on luminance instead of individual color channels, and then I realized that&rsquo;s wrong, film/cameras apply the tone curve to the channels individually. You get a much more natural look, as MJP alluded.</p>
<hr />
####
[SteveM]( "steel_3d@hotmail.com") - <time datetime="2014-11-11 12:04:52">Nov 2, 2014</time>
<p>Here&rsquo;s another useful curve to try: float a = 1.8; /// Mid float b = 1.4; /// Toe float c = 0.5; /// Shoulder float d = 1.5; /// Mid return (col * (a * col + b)) * rcp(col * (a * col + c) + d); It&rsquo;s essentially linear - it only modifies the shoulder region and drastically improves highlight clipping. Doesn&rsquo;t modify the overall contrast or brightness, and it especially doesn&rsquo;t crush the darks in the toe region. My artists currently prefer this linear look, which better matches other content creation packages for baseline preview. Extra contrast and saturation can always be added later under artistic control (ie: color grading).</p>
<hr />
####
[Readings on Physically Based Rendering | Interplay of Light](http://interplayoflight.wordpress.com/2013/12/30/readings-on-physically-based-rendering/ "") - <time datetime="2013-12-30 05:15:16">Dec 1, 2013</time>
<p>[…] A Closer Look At Tone Mapping, Matt Pettineo provides a showcase of  popular tonemapping operators with sample code. […]</p>
<hr />
####
[Cleaning up the HDR pipeline | IceFall Games](http://mtnphil.wordpress.com/2014/04/30/cleaning-up-the-hdr-pipeline/ "") - <time datetime="2014-04-29 23:34:01">Apr 2, 2014</time>
<p>[…] I also implemented the filmic tone-mapping described here. I think it does look a little better than Reinhard (more “punchy”). There is a lot more information here too. […]</p>
<hr />
####
[HDR Rendering &#8211; Tonemapping and Bloom | Chetan Jags](https://chetanjags.wordpress.com/2015/06/28/hdr-rendering-tonemapping-and-bloom/ "") - <time datetime="2015-06-27 14:56:13">Jun 6, 2015</time>
<p>[…] <a href="https://mynameismjp.wordpress.com/2010/04/30/a-closer-look-at-tone-mapping/">https://mynameismjp.wordpress.com/2010/04/30/a-closer-look-at-tone-mapping/</a> […]</p>
<hr />
####
[Ciro Cardoso]( "cirocardoso@yahoo.co.uk") - <time datetime="2016-10-30 13:23:29">Oct 0, 2016</time>
<p>How can we implement these curves in something like Fusion? I already have script to create a Filmic Tonemapping, but I would love to play with the other curves.</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2016-10-31 21:54:13">Oct 1, 2016</time>
<p>Hi Ciro, Unfortunately I&rsquo;m not at all familiar with Fusion, so I don&rsquo;t know how to implement arbitrary tone mapping curves in that program. -Matt</p>
<hr />
####
[Ciro Cardoso]( "cirocardoso@yahoo.co.uk") - <time datetime="2016-11-04 01:07:48">Nov 5, 2016</time>
<p>Hi MJP, Thanks for the quick reply. Basically, I am trying to translate those curves (Log Luminance/RGB/ Output Luminance/RGB) to curves that you can find in Photoshop for example. But, I don&rsquo;t understand how can I translate for example the Log Luminance/RGB to a curve. Maybe I can&rsquo;t??</p>
<hr />
####
[Designing A Linear HDR Pipeline | kosmonaut games](https://kosmonautblog.wordpress.com/2017/03/26/designing-a-linear-hdr-pipeline/ "") - <time datetime="2017-03-26 08:31:39">Mar 0, 2017</time>
<p>[…] A great resource for tonemapping is, again, MJP’s blog. Highly recommended:https://mynameismjp.wordpress.com/2010/04/30/a-closer-look-at-tone-mapping/ […]</p>
<hr />
####
[Todo sobre HDR el resumen &#8211; El tesoro del saber retro](https://tesorodelsaberretro.wordpress.com/2016/09/10/todo-sobre-hdr-el-resumen/ "") - <time datetime="2016-09-10 09:24:23">Sep 6, 2016</time>
<p>[…] <a href="https://mynameismjp.wordpress.com/2010/04/30/a-closer-look-at-tone-mapping/">https://mynameismjp.wordpress.com/2010/04/30/a-closer-look-at-tone-mapping/</a> […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Attack of the depth buffer</title>
			<link>https://therealmjp.github.io/posts/attack-of-the-depth-buffer/</link>
			<pubDate>Tue, 23 Mar 2010 07:42:36 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/attack-of-the-depth-buffer/</guid>
			<description>In these exciting modern times, people get a lot of mileage out of their depth buffers. Long gone are the days where we only use depth buffers for visibility and stenciling, as we now make use of the depth buffer to reconstruct world-space or view-space position of our geometry at any given pixel. This can be a powerful performance optimization, since the alternative is to output position into a &amp;ldquo;fat&amp;rdquo; floating-point buffer.</description>
			<content:encoded><![CDATA[<p>In these exciting modern times, people get a lot of mileage out of their depth buffers. Long gone are the days where we only use depth buffers for visibility and stenciling, as we now make use of the depth buffer to <a href="http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/">reconstruct world-space or view-space position</a> of our geometry at any given pixel.  This can be a powerful performance optimization, since the alternative is to output position into a &ldquo;fat&rdquo; floating-point buffer. However it&rsquo;s important to realize that using the depth buffer in such unconventional ways can impose new precision requirements, since complex functions like lighting attenuation and shadowing will depend on the accuracy of the value stored in your depth buffer.  This is particularly important if you&rsquo;re using a hardware depth buffer for reconstructing position,  since the z/w value stored in it will be non-linear with respect to the view-space z value of the pixel. If you&rsquo;re not familiar with any of this, there&rsquo;s a good overview <a href="http://www.sjbaker.org/steve/omniv/love_your_z_buffer.html">here</a> by Steve Baker. The basic gist of it is that z/w will increase very quickly in the as you move away from the near-clip plane of your perspective projection, and for much of the area viewable by your camera you will have values &gt;= 0.9 in your depth buffer. Consequently you&rsquo;ll end up with a lot of precision for geometry that&rsquo;s close to your camera, and very little for geometry that&rsquo;s way in the back. <a href="http://www.codermind.com/articles/Depth-buffer-tutorial.html">This article</a> from codermind has some mostly-accurate graphs that visualize the problem.</p>
<p>Recently I&rsquo;ve been doing some research into different formats for storing depth, in order to get a solid idea of the amount of error I can expect.  To do this I made DirectX11 app where I rendered a series of objects at various depths, and compared the position reconstructed from the depth buffer with a position interpolated from the vertex shader.  This let me easily flip through different depth formats visualize the associated error. Here&rsquo;s a front view and a side view of the test scene:</p>
<p><a href="/images/converted/attack-of-the-depth-buffer/scene.png"><img src="/images/converted/attack-of-the-depth-buffer/scene.png" alt="" title="Scene"></a></p>
<p><a href="/images/converted/attack-of-the-depth-buffer/sceneside.png"><img src="/images/converted/attack-of-the-depth-buffer/sceneside.png" alt="" title="SceneSide"></a></p>
<p>The cylinders are placed at depths of 5, 20, 40, 60, 80, and 100. The near-clip plane was set to 1, and the far-clip was set to 101.</p>
<p>For an error metric, I calculated the difference between the reference position (interpolated view-space vertex position) and normalized it by dividing by the distance to the far clip plane. I also multiplied by 100, so that a fully red pixel represented a difference equivalent to 1% of the view distance. For a final output I put the shaded and lit scene in the top-left corner, the sampled depth in the top right, the error in the bottom left, and error * 100 in the bottom right.</p>
<p>For all formats marked &ldquo;Linear Z&rdquo;, the depth was calculated by taking view-space Z and dividing by the distance to the far-clip plane.  Position was reconstructed using the method decribed <a href="http://mynameismjp.wordpress.com/2009/05/05/reconstructing-position-from-depth-continued/">here</a>. For formats marked &ldquo;Perspective Z/W&rdquo;, the depth was calculated by interpolating the z and w components of the clip-space position and then dividing in the pixel shader.  Position was reconstructed by first reconstructing view-space Z from Z/W using values derived from the projection matrix.  For formats marked &ldquo;1 - Perspective Z/W&rdquo;, the near and far plane values were flipped when creating the perspective projection matrix. This effectively stores 1 - z/w in the depth buffer. More on that in #9.</p>
<p>So without further rambling, let&rsquo;s look at some results:</p>
<p><strong>1. Linear Z, 16-bit floating point</strong></p>
<p><a href="/images/converted/attack-of-the-depth-buffer/16f_linear.png">
</a><a href="/images/converted/attack-of-the-depth-buffer/16f_linear.png"><img src="/images/converted/attack-of-the-depth-buffer/16f_linear.png" alt="" title="16F_Linear"></a></p>
<p>So things are not so good on our first try. We get significant errors along the entire visible range with this format,  with the error increasing as we get towards the far-clip plane. This makes sense, considering that a floating-point value has more precision closer to 0.0 than it does closer to 1.0.</p>
<p><strong>2. Linear Z, 32-bit floating point</strong></p>
<p><a href="/images/converted/attack-of-the-depth-buffer/32f_linear.png"><img src="/images/converted/attack-of-the-depth-buffer/32f_linear.png" alt="" title="32F_Linear"></a>
Ahh, much nicer. No visible error at all. It&rsquo;s pretty clear that if you&rsquo;re going to manually write depth to a render target, this is a good way to go. Storing into a 32-bit UINT would probably have even better results due to an even distribution of precision, but that format may not be available depending on your platform.  In D3D11 you&rsquo;d also have to add a tiny bit of packing/unpacking code since there&rsquo;s no UNORM format.</p>
<p><strong>3. Linear Z, 16-bit UINT</strong></p>
<p><a href="/images/converted/attack-of-the-depth-buffer/16u_linear.png"><img src="/images/converted/attack-of-the-depth-buffer/16u_linear.png" alt="" title="16U_Linear"></a></p>
<p>For this image I output depth to a render target with the DXGI_FORMAT_R16_UNORM format. As you can see it still has errors, but they&rsquo;re significantly decreased compared to a 16-bit floating point. It seems to me that if you were going to restrict yourself to 16 bits for depth, this is a way to go.</p>
<p><strong>4. Perspective Z/W, 16-bit floating point</strong></p>
<p><a href="/images/converted/attack-of-the-depth-buffer/16f_perspective.png"><img src="/images/converted/attack-of-the-depth-buffer/16f_perspective.png" alt="" title="16F_Perspective"></a></p>
<p>This is easily the worst format out of everything I tested.  You&rsquo;re at a disadvantage right off the bat just from using 16-bits instead of 32, and you also compound that with the non-linear distribution of precision that occurs from storing perspective depth. Then on top of <em>that</em>, you&rsquo;re encoding to floating point which gives you even worse precision for geometry that&rsquo;s far from the camera. The results are not pretty&hellip;don&rsquo;t use this!</p>
<p><strong>5. Perspective Z/W, 32-bit floating point</strong></p>
<p><a href="/images/converted/attack-of-the-depth-buffer/32f_perspective.png"><img src="/images/converted/attack-of-the-depth-buffer/32f_perspective.png" alt="" title="32F_Perspective"></a></p>
<p>This one isn&rsquo;t so bad compared to using a 16-bit float, but there&rsquo;s still error at higher depth values.</p>
<p><strong>6. Perspective Z/W, 16-bit UINT</strong></p>
<p><a href="/images/converted/attack-of-the-depth-buffer/16u_perspective.png"><img src="/images/converted/attack-of-the-depth-buffer/16u_perspective.png" alt="" title="16U_Perspective"></a></p>
<p>I used a normal render target for this in my test app, but it should be mostly equivalent to sampling from a 16-bit depth buffer. As you&rsquo;d expect, quite a bit of error once you move away from the near clip plane.</p>
<p><strong>7. Perspective Z/W, 24-bit UINT</strong></p>
<p><a href="/images/converted/attack-of-the-depth-buffer/24u_perspective.png"><img src="/images/converted/attack-of-the-depth-buffer/24u_perspective.png" alt="" title="24U_Perspective"></a></p>
<p>This is the most common depth buffer format, and in my sample app I actually sampled the hardware depth-stencil buffer created from the first rendering pass.  Compared to some of the alternatives this really isn&rsquo;t terrible, and a lot of people have shipped awesome-looking games with this format. The maximum error towards the back is ~0.005%. If the distance to your far plane is very high, the error can be pretty significant.</p>
<p><strong>8. Position, 16-bit floating point</strong></p>
<p><a href="/images/converted/attack-of-the-depth-buffer/position16f.png"><img src="/images/converted/attack-of-the-depth-buffer/position16f.png" alt="" title="Position16F"></a></p>
<p>For this format, I just output view-space position straight to a DXGI_FORMAT_R16G16B16A16_FLOAT render target. The only thing this format has going for it is convenience and speed of reconstruction&hellip;all you have to do is sample and you have position. In terms of accuracy, the amount of error is pretty close to what you get from storing linear depth in a 16-bit float. All in all&hellip;it&rsquo;s a pretty bad choice.</p>
<p><strong>9. 1 - Z/W, 16-bit floating point</strong></p>
<p><a href="/images/converted/attack-of-the-depth-buffer/16f_perspective_flipped.png"><img src="/images/converted/attack-of-the-depth-buffer/16f_perspective_flipped.png" alt="" title="16F_Perspective_Flipped"></a></p>
<p>This is where things get a bit interesting. Earlier I mentioned how floating-point values have more precision closer to 0.0 than they do closer to 1.0. It turns out that if you flip your near and far plane such so that you store 1 - z/w in the depth buffer, your two precision distribution issues will mostly cancel each other out. As far as I know this was first proposed by Humus in <a href="http://forum.beyond3d.com/showthread.php?t=52049">this Beyond3D</a> thread. He later posted <a href="http://www.humus.name/index.php?page=News&amp;ID=255">this short article</a>, where elaborated on some of the issues brought up in that thread.  As you can see he&rsquo;s quite right: flipping the clip planes gives significantly improved results. They&rsquo;re still not great, but clearly we&rsquo;re getting somewhere.</p>
<p><strong>10. 1 - Z/W, 32-bit floating point</strong></p>
<p><a href="/images/converted/attack-of-the-depth-buffer/32f_perspective_flipped.png"><img src="/images/converted/attack-of-the-depth-buffer/32f_perspective_flipped.png" alt="" title="32F_Perspective_Flipped"></a></p>
<p>With a 32-bit float, flipping the planes gives us results similar to what we got when storing linear z. Not bad! In D3D10/D3D11 you can even use this format for a depth-stencil buffer&hellip;as long as you&rsquo;re willing to either give up stencil or use 64 bits for depth.</p>
<p>The one format I would have liked to add to this list is a 24-bit float depth-stencil format.  This format is available on consoles, and is even exposed in D3D9 as D3DFMT_D24FS8. However according to the caps spreadsheet that comes with DX SDK, only ATI 2000-series and up GPU&rsquo;s actually support this format. In D3D10/D3D11 there doesn&rsquo;t even appear to be an equivalent DXGI format, unless I&rsquo;m missing something.</p>
<p>If there&rsquo;s any other formats or optimizations out there that you think are worthwhile, please let me know so that I can add them to the test app!  Also if you&rsquo;d to play around with the test app, I&rsquo;ve upload the source and binaries <a href="http://cid-538e432ea49f5bde.skydrive.live.com/self.aspx/Public/Samples%20And%20Tutorials/DX11/PRTest.zip">here</a>.  The project uses my new sample framework, which I still consider to be work-in-progress.  However if you have any comments about the framework please let me know. I haven&rsquo;t put in the time to make the components totally separable, but if people are interested then I will take some time to clean things up a bit.</p>
<p>EDIT: I also started a thread <a href="http://www.gamedev.net/community/forums/topic.asp?topic_id=565995">here</a> on gamedev.net, to try to get some discussion going on the subject. Feel free to weigh in!</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://www.sgtconker.com/2010/03/depth-and-games/" title="">Depth And Games « Sgt. Conker</a> - <time datetime="2010-03-26 23:03:57">Mar 5, 2010</time></p>
<p>[&hellip;] “Blitzkrieg” Pettino fights with the depth buffer and Charles “One” Humphrey flags bits in [&hellip;]</p>
<hr />
####
[default_ex]( "default_ex@live.com") - <time datetime="2010-03-23 08:49:15">Mar 2, 2010</time>
<p>The bit about D24FS8 worries me a bit. I was thinking of using that format to provide layers to non-opaque objects in my deferred rendering system following an observation from the real world (not often are non-opaque objects visible through each other). I was also considering using that format with stencil passes later in the render system to provide natural separation of depth layers in depth of field post processing, similar to what was described at GDC09 by the SO4 developers.</p>
<hr />
####
[Patrick](http://recreationstudios.blogspot.com "mccarthp@gmail.com") - <time datetime="2010-03-23 08:55:21">Mar 2, 2010</time>
<p>Have you looked at a Logarithmic Depth Buffer? I first heard about it on Ysaneya&rsquo;s dev blog: <a href="http://www.gamedev.net/community/forums/mod/journal/journal.asp?jn=263350&amp;reply">http://www.gamedev.net/community/forums/mod/journal/journal.asp?jn=263350&amp;reply</a>_id=3513134 It is incredibly simple and it has fixed all z-buffer issues I&rsquo;ve had in the past. It is amazing. I would definitely recommend trying it out.</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2010-03-23 09:40:14">Mar 2, 2010</time>
<p>Yeah I did look into that a bit. It&rsquo;s cool stuff, but I have two concerns: 1. It has artifacts for triangles that cross the near-clip plane. Ysaneya got around this by manually outputting depth from the pixel shader, but this is a non-option for me since it disables fast-z and early z-cull. It also means you can&rsquo;t use multisampling, since you can only output a single depth value per fragment. 2. If you don&rsquo;t store z/w, then I think you&rsquo;ll run into all sorts of problems with z-cull and hardware optimizations since the z buffer will no longer be linear in screen space. But thank you for bringing this up! It&rsquo;s always good to have more things to think about it.</p>
<hr />
####
[Michael Hansen](http://www.evofxstudio.net "michaelhansen555@msn.com") - <time datetime="2010-03-24 08:28:40">Mar 3, 2010</time>
<p>hello mp i love all your toturials you have ,, as of the new gamestudio 4.0 we can do this depth thing here are some code,,so you can test is out you can use custrom shaders if you only create a windows game 4.0 project delete the game.cs and here are the new class.. aswell handling the device now you have a floatpoint 128 bit hdr buffer if you have a directx 11 card and if you have a directx 10 or 10.1 you will get a floatpoint 64 bit hdr on xbox360 we will only get a float 32bit bit hdr buffer wicth is expandet to 64 bit in the xenon gpu so alot of stuff can be done here Best Regards Michael Hansen public partial class Form1 : Form { public EvoFxEngine EvoFxEngine; public bool InitializeGame() { EvoFxEngine = new EvoFxEngine(this.Panel3D.Handle); return true; } private int lastTickCount; private int frameCount; private float FrameRate; private SpriteBatch MySpriteBatch; private SpriteFont MySpriteFont; public void RenderScene() { int currentTickCount = Environment.TickCount; int ticks = currentTickCount - lastTickCount; if (ticks &gt;= 1000) { FrameRate = (float)frameCount * 1000 / ticks; frameCount = 0; lastTickCount = currentTickCount; } EvoFxEngine.GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.CornflowerBlue); EvoFxEngine.GraphicsDevice.Present(); frameCount++; } #region NativeMethods Class [System.Security.SuppressUnmanagedCodeSecurity] // We won&rsquo;t use this maliciously [DllImport(&ldquo;User32.dll&rdquo;, CharSet = CharSet.Auto)] public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags); /// Windows Message [StructLayout(LayoutKind.Sequential)] public struct Message { public IntPtr hWnd; public IntPtr msg; public IntPtr wParam; public IntPtr lParam; public uint time; public System.Drawing.Point p; } public void OnApplicationIdle(object sender, EventArgs e) { while (this.AppStillIdle) { RenderScene(); } } private bool AppStillIdle { get { Message msg; return !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0); } } #endregion ////////////////////////////////////////////////////////////////////// public class EvoFxEngine { private static GraphicsDevice graphicsDevice; public static GraphicsDevice GraphicsDevice { get { return graphicsDevice; } set { graphicsDevice = value; } } private GraphicsAdapter GraphicsAdapter; private GraphicsProfile GraphicsProfile; private PresentationParameters PresentationParameters; public EvoFxEngine(IntPtr ScreenHandle) { PresentationParameters = new PresentationParameters(); PresentationParameters.BackBufferFormat = SurfaceFormat.HdrBlendable; PresentationParameters.IsFullScreen = false; PresentationParameters.BackBufferWidth = 1280; PresentationParameters.BackBufferHeight = 720; PresentationParameters.DeviceWindowHandle = ScreenHandle; PresentationParameters.DepthStencilFormat = DepthFormat.Depth24; PresentationParameters.PresentationInterval = PresentInterval.Immediate; GraphicsProfile = GraphicsProfile.Reach; graphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile, PresentationParameters); } }</p>
<hr />
####
[]( "") - <time datetime="2010-10-26 14:28:16">Oct 2, 2010</time>
<p>DXGI_FORMAT includes: DXGI_FORMAT_D24_UNORM_S8_UINT <a href="http://msdn.microsoft.com/en-us/library/bb173059(VS.85).aspx">http://msdn.microsoft.com/en-us/library/bb173059(VS.85).aspx</a></p>
<hr />
####
[Tex]( "ejted@hotmail.com") - <time datetime="2010-07-30 02:18:53">Jul 5, 2010</time>
<p>nice framework you have ! like the code for loading .X files&hellip; loading skinned .x files directly to dx11 would be nice to !</p>
<hr />
####
[Position From Depth 3: Back In The Habit &laquo; The Danger Zone](http://mynameismjp.wordpress.com/2010/09/05/position-from-depth-3/ "") - <time datetime="2010-09-05 23:12:00">Sep 0, 2010</time>
<p>[&hellip;] an actual working sample showing some of these techniques, you can have a look at the sample for my article on depth precision. Posted in Graphics, Programming. Leave a Comment [&hellip;]</p>
<hr />
####
[Andon M. Coleman](https://www.facebook.com/andon.coleman "amcolema@eagle.fgcu.edu") - <time datetime="2014-02-02 09:47:42">Feb 0, 2014</time>
<p>One thing has been bugging me about this for years now&hellip; where are you getting a 16-bit **floating-point** perspective depth buffer from? In D3D and OpenGL the only 16-bit depth-renderable format is fixed-point (unorm). There is effectively only one portably exposed floating-point depth buffer format and that is 32-bit.</p>
<hr />
####
[SSAO на OpenGL ES 3.0 | WithTube](http://video.teletrunk.kz/?p=27 "") - <time datetime="2014-12-08 03:49:17">Dec 1, 2014</time>
<p>[…] на мой взгляд лучшая реализация Hemispherical SSAOSSAO | Game RenderingAttack of the depth buffer — различные представления z-буфераЛинейная алгебра […]</p>
<hr />
####
[Linear depth buffer my ass | Yosoygames](http://yosoygames.com.ar/wp/2014/01/linear-depth-buffer-my-ass/ "") - <time datetime="2014-01-31 10:11:44">Jan 5, 2014</time>
<p>[…] Attack of the depth buffer […]</p>
<hr />
####
[Steve](http://blog.selfshadow.com "sjh199@zepler.org") - <time datetime="2014-06-06 08:22:40">Jun 5, 2014</time>
<p>Did you give 24bit uint a go with 1 – z/w? You may find that you get the similar benefits.</p>
<hr />
####
[WillP]( "pearcewf@gmail.com") - <time datetime="2014-06-06 06:29:44">Jun 5, 2014</time>
<p>I&rsquo;ve been using a 32-bit floating point depth buffer in my project for a while, and recently gave the 1 - Z / W 32-bit floating point option shown above a try. It did indeed give me better depth precision, even with increasing the distance between my near and far planes by a decent amount. My question is are there any reasons besides losing stencil or having to use the 64 bit depth/stencil option that this should NOT be used? I didn&rsquo;t notice any difference in application performance, but was wondering if there are potential issues I may be overlooking by switching over and fully adopting this format into my engine. What would keep this usage pattern that gives better depth storage from becoming a norm (or is it already, and I just missed the boat)?</p>
<hr />
####
[Depth Precision Visualized &#8211; Nathan Reed&#039;s coding blog](http://www.reedbeta.com/blog/2015/07/03/depth-precision-visualized/ "") - <time datetime="2015-07-03 00:59:02">Jul 5, 2015</time>
<p>[…] open-access link available, unfortunately). It was more recently re-popularized in blog posts by Matt Pettineo and Brano Kemen, and by Emil Persson’s Creating Vast Game Worlds SIGGRAPH 2012 […]</p>
<hr />
####
[EO]( "dominicstreeter@hotmail.com") - <time datetime="2014-07-05 06:22:06">Jul 6, 2014</time>
<p>WARNING: The geometry is drawn twice to render the lighting in this sample there is no deferred lighting equation hidden here, this light is forward rendered. The sample does not demonstrate depth buffer position reconstruction for a light based model, resolving the view or world space position of the light on any part of the screen. The precision will vary between the fudged reconstruction here and a true reconstruction algorithms final precision. I have so many questions but rather than asking them I implore you to write the definitive baseline sample for depth reconstruction from a real light model so you can get the view and world space position and prove all the math. Your snippets tantalisingly offer the elements of a pipeline where every element is critical, in isolation they only become valuable if you get it perfect and it is very slow and grinding work coming from a forward background. I will never surrender to this depth buffers attack; but please help me. If the plea of common dev folk does not drive you perhaps that margin of error between algorithms and positions might do instead :)</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2014-02-03 17:42:34">Feb 1, 2014</time>
<p>For most of the formats that were tested, I was manually generating the depth buffer outputting a depth value from a pixel shader to a render target. I don&rsquo;t think that fp16 is a supported depth format for any GPU that&rsquo;s I&rsquo;ve ever worked with. I included it in the testing because when I wrote the article it was still fairly common for DX9 games to store depth in render targets in order to avoid relying on driver hacks.</p>
<hr />
####
[Reading list on Z-buffer precision | Light is beautiful](http://lousodrome.net/blog/light/2015/07/22/reading-list-on-z-buffer-precision/ "") - <time datetime="2015-07-22 07:42:32">Jul 3, 2015</time>
<p>[…] Attack of the depth buffer, 2010, by Matt Pettineo, on visualizing depth buffer error when using different buffer formats. […]</p>
<hr />
####
[How can I find the pixel space coordinates of a 3D point &#8211; Part 3 &#8211; The Depth Buffer &#8211; Nicolas Bertoa](https://nbertoa.wordpress.com/2017/01/21/how-can-i-find-the-pixel-space-coordinates-of-a-3d-point-part-3-the-depth-buffer/ "") - <time datetime="2017-01-21 13:51:09">Jan 6, 2017</time>
<p>[…] Attack of The Depth Buffer – Matt Petineo […]</p>
<hr />
####
[Reverse Z Cheat Sheet &#8211; Intrinsic](http://www.intrinsic-engine.com/reverse-z-cheat-sheet/ "") - <time datetime="2017-12-30 07:52:53">Dec 6, 2017</time>
<p>[…] <a href="https://mynameismjp.wordpress.com/2010/03/22/attack-of-the-depth-buffer/">https://mynameismjp.wordpress.com/2010/03/22/attack-of-the-depth-buffer/</a> […]</p>
<hr />
####
[What&#8217;s New in Metal 3 &#8211; Metal by Example](http://metalbyexample.com/new-in-metal-3/ "") - <time datetime="2019-06-10 16:08:11">Jun 1, 2019</time>
<p>[…] This post by Matt Pettineo has a good overview of the trade-offs of different approaches to depth buffer precision. You can use the embedded tool in this post by Theodor Mader to get a sense of how error is distributed when using 16-bit depth. […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>D3D Performance and Debugging Tools Round-Up: PerfHUD</title>
			<link>https://therealmjp.github.io/posts/d3d-performance-and-debugging-tools-round-up-perfhud/</link>
			<pubDate>Sun, 07 Mar 2010 05:42:44 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/d3d-performance-and-debugging-tools-round-up-perfhud/</guid>
			<description>Officially, Nvidia&amp;rsquo;s PerfHUD is a performance-monitoring and debugging application for use with Nvidia GPU&amp;rsquo;s. Unofficially, it&amp;rsquo;s pure awesomeness for a graphics programmer. While I personally find PIX to be a more useful tool when it comes to debugging, the fact that PerfHUD gives you hardware-specific details makes it infinitely more useful for profiling. At work I find myself using it every time there&amp;rsquo;s a performance issue on the PC. Here&amp;rsquo;s some of the things I like to do with it (warning, it&amp;rsquo;s a long list!</description>
			<content:encoded><![CDATA[<p>Officially, Nvidia&rsquo;s PerfHUD is a performance-monitoring and debugging application for use with Nvidia GPU&rsquo;s. Unofficially, it&rsquo;s pure awesomeness for a graphics programmer.  While I personally find PIX to be a more useful tool when it comes to debugging, the fact that PerfHUD gives you hardware-specific details makes it infinitely more useful for profiling. At work I find myself using it every time there&rsquo;s a performance issue on the PC. Here&rsquo;s some of the things I like to do with it (warning, it&rsquo;s a long list!):</p>
<p><strong>1. View driver time and GPU idle time per frame</strong></p>
<p>When doing graphics in Windows, something you always need to be wary of is spending too much time sitting around in the driver instead of giving the GPU enough work to do. With PerfHUD instead of looking at the number of Draw/SetRenderState/SetTexture calls and guessing their impact (although PerfHUD will show you these values if you want), you get a nice sweet graph that shows you your driver time, GPU idle time, and total frame time.</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/driveridletime.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/driveridletime.png" alt="" title="DriverIdleTime"></a></p>
<p>These graphics make it very obvious whether your app is CPU-bound or GPU-bound, and if you are CPU-bound you can tell whether it&rsquo;s because you&rsquo;re spending too much time in the driver.</p>
<p><strong>2. View memory usage</strong></p>
<p>Want to know how much memory your app is using? PerfHUD tells you.</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/memory.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/memory.png" alt="" title="Memory"></a></p>
<p><strong>3. View shader utilization stats</strong></p>
<p>The default PerfHUD layout includes a graph showing you which percentage of unified shaders are being used for different shader types. This lets you know if you&rsquo;re spending a lot of time in vertex, geometry, or pixel shaders.</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/shaderusage.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/shaderusage.png" alt="" title="ShaderUsage"></a></p>
<p><strong>4. View GPU pipeline usage states</strong></p>
<p>The default layout also has a graph showing you how much you&rsquo;re using the input assembler, shader units, texture units, and ROP&rsquo;s:</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/unitusage.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/unitusage.png" alt="" title="UnitUsage"></a></p>
<p><strong>5. Customize the graphs</strong></p>
<p>Above I used the term &ldquo;default layout&rdquo; a few times. I do this because the initial layout you get isn&rsquo;t fixed, and you can customize it. You can add new graphs, move them, remove them, and customize the data shown on a graph. This lets pick and choose from the various performance counters available, and also change how the data is displayed. For instance you can select ranges, or switch between frame percentage or raw time.</p>
<p><strong>6. Run instant global experiments</strong></p>
<p>PerfHUD has hot-keys that let you toggle different experiments on and off. These include:</p>
<p>-Swap out all textures for 2x2 textures (removes texture bandwidth usage)
-Use a 1x1 viewport (removes pixel shading usage)
-Ignore all Draw calls (isolates the CPU)
-Eliminate geometry (removes vertex shader/input assembler usage)</p>
<p>You can also show wireframes, depth complexity (which shows your overdraw), and also highlight different pixel shader profiles.</p>
<p><strong>7. View textures and render targets for a draw call</strong></p>
<p>This is something you can do in PIX so it&rsquo;s not that smile, but I&rsquo;m mentioning this because PerfHUD makes it easier to view all of the textures and render targets at the same time. In PIX you have to look at the debug state and open a new tab in the Details view for each texture/RT, which is kinda annoying. Also note that when you do this the current state of the backbuffer is shown on the screen, with the current Draw call highlighted in orange. You don&rsquo;t see anything in my picture since the app doesn&rsquo;t draw anything to the backbuffer until the final step.</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/texturesrt.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/texturesrt.png" alt="" title="TexturesRT"></a></p>
<p><strong>8. View dependencies for a Draw call</strong></p>
<p>This is actually a pretty neat debugging feature. PerfHUD basically gives you a list of all previous Draw calls whose results are used by the current Draw call. It will also show which future Draw calls use the results from the current Draw call.</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/dependencies.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/dependencies.png" alt="" title="Dependencies"></a></p>
<p><strong>9. View and modify states for a Draw call</strong></p>
<p>PIX is really good at letting you see the current device state at a certain point in a frame, but PerfHUD takes this a step further by letting you modify them and instantly view the results.</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/modifystate.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/modifystate.png" alt="" title="ModifyState"></a></p>
<p><strong>10. View and modify shaders</strong></p>
<p>PerfHUD doesn&rsquo;t let you debug shaders like PIX can, but it does let you modify a shader and see the live changes in your app. You can also load up any shader from file, compile it, and replace that shader.</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/shaders.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/shaders.png" alt="" title="Shaders"></a></p>
<p><strong>11. Replace textures with special debugging textures</strong></p>
<p>In the Frame Debugger, you can replace a specific texture with one of the following:</p>
<ul>
<li>
<p>2x2 texture</p>
</li>
<li>
<p>Black, 25% gray, 50% gray, 75% gray, white textures</p>
</li>
<li>
<p>Vertical and horizontal gradients</p>
</li>
<li>
<p>Mipmap visualization texture</p>
</li>
</ul>
<p>Here&rsquo;s a screenshot showing the mipmap visualization texture applied as the diffuse albedo texture for all meshes:</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/mipmapvisualization.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/mipmapvisualization.png" alt="" title="MipmapVisualization"></a></p>
<p><strong>12. View comprehensive performance statistics</strong></p>
<p>The  Frame Profiler is easily the coolest part about PerfHUD. It presents you with a whole slew of information about what&rsquo;s going on with the GPU for different parts of a frame, and makes it easy to figure out which parts of your frame are the most expensive for different parts of the GPU pipeline. In fact, PerfHUD will automatically figure out the most expensive draw calls and indicate it for you. I&rsquo;m not going to go through each feature in detail since I want this post to stay readable, but I&rsquo;ll give you a list:</p>
<ul>
<li>
<p>View the bottleneck or utilization time per unit for a Draw call, state bucket (a state bucket is a group of draw calls that have similiar performance and bottleneck charactistics, meaning that if you reduce a particular bottleneck all Draw calls in the state bucket are likely to be quicker)</p>
</li>
<li>
<p>View a graph of bottleneck or utilization percentages for all all Draw calls in a frame</p>
</li>
<li>
<p>View a graph of CPU and GPU timing info</p>
</li>
<li>
<p>View a graph of the number of pixels shaded per Draw call</p>
</li>
<li>
<p>View a graph of the texture LOD level for all Draw calls</p>
</li>
<li>
<p>View a graph of the number of primitives and screen coverage per Draw call</p>
</li>
</ul>
<p>The following image shows the frame profiler in action. The graph is showing the bottleneck percentage per Draw call, and the selected Draw call is in the shadowmap generation pass. As you&rsquo;d expect, the call is primarily bound by the input assembler stage since the shaders are so simple. You can also see that PerfHUD grouped all of the other shadow map generation Draw calls into the same state bucket.</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/profiler.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-perfhud/profiler.png" alt="" title="Profiler"></a></p>
<p><strong>Useful Tips:</strong></p>
<ol>
<li>
<p>PerfHUD itself is actually just a layer on top of Nvidia&rsquo;s PerfKit, which is a library that lets you access hardware and driver-specific performance counters. If you wanted you could just use those API&rsquo;s yourself and display the information on-screen, or integrate it into in-house profiling tools. In fact Nvidia&rsquo;s provides a PIX plugin, which lets PIX display and record them just like any other other standard performance counter. However the catch is that a lot of the hardware counters aren&rsquo;t updated every frame, which makes it difficult to use them to figure out bottlenecks. You also have the problem that it&rsquo;s difficult to figure out bottlenecks for a specific portion of the frame, since you can&rsquo;t query the counters multiple times per frame. The PerfHUD Frame Profiler makes this all easy by automatically running a frame multiple times, allowing it to gather sufficient information from the hardware performance counters.  You could of course do this yourself, but it&rsquo;s a lot easier to just use PerfHUD.</p>
</li>
<li>
<p>PerfHUD is totally usable for XNA apps. In fact, all of those screenshots are from my InferredRendering sample. To run an app with PerfHUD you have to query for the PerfHUD adapter on startup, and use it if it&rsquo;s available. The user guide gives sample code for doing this in DX9, and it&rsquo;s even easier with XNA.In your constructor, add a handler for the GraphicsDeviceManager.PreparingDeviceSettings event:</p>
<pre tabindex="0"><code>graphics.PreparingDeviceSettings += graphics\_PreparingDeviceSettings;
</code></pre><p>Then in your event handler, use this code:</p>
<pre tabindex="0"><code>foreach (GraphicsAdapter adapter in GraphicsAdapter.Adapters)
{
    if (adapter.Description.Contains(&#34;PerfHUD&#34;))
    {                    
        e.GraphicsDeviceInformation.Adapter = adapter;
        e.GraphicsDeviceInformation.DeviceType = DeviceType.Reference;
        break;
    }
}
</code></pre></li>
<li>
<p>Be careful using PerfHUD when your app is running in windowed mode. Closing the window can cause it to crash.</p>
</li>
</ol>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://blog.thelazydeveloper.com/2011/03/28/using-nvidia-perfhud-with-xna-4-0/" title="">Using Nvidia PerfHUD with XNA 4.0 | The Stolen Journals</a> - <time datetime="2011-03-28 13:32:10">Mar 1, 2011</time></p>
<p>[&hellip;] the PerfHUD performance profiling overlay. There’s a good overview of the interface by MJP, here. Tweet This entry was posted in Uncategorized. Bookmark the permalink. ← Acronym [&hellip;]</p>
<hr />
####
[Performance Tools: PerfHUD &laquo; Sgt. Conker](http://www.sgtconker.com/2010/03/performance-tools-perfhud/ "") - <time datetime="2010-03-06 23:51:48">Mar 6, 2010</time>
<p>[&hellip;] Matt &ldquo;Perf Analyzer&rdquo; Pettineo has a new post about analyzing Direct3D performance using NVIDIA PerfHUD, where he goes into great deal about each feature of the tool. Read all about it on his blog. [&hellip;]</p>
<hr />
####
[Pete](http://amapplease.blogspot.com "ultrahead@msn.com") - <time datetime="2010-03-07 02:25:58">Mar 0, 2010</time>
<p>Great info! You should also check &ldquo;Nexus&rdquo;: <a href="http://developer.nvidia.com/object/nexus.html">http://developer.nvidia.com/object/nexus.html</a></p>
<hr />
####
[Popcorn]( "corbin@skymo.com") - <time datetime="2010-03-21 14:39:28">Mar 0, 2010</time>
<p>xna will set SoftwareVertexProcessing mode when perfhud is being used due to perfhud requirement for Reference device setting how do you work around that? I get 1 frame every 30 to 40 seconds with my app perfhud is unusable for me</p>
<hr />
####
[Intermission #8–What’s gone wrong? - Dark Genesis](http://xna-uk.net/blogs/darkgenesis/archive/2010/07/16/intermission-8-what-s-gone-wrong.aspx "") - <time datetime="2011-02-23 08:40:18">Feb 3, 2011</time>
<p>[&hellip;] of the Direct X SDK)     NVIDIA PERFHud (part of the Nvidia PerfSDK) – Useful Post here on how to get the most of this in XNA     AMD PerfTools     The [&hellip;]</p>
<hr />
####
[ozlael](http://ozlael.egloos.com "") - <time datetime="2011-03-26 03:49:14">Mar 6, 2011</time>
<p>Always thank you for useful article. Can i copy this post to my blog and translate into korean?</p>
<hr />
####
[Chris]( "Christian.Schlager@gmail.com") - <time datetime="2010-10-18 00:52:13">Oct 1, 2010</time>
<p>In XNA 4.0 the member GraphicsDeviceInformation.DeviceType is gone. When I try to start my test game with PerfHUD it says: No suitable graphics card found. Unable to create the graphics device. What can I do? Thanks :)</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>D3D Performance and Debugging Tools Round-Up: PIX</title>
			<link>https://therealmjp.github.io/posts/d3d-performance-and-debugging-tools-round-up-pix/</link>
			<pubDate>Mon, 15 Feb 2010 05:17:18 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/d3d-performance-and-debugging-tools-round-up-pix/</guid>
			<description>So at this point just everybody knows about knows about PIX. I mean it comes with the DirectX SDK, for crying out loud. This handy little program started its like as the Performance Investigator for Xbox (original Xbox, that is) and today is useful performance and debugging tool for both Windows and the Xbox 360. Since it&amp;rsquo;s a DirectX tool, most of the information you can gather from it is hardware-independent.</description>
			<content:encoded><![CDATA[<p>So at this point just everybody knows about knows about PIX. I mean it comes with the DirectX SDK, for crying out loud.  This handy little program started its like as the Performance Investigator for Xbox (original Xbox, that is) and today is useful performance and debugging tool for both Windows and the Xbox 360.  Since it&rsquo;s a DirectX tool, most of the information you can gather from it is hardware-independent. For instance it can easily tell you how many DrawIndexedPrimitives calls you&rsquo;re making, but it can&rsquo;t tell you whether the GPU is bound by texture bandwidth. For that reason I find that PIX is much more useful for debugging as opposed to performance investigations. However it can be useful when your performance is held up by API calls (since it can tell you where you&rsquo;re making them in a frame, and how many) and in Vista/Win7 it has access to GPU timing information that cam tell you how much time per frame the GPU is working, idle, or waiting for resources.  Another nice thing about PIX is that it now has full support for D3D11 as of the new February 2010 SDK, which unfortunately isn&rsquo;t the case for NVPerfHUD.</p>
<p>If you&rsquo;re an XNA programmer I&rsquo;d recommend checking out my in-depth <a href="http://mynameismjp.wordpress.com/samples-tutorials-tools/pix-with-xna-tutorial/">PIX With XNA article</a>, especially if you&rsquo;re new to D3D in general. For rest of you, here&rsquo;s a summary of what I think are the most useful things you can do with PIX:</p>
<p><strong>1. View the results of a Draw call</strong></p>
<p>With everyone and their mother using a deferred renderer these days, more often than not what&rsquo;s displayed on the screen is the result of several passes.  This means that when things go wrong, it&rsquo;s hard to guess the problem since it could have occurred in multiple places.  Fortunately PIX can help us by letting us pick any singular Draw call and see exactly what was drawn to the screen.  All you have to do s capture a frame, find the Draw call in the Event view, and then click on the &ldquo;Render&rdquo; tab in the Details view.  Here&rsquo;s a screenshot I took showing what was drawn to the normal-specular buffer during the G-Buffer pass of my <a href="http://mynameismjp.wordpress.com/2010/01/10/inferred-rendering/">Inferred Rendering Sample</a>:</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixviewdraw1.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixviewdraw1.png" alt="" title="PIXViewDraw"></a></p>
<p><strong>2.  View device state at any point in frame</strong></p>
<p>Ever have a problem where something wasn&rsquo;t drawing, and it turned out you left alpha-testing enabled or something silly like that? I know it&rsquo;s happened to me. If it happens again, you can help diagnose the problem by using PIX to view the state of your device at the time of a Draw call (or at any other point in the frame, for that matter).  To do it you capture a frame, find the Draw call or other Event you&rsquo;re interested in, and then find the device in your Objects view (filtering by Type can help). Then you just right-click on the device, click &ldquo;View Device&rdquo;, and have a look at the tab that appears in the Details view.  It looks something like this:</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixdevstate.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixdevstate.png" alt="" title="PIXDevState"></a></p>
<p><strong>3. View mesh data for a draw call</strong></p>
<p>Doing this lets you see what your vertex data looks like before and after your vertex shader (also before and after your geometry shader, if you&rsquo;re using D3D10 or D3D11). Just capture a frame, click on the Draw event, and click on the Mesh tab in the Details view.</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixviewmesh.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixviewmesh.png" alt="" title="PIXViewMesh"></a></p>
<p><strong>4. Debug shaders</strong></p>
<p>I don&rsquo;t I need to mention why this is useful. With PIX you can step through both the compiled assembly and HLSL code for your shader.  The easiest way to start debugging is to view the pixel history of a pixel by right-clicking on it in the Render tab, and then click on the links displayed for a Draw event.</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixdebugshader.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixdebugshader.png" alt="" title="PIXDebugShader"></a></p>
<p><strong>5. View textures, render targets, buffers, depth/stencil surfaces, and vertex declarations</strong></p>
<p>You can view the contents of all of these things just by finding them in the Objects view and right-clicking. Like everything else in PIX, what&rsquo;s displayed will reflect the current state of the object based on the event you&rsquo;ve selected in the Event view.  For vertex buffers you&rsquo;ll also need to specify the  vertex format using an HLSL-like syntax, which is really easy to do.</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixviewbuffer.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixviewbuffer.png" alt="" title="PIXViewBuffer"></a></p>
<p><strong>6. View a CPU/GPU timing graph (Vista/Win7 only)</strong></p>
<p>If you select the &ldquo;Statistics for each frame&rdquo; option when starting your experiment, one of the things you&rsquo;ll get is a timeline showing your CPU and GPU work for the frames captured.  This let&rsquo;s you easily view whether the GPU is idling or hard at work (which makes it simple to determine if you&rsquo;re CPU or GPU-bound). It also can show you where GPU work is done in relation to a frame being submitted by the CPU, so you can tell if the CPU is working one or more frames ahead of the GPU.</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixtiminggraph.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixtiminggraph.png" alt="" title="PIXTimingGraph"></a></p>
<p><strong>7. Record performance counter values for a frame, and show them on a HUD</strong></p>
<p>PIX has a number of counters available that let you keep track of things like the number of Draw calls or the number of SetTexture calls in a frame.  When you create a new experiment and you select the &ldquo;Statistics for each frame&rdquo; option, it will let you pick from a selection countersets provided by PIX.  To see which counters are included in a counterset or to make your own, click the &ldquo;More Options&rdquo; button, click the &ldquo;Set Counters&rdquo; action, and then click on &ldquo;Customize&rdquo;.  In this dialog you can pick through all of the D3D counters provided by PIX, or add in any of the standard Windows Performance Counters installed on your system. Also note that vendor&ndash;specific tools like NVPerkKit will install plugins for PIX that let you add in hardware-specific counters.</p>
<p>If you enable the HUD for your experiment, you&rsquo;ll get something that looks like this when running your app:</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixhud.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixhud_resized_1024.png" alt="" title="PIXHUD"></a></p>
<p>Either way once you close your app and you&rsquo;re viewing the experiment results, the Events view will display the value of all active counters for each frame.</p>
<p><a href="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixevents.png"><img src="/images/converted/d3d-performance-and-debugging-tools-round-up-pix/pixevents_resized_1024.png" alt="" title="PIXEvents"></a></p>
<p><strong>Useful Tips:</strong></p>
<ol>
<li>
<p>If you&rsquo;re going to debug shaders and you don&rsquo;t to have to step through the assembly, make sure you compile them with the DEBUG flag. This embeds debugging info in the compiled bytecode, including a path to the HLSL source code file. You&rsquo;ll also want to disable optimization if possible, otherwise you&rsquo;ll find that the compiler usually aggressively reorders your code.  XNA users: the Effect processor will enable the DEBUG flag when you perform Debug builds, and it will attempt to disable optimizations. If you&rsquo;re using a vs_2_0 or ps_2_0 shader it&rsquo;s possible that disabling optimizations  will cause you to go over the instruction limit, in which case the processor will re-enable optimizations.</p>
</li>
<li>
<p>Add markers to your app! PIX has a <a href="http://msdn.microsoft.com/en-us/library/ee417071%28VS.85%29.aspx">small list of functions</a> that lets you mark off portions of a frame, which causes PIX to collapse all of the events that occurred in the marked area.  So for instance you could add a marker for &ldquo;G-Buffer Pass&rdquo;, which lets you easily find draw calls made to build your G-Buffer.  If you&rsquo;re using XNA my sample includes a handy &ldquo;PIXHelper&rdquo; class that has pinvokes declared for those functions, as well as extension methods for SpriteMatch and Model.</p>
</li>
<li>
<p>Use the &ldquo;D&rdquo; buttons in the Events view to quickly iterate through your Draw calls</p>
</li>
<li>
<p>If you want to use the HUD and you&rsquo;re using D3D9, make sure you Present with the implicit swap chain created with the device</p>
</li>
<li>
<p>With D3D9 disable multisampling if you&rsquo;re going to capture a frame. PIX doesn&rsquo;t like it.</p>
</li>
</ol>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://www.sgtconker.com/2010/02/tools-and-fools/" title="">Tools And Fools « Sgt. Conker</a> - <time datetime="2010-02-15 11:15:53">Feb 1, 2010</time></p>
<p>[&hellip;] Start with PIX. [&hellip;]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>New Series: D3D Performance and Debugging Tools Round-Up</title>
			<link>https://therealmjp.github.io/posts/new-series-d3d-performance-and-debugging-tools-round-up/</link>
			<pubDate>Mon, 15 Feb 2010 05:16:17 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/new-series-d3d-performance-and-debugging-tools-round-up/</guid>
			<description>Recently I&amp;rsquo;ve been spending a lot of time with the various performance and debugging utilities available for Direct3D, and I thought it might be useful to give a quick overview of what&amp;rsquo;s out there. I&amp;rsquo;m sure most people who do a lot of Direct3D/XNA work are aware of these tools, but probably aren&amp;rsquo;t familiar with all of the really cool things you can do with them.
What I&amp;rsquo;m going to do is run through each tool one at a time, and share some of the common use cases and show some screenshots of features I think are neat.</description>
			<content:encoded><![CDATA[<p>Recently I&rsquo;ve been spending a lot of time with the various performance and debugging utilities available for Direct3D, and I thought it might be useful to give a quick overview of what&rsquo;s out there.  I&rsquo;m sure most people who do a lot of Direct3D/XNA work are aware of these tools, but probably aren&rsquo;t familiar with all of the really cool things you can do with them.</p>
<p>What I&rsquo;m going to do is run through each tool one at a time, and share some of the common use cases and show some screenshots of features I think are neat. That way some people might learn about something they never knew about, and hopefully a few people can tell me about something <em>I</em> never knew about.</p>
<p>As of right now here&rsquo;s the tools I&rsquo;m planning to run through:</p>
<ol start="2">
<li>
<p>PIX</p>
</li>
<li>
<p>NVPerfHUD</p>
</li>
<li>
<p>GPU PerfStudio</p>
</li>
<li>
<p>NVShaderPerf</p>
</li>
<li>
<p>GPU ShaderAnalyzer</p>
</li>
</ol>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://www.sgtconker.com/2010/02/tools-and-fools/" title="">Tools And Fools « Sgt. Conker</a> - <time datetime="2010-02-15 11:10:17">Feb 1, 2010</time></p>
<p>[&hellip;] Enter Matt “” Pettineo. [&hellip;]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Name Change</title>
			<link>https://therealmjp.github.io/posts/name-change/</link>
			<pubDate>Sat, 06 Feb 2010 23:22:13 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/name-change/</guid>
			<description>I got tired having an awkward mouthful of a blog name, so I decided to shorten it to something much snappier. Hence &amp;ldquo;MJP&amp;rsquo;s XNA Danger Zone&amp;rdquo; becomes simply &amp;ldquo;The Danger Zone&amp;rdquo;. I like it better already.
Actually the main reason for the change is that I&amp;rsquo;ve been taking a break from the XNA stuff so that I can finally play around with DX11 a bit. In fact I&amp;rsquo;ve been working on a simple and flexible DX11 sample framework, so you may see a few DX11 samples from me in the future.</description>
			<content:encoded><![CDATA[<p>I got tired having an awkward mouthful of a blog name, so I decided to shorten it to something much snappier.  Hence &ldquo;MJP&rsquo;s XNA Danger Zone&rdquo; becomes simply &ldquo;The Danger Zone&rdquo;. I like it better already.</p>
<p>Actually the main reason for the change is that I&rsquo;ve been taking a break from the XNA stuff so that I can finally play around with DX11 a bit. In fact I&rsquo;ve been working on a simple and flexible DX11 sample framework, so you may see a few DX11 samples from me in the future.  It should be fun, the new multi-threading features are really really cool. I&rsquo;d like to do some compute shader stuff too, especially since I haven&rsquo;t gotten around to playing with Cuda yet. I think tessellation will have to wait a bit though, since I don&rsquo;t think I&rsquo;m going to buy a DX11 GPU until the Fermi-based GPU&rsquo;s from Nvidia come out. Until then, D3D_FEATURE_LEVEL_10_0 will have to do.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://www.sgtconker.com/2010/02/name-and-saw/" title="">Name And Saw « Sgt. Conker</a> - <time datetime="2010-02-07 18:42:41">Feb 0, 2010</time></p>
<p>[&hellip;] Enter Matt “Danger Mouse” Pettino. [&hellip;]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Inferred Rendering</title>
			<link>https://therealmjp.github.io/posts/inferred-rendering/</link>
			<pubDate>Sun, 10 Jan 2010 17:30:10 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/inferred-rendering/</guid>
			<description>So like I said in my last post, I&amp;rsquo;ve been doing some research into Inferred Rendering. If you&amp;rsquo;re not familiar with the technique, Scott Kircher has the original paper and presentation materials hosted on his website. The main topic of the paper is what they call &amp;ldquo;Discontinuity Sensitive Filtering&amp;rdquo;, or &amp;ldquo;DSF&amp;rdquo; for short. Basically it&amp;rsquo;s standard 2x2 bilinear filtering, except in addition to sampling the texture you&amp;rsquo;re interested in you also sample what they call a a &amp;ldquo;DSF buffer&amp;rdquo; containing depth, an instance ID (semi-unique for each instance rendering on-screen), and a normal ID (a semi-unique value identifying areas where the normals are continuous).</description>
			<content:encoded><![CDATA[<p>So like I said in my last post, I&rsquo;ve been doing some research into Inferred Rendering.  If you&rsquo;re not familiar with the technique, Scott Kircher has the original paper and presentation materials hosted on <a href="http://graphics.cs.uiuc.edu/~kircher/publications.html">his website</a>.  The main topic of the paper is what they call &ldquo;Discontinuity Sensitive Filtering&rdquo;, or &ldquo;DSF&rdquo; for short.  Basically it&rsquo;s standard 2x2 bilinear filtering, except in addition to sampling the texture you&rsquo;re interested in you also sample what they call a a &ldquo;DSF buffer&rdquo; containing depth, an instance ID (semi-unique for each instance rendering on-screen), and a normal ID (a semi-unique value identifying areas where the normals are continuous).  By comparing the values sampled from the DSF buffer with the values supplied for the mesh being rendered (they apply the DSF filter during final pass of a light-prepass renderer where meshes are re-rendered and sample from the lighting buffer), they can bias the bilinear weights so that texels not &ldquo;belonging&rdquo; to the object being rendered are automatically rejected.  They go through all of this effort so that they can do two things:</p>
<ol start="2">
<li>
<p>They can use a lower-res G-Buffer and L-Buffer but still render their geometry at full res</p>
</li>
<li>
<p>They can light transparent surfaces using a deferred approach, by applying a stipple pattern when rendering the transparents to the G-Buffer</p>
</li>
</ol>
<p>The second part is what&rsquo;s interesting, so let&rsquo;s talk about.  Basically what they do is they break up the G-Buffer into 2x2 quads.  Then for transparent objects, an output mask is applied so that only one pixel in the quad is actually written to.  Then by rotating the mask, you could render up to 3 layers of transparency into the quad and still have opaques visible underneath.  For a visual, this is what a quad would look like if only one transparent layer was rendered:</p>
<p><a href="/images/converted/inferred-rendering/transparent1layer.png"><img src="/images/converted/inferred-rendering/transparent1layer.png" alt="" title="Transparent1Layer"></a></p>
<p>So &ldquo;T1&rdquo; would be from the transparent surface, and &ldquo;O&rdquo; would be from opaque objects below it.  This is what it would look like if you had 3 transparent surfaces overlapping:</p>
<p><a href="/images/converted/inferred-rendering/transparent3layers.png"><img src="/images/converted/inferred-rendering/transparent3layers.png" alt="" title="Transparent3Layers"></a></p>
<p>After laying out your G-Buffer, you then fill your L-Buffer (Lighting Buffer) with values just like you would with a standard Light Pre-pass renderer.  After you&rsquo;ve filled your L-Buffer, you re-render your opaque geometry and sample your L-Buffer using a DSF filter so that only the texels belonging to opaque geometry get samples.  Then you render your transparent geometry with blending enabled, each time adjusting your DSF sample positions so that the 4 nearest texels (according to the output mask you used when rendering it to the G-Buffer) are sampled.</p>
<p>So you can light your transparents just like any other geometry, which is really cool stuff if you have a lot dynamic lights and shadows (which you probably do if you&rsquo;re doing deferred rendering in the first place).  But now come the downsides:</p>
<ol start="2">
<li>
<p>Transparents end up being lit at 1/4 resolution, and opaques underneath transparents will be lit at either 3/4, 2/4, or 1/4 resolution.  How bad this looks mainly depends on whether you have high-frequency normal maps, since the lighting itself is generally low-frequency.  You&rsquo;re also helped a bit by the fact that your diffuse albedo texture will still be sampled at full rate.  Here&rsquo;s a screenshot comparing forward-rendered transparents (left-side), with deferred transparents (right-side):
<a href="/images/converted/inferred-rendering/comparenm.png"><img src="/images/converted/inferred-rendering/comparenm_resized_300.png" alt="" title="CompareNM"></a>
You can see that aliasing artifacts become visible on the transparent layers, due to the normal maps.  Even more noticable is shadow map aliasing, which becomes noticeably worse on the transparent layers since it&rsquo;s only sampled at 1/4 rate.  Here&rsquo;s a screenshot showing the same comparison, this time with normal maps disabled:</p>
<p><a href="/images/converted/inferred-rendering/compare.png"><img src="/images/converted/inferred-rendering/compare_resized_300.png" alt="" title="Compare"></a>
The aliasing becomes much less visible on the unshadowed areas without normal mapping disabled, since now the normals are much lower-frequency.  However you still have the same problem with shadow map aliasing.</p>
</li>
<li>
<p>The DSF filtering is not cheap.  Or at least, the way I implemented it wasn&rsquo;t cheap.  My code can probably be optimized a bit to reduce instructions, but unless I&rsquo;m missing something fundamental I don&rsquo;t think you could make any significant improvements.  If someone does figure out anything, please let me know!  Anyway when compiling my opaque pixel shader with fxc.exe  (from August 2009 SDK) using ps_3_0, I get a nice 11 instructions (9 math, 2 texture) when no DSF filtering is used.  When filtering is added in, it jumps up to a nasty 64 instructions! (55 math, 9 texure).  For transparents the shader jumps up again (71 math, 9 texture) since some additional math is needed to adjust the filtering in order to sample according to the stipple pattern.  Running the shaders through NVShaderPerf gives me the following:
<a href="/images/converted/inferred-rendering/cyclesnvidia.png"><img src="/images/converted/inferred-rendering/cyclesnvidia.png" alt="" title="CyclesNvidia"></a>
Here&rsquo;s what I get with ATI&rsquo;s GPU ShaderAnalyzer:</p>
<p><a href="/images/converted/inferred-rendering/cyclesati.png"><img src="/images/converted/inferred-rendering/cyclesati.png" alt="" title="CyclesATI"></a>So like I said, it&rsquo;s not definitely not free.  In the paper they mention that they also use a half-sized G-Buffer + L-Buffer which offsets the cost of the extra filtering.  When running my test app on my GTX 275 at half-res G-Buffer there&rsquo;s almost no difference in framerate and at quarter-res it&rsquo;s actually faster to defer the transparents.  Using a full-res G-Buffer/L-Buffer it&rsquo;s quicker to forward-render the transparents, with 4  large point lights and 1 directional light + shadow.  So I&rsquo;d imagine for a full-res G-Buffer/L-Buffer you&rsquo;d need quite a few dynamic lights for it to pay off when going deferred for transparents.  But in my opinion, the decrease in quality when using a lower-res G-Buffer just isn&rsquo;t worth it.  Here&rsquo;s a screenshot showing deferred transparents with half-sized G-Buffer:</p>
<p><a href="/images/converted/inferred-rendering/halfsizegbuffer.png"><img src="/images/converted/inferred-rendering/halfsizegbuffer_resized_300.png" alt="" title="HalfSizeGBuffer"></a></p>
<p>Notice how bad the shadows look on the transparents, since now the shadow map is being sampled at 1/8th rate.  Even on the opaques you start to lose quite a bit of the normal map detail.</p>
</li>
<li>
<p>You only get 3 layers of transparency.  However past 3 layers it would probably be really hard to notice that you&rsquo;re missing anything, at least to the average player.</p>
</li>
<li>
<p>Since you use instance ID&rsquo;s to identify transparent layers, you&rsquo;ll have problems with models that have multiple transparency levels (like a car, which has 4 windows)</p>
</li>
</ol>
<p>Regardless, I think the technique is interesting enough to look into.  Personally when I read the paper I had major concerns about what shadows would look like on the transparents (especially with a lower-res L-Buffer), which is what lead to me to make a prototype with XNA so that I could evaluate some of the more pathological cases that could pop up.  If you&rsquo;re also interested, I&rsquo;ve uploaded the binary <a href="http://cid-538e432ea49f5bde.skydrive.live.com/self.aspx/Public/Samples%20And%20Tutorials/InferredRendering%5E_Bin.zip">here</a>, and the source <a href="http://cid-538e432ea49f5bde.skydrive.live.com/self.aspx/Public/Samples%20And%20Tutorials/InferredRendering%5E_Src.zip">here</a>.  If you want to run the binary you&rsquo;ll need the XNA 3.1 Redistributable, located <a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=53867a2a-e249-4560-8011-98eb3e799ef2&amp;displaylang=en">here</a>.</p>
<p>One thing you&rsquo;ll notice about my implementation is that I didn&rsquo;t factor in normals at all in the DSF filter, and instead I stored depth in a 16-bit component and instance ID in the the other 16 bits.  This would give you much more than the 256 instances that the original implementation is limited to, at the expense of some artifacts around areas where the normal changes drastically on the same mesh.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://mynameismjp.wordpress.com/2010/02/14/d3d-performance-and-debugging-tools-round-up-pix/" title="">D3D Performance and Debugging Tools Round-Up: PIX « The Danger Zone</a> - <time datetime="2010-02-14 21:17:31">Feb 0, 2010</time></p>
<p>[&hellip;] With everyone and their mother using a deferred renderer these days, more often than not what’s displayed on the screen is the result of several passes.  This means that when things go wrong, it’s hard to guess the problem since it could have occurred in multiple places.  Fortunately PIX can help us by letting us pick any singular Draw call and see exactly what was drawn to the screen.  All you have to do s capture a frame, find the Draw call in the Event view, and then click on the “Render” tab in the Details view.  Here’s a screenshot I took showing what was drawn to the normal-specular buffer during the G-Buffer pass of my Inferred Rendering Sample: [&hellip;]</p>
<hr />
####
[Inferred And Deferred &laquo; Sgt. Conker](http://www.sgtconker.com/2010/01/inferred-and-deferred/ "") - <time datetime="2010-01-12 20:56:46">Jan 2, 2010</time>
<p>[&hellip;] Enter Matt “Advanced Stuffs” Pettineo. [&hellip;]</p>
<hr />
####
[Mike]( "b1691505@uggsrock.com") - <time datetime="2010-01-15 21:15:13">Jan 5, 2010</time>
<p>The download links to your SkyDrive files are broken&hellip;</p>
<hr />
####
[Garuda]( "garuda_xc@126.com") - <time datetime="2010-02-28 22:02:45">Feb 0, 2010</time>
<p>Great post! A lot of help Small question, why use &ldquo;return float4(color * Alpha, Alpha);&rdquo; and &ldquo;SrcBlend = ONE;&rdquo; during alpha blend? Is it an optimization trick?</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2010-02-28 22:26:40">Feb 0, 2010</time>
<p>It&rsquo;s called &ldquo;premultiplied alpha&rdquo;. Tom Forsyth has a comprehensive overview here: <a href="http://home.comcast.net/~tom">http://home.comcast.net/~tom</a>_forsyth/blog.wiki.html#[[Premultiplied%20alpha]] Shawn Hargreaves also has a nice overview here: <a href="http://blogs.msdn.com/shawnhar/archive/2009/11/06/premultiplied-alpha.aspx">http://blogs.msdn.com/shawnhar/archive/2009/11/06/premultiplied-alpha.aspx</a></p>
<hr />
####
[Garuda]( "garuda_xc@126.com") - <time datetime="2010-03-01 02:23:34">Mar 1, 2010</time>
<p>Thanks! Also some confusion in lighting computation. What&rsquo;s specular level &ldquo;((specExponent + 8.0f) / (8.0f * 3.14159265f))&rdquo; about? And why diffuse factor is multiplied to specular factor as &ldquo;specular * NdotL&rdquo; ?</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2010-03-02 01:39:42">Mar 2, 2010</time>
<p>That bit with the specular is a normalization factor. It&rsquo;s there to ensure that energy is conserved for the BRDF. You can derive it if you take the blinn-phong equation, set up an integral around the hemisphere, and solve for 1. The diffuse factor is multiplied in so that the attenuation is applied to the specular, and so that you don&rsquo;t get incorrect specular reflections due to normal maps.</p>
<hr />
####
[Garuda]( "garuda_xc@126.com") - <time datetime="2010-03-02 02:11:21">Mar 2, 2010</time>
<p>Thank you! You are a helpful guru~</p>
<hr />
####
[Lighting alpha objects in deferred rendering contexts | Interplay of Light](http://interplayoflight.wordpress.com/2013/09/24/lighting-alpha-objects-in-deferred-rendering-contexts/ "") - <time datetime="2013-09-24 05:01:13">Sep 2, 2013</time>
<p>[…] Use some sort of screen door transparency like the one implemented by Inferred rendering. This is basically opaque rendering in which the “alpha” object is rendered on top of […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Correcting XNA&#39;s Gamma Correction</title>
			<link>https://therealmjp.github.io/posts/correcting-xnas-gamma-correction/</link>
			<pubDate>Thu, 31 Dec 2009 22:31:58 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/correcting-xnas-gamma-correction/</guid>
			<description>One thing I never used to pay attention to is gamma correction. This is mainly because it rarely gets mentioned, and also because you can usually get pretty good results without ever even thinking about it. However it only took a few days at my new job for me to realize just how essential it is if you want professional-quality results.
Lately I&amp;rsquo;ve been doing some research into inferred rendering (more on that later), and while working up a prototype renderer in XNA I decided that I would (for once) be gamma-correct throughout the pipeline.</description>
			<content:encoded><![CDATA[<p>One thing I never used to pay attention to is <a href="http://download.microsoft.com/download/b/5/5/b55d67ff-f1cb-4174-836a-bbf8f84fb7e1/Picture%20Perfect%20-%20Gamma%20Through%20the%20Rendering%20Pipeline.zip">gamma correction</a>.  This is mainly because it rarely gets mentioned, and also because you can usually get <em>pretty good</em> results without ever even thinking about it.  However it only took a few days at my new job for me to realize just how essential it is if you want professional-quality results.</p>
<p>Lately I&rsquo;ve been doing some research into inferred rendering (more on that later), and while working up a prototype renderer in XNA I decided that I would (for once)  be gamma-correct throughout the pipeline.  So I went looking through the XNA Framework documentation for framework&rsquo;s equvalent of the D3DSAMP_SRGBTEXTURE sampler state (which automatically converts from sRGB to linear in the texture unit) and the D3DRS_SRGBWRITEENABLE render state (which automatically converts from linear to sRGB in the ROP)&hellip;and I didn&rsquo;t find them.  The thought of these being left out struck me as odd, so I did a bit of searching on Google.  After refining my search terms I found <a href="http://forums.xna.com/forums/t/9727.aspx">this post</a> by framework developer Shawn Hargreaves, confirming that those states were not exposed in the framework due to inconsistencies between Windows and Xbox.  After looking through some presentations again I concluded that he was talking about&hellip;</p>
<p>1.  The fact that the 360 uses a 4-segment piecewise linear approximation curve to perform conversion to and from sRGB, which gives quite different results compared to what you get with PC GPU&rsquo;s.</p>
<p>2.  The fact that blending behavior is different in DX9 and DX10-level GPU&rsquo;s, regardless of which API you use.  DX9 GPU&rsquo;s will perform framebuffer blending after conversion to sRGB (which is mathematically incorrect), while DX10 GPU&rsquo;s will do the blending in linear space and then convert the blended result to sRGB.  There is a cap to detect this behavior (D3DPMISCCAPS_POSTBLENDSRGBCONVERT) but it&rsquo;s only available if you create an IDirect3D9Ex device.</p>
<p>So yeah, that&rsquo;s annoying.  But like most limitations in the framework you can work around them if you&rsquo;re determined enough, and fortunately this one is a piece of cake.  Well&hellip;on the PC, at least.  So let&rsquo;s start with the first half, sampling sRGB textures.  Like I mentioned before there&rsquo;s a nice convenient sampler state in D3D9 that will do the sRGB-&gt;linear automatically, but XNA&rsquo;s SamplerState just doesn&rsquo;t have it.  But fortunately that&rsquo;s not the only way to set sampler states&hellip;we can also get the Effects framework to do it for us by defining a sampler_state in our effect files.  So I took a peek at the <a href="http://msdn.microsoft.com/en-us/library/ee418548%28VS.85%29.aspx#Sampler_Stage_States">D3D9 Effect States documentation</a>, and added the appropriate state declaration to my effect file.  And it worked!  For the lazy, all you have to do is this (important line in bold):</p>
<pre tabindex="0"><code>texture2D DiffuseMap;
sampler2D DiffuseSampler = sampler_state
{
   Texture = &lt;DiffuseMap&gt;;
   **SRGBTexture = true;**
};
</code></pre><p>Okay now for the other half, sRGB writes.  Once again D3D9 has a convenient render state that does all of the work for us, and the Effects framework can set render states for us if we include them in a pass declaration.  But unfortunately this time the Effect States documentation didn&rsquo;t have anything for SRGBWRITEENABLE.  Too determined to give up, I followed the standard convention of effect states and chopped the prefix off the &ldquo;D3DRS_&rdquo; prefix.  And hey, it worked!</p>
<pre tabindex="0"><code>technique Transparent
{
    pass Pass1
    {
       VertexShader = compile vs_3_0 TransparentVS();
       PixelShader = compile ps_3_0 TransparentPS();

       **SRGBWriteEnable = true;**
    }
}
</code></pre><p>So we&rsquo;ve solved our gamma problems&hellip;at least if you&rsquo;re only targeting the PC and you&rsquo;re using Effects.  If you&rsquo;re not using Effects, then I don&rsquo;t know of any way to toggle those states.  It&rsquo;s probably possible with some sort of interop/reflection voodoo, but I don&rsquo;t know enough about these things to recommend it.</p>
<p>There&rsquo;s also the Xbox 360 problem, which is actually two problems in one.  The first problem is that the Xbox 360 doesn&rsquo;t use sampler and render states to control sRGB read and writes.  It instead uses the D3D10 convention of having special surface formats for textures and render targets that control whether conversion takes place.  I don&rsquo;t have access to my Xbox 360 at the moment so I can&rsquo;t verify for sure, but I strongly suspect that the effect states won&rsquo;t work.  And even if they did work you&rsquo;d still have the second problem, which is that the Xbox uses that piecewise approximation curve  (<a href="http://www.valvesoftware.com/publications/2008/GDC2008_PostProcessingInTheOrangeBox.pdf">this presentation</a> by Valve shows some of the nastiness that can occur with it).</p>
<p>Fortunately we can bypass those problems by doing the conversion ourselves in the shader.  The good news is that the code is a piece of cake&hellip;the bad news is that it&rsquo;s not super cheap since it involves raising your RGB color value to a non-integral power. Here&rsquo;s the code:</p>
<pre tabindex="0"><code>// Converts from linear RGB space to sRGB.
float3 LinearToSRGB(in float3 color)
{
    return pow(color, 1/2.2f);
}

// Converts from sRGB space to linear RGB.
float3 SRGBToLinear(in float3 color)
{
    return pow(color, 2.2f);
}
</code></pre><p>Unfortunately with these you also have the problem that filtering and blending will be performed in sRGB space, and there&rsquo;s not much you can do about that (aside from doing the filtering and blending yourself, but that would be way too expensive).</p>
<p>If you want to make these conversions a little cheaper, you can use a trick that my coworker showed me: round down the 2.2 to 2.0.  This gives you a simple square operation for conversion to linear (you can just dot the value with itself), and a sqrt operation for conversion to sRGB.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22spm@venzon.net%22">Joe Venzon</a> - <time datetime="2010-06-01 15:53:22">Jun 2, 2010</time></p>
<p>&ldquo;If you want to make these conversions a little cheaper, you can use a trick that my coworker showed me: round down the 2.2 to 2.0. This gives you a simple square operation for conversion to linear (you can just dot the value with itself), and a sqrt operation for conversion to sRGB.&rdquo; I think you mean you want to multiply the value with itself, not dot it with itself.</p>
<hr />
####
[porges](http://porg.es/blog/ "porges@porg.es") - <time datetime="2011-05-08 16:01:28">May 0, 2011</time>
<p>In XNA 4.0, sRGBWriteEnable doesn&rsquo;t work. The compiler claims it is obsolete. Do you know anything about this?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-05-08 19:10:15">May 0, 2011</time>
<p>I do know that for XNA 4 the effect content processor does some extra stuff when compiling effects to keep track of which states need to be set, and I&rsquo;m guessing that when they did that they decided not to support the sRGB states.</p>
<hr />
####
[XNA 4.0 Gamma Corrected Pipeline | Ploobs](http://ploobs.com.br/?p=1207 "") - <time datetime="2011-07-02 04:59:01">Jul 6, 2011</time>
<p>[&hellip;] In XNA 3.1 (PC version only) we could use a DirectX 9c instructions to configure the texture sampler to automatically convert the texture from SRGB to Linear space on hardware. We also could set the render surface to be SRGB, so the gamma correction pipeline was pretty simple and fully done in hardware. More informations here. [&hellip;]</p>
<hr />
####
[CORRECTING XNA’S GAMMA CORRECTION &#8211; morning&#039;s blog](https://ywjheart.wordpress.com/2016/11/04/correcting-xnas-gamma-correction/ "") - <time datetime="2016-11-03 23:04:12">Nov 4, 2016</time>
<p>[…] <a href="https://mynameismjp.wordpress.com/2009/12/31/correcting-xnas-gamma-correction/">https://mynameismjp.wordpress.com/2009/12/31/correcting-xnas-gamma-correction/</a> […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>More Post-Processing Tricks: Lens Flare</title>
			<link>https://therealmjp.github.io/posts/more-post-processing-tricks-lens-flare/</link>
			<pubDate>Tue, 15 Dec 2009 08:53:17 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/more-post-processing-tricks-lens-flare/</guid>
			<description>I was playing Killzone 2 the other day, which reminded me of the lens flare trick they used. Unlike most games, which use some sprites controlled by an occlusion query, they applied the effect as a post-process similar to bloom. The upside is that it works for all bright areas and not pre-defined areas (the sun), and you don&amp;rsquo;t have to do occlusion queries or anything like that since that&amp;rsquo;s handled automatically.</description>
			<content:encoded><![CDATA[<p>I was playing Killzone 2 the other day, which reminded me of the lens flare trick they used.  Unlike most games, which use some sprites controlled by an occlusion query, they applied the effect as a post-process similar to bloom.  The upside is that it works for all bright areas and not pre-defined areas (the sun), and you don&rsquo;t have to do occlusion queries or anything like that since that&rsquo;s handled automatically.  Plus it&rsquo;s really easy to fit it into a post-processing chain, since you can use your bloom results as the input.  The downside is that it&rsquo;s pretty far from realistic&hellip;I&rsquo;m not sure that most would like the end result.  This screen <a href="http://image.com.com/gamespot/images/2009/139/928377_20090520_screen014.jpg">here</a> shows the effect pretty clearly (it&rsquo;s the orange and purple blobby areas by the left bad guy&rsquo;s head, on the opposite side of the screen from the bright light source).</p>
<p>I haven&rsquo;t seen anyone duplicate or even discuss the technique since before the game out, so I figured I&rsquo;d take a crack at deciphering it myself.  After some experimenting I came up with the following basic approach:</p>
<p>1.  Render a bloom buffer using standard downscale + threshold + blur
2.  Flip the texture coordinates by doing float2(1, 1) - texCoord
3.  Blur both towards the center of the screen and away from it
4.  Combine additively with the bloom buffer</p>
<p>To fake a chromatic aberration, Killzone 2 uses a strong orange tint for areas closer to the center of the screen and a purple tint on the periphery.  Upon some further close analysis it started to look like they were doing it in two passes with a different tint and different texture coordinate scaling for each pass.  I decided to make my implementation the same,  so I could produce similar results.  This is the shader code I came up with:</p>
<pre tabindex="0"><code>const static float4 vPurple = float4(0.7f, 0.2f, 0.9f, 1.0f);
const static float4 vOrange = float4(0.7f, 0.4f, 0.2f, 1.0f);
const static float fThreshold = 0.1f;

float4 LensFlarePS (    in float2 in_vTexCoord    : TEXCOORD0,
uniform int NumSamples,
uniform float4 vTint,
uniform float fTexScale,
uniform float fBlurScale)    : COLOR0
{
    // The flare should appear on the opposite side of the screen as the
    // source of the light, so first we mirror the texture coordinate.
    // Then we normalize so we can apply a scaling factor.
    float2 vMirrorCoord = float2(1.0f, 1.0f) - in_vTexCoord;
    float2 vNormalizedCoord = vMirrorCoord * 2.0f - 1.0f;
    vNormalizedCoord *= fTexScale;

    // We&#39;ll blur towards the center of screen, and also away from it.
    float2 vTowardCenter = normalize(-vNormalizedCoord);
    float2 fBlurDist = fBlurScale * NumSamples;
    float2 vStartPoint = vNormalizedCoord + ((vTowardCenter / g_vSourceDimensions) * fBlurDist);
    float2 vStep = -(vTowardCenter / g_vSourceDimensions) * 2 * fBlurDist;

    // Do the blur and sum the samples
    float4 vSum = 0;
    float2 vSamplePos = vStartPoint;
    for (int i = 0; i &lt; NumSamples; i++)
    {
        float2 vSampleTexCoord = vSamplePos * 0.5f + 0.5f;

        // Don&#39;t add in samples past texture border
        if (vSampleTexCoord.x &gt;= 0 &amp;&amp; vSampleTexCoord.x &lt;= 1.0f
            &amp;&amp; vSampleTexCoord.y &gt;=0 &amp;&amp; vSampleTexCoord.y &lt;= 1.0f)
        {
            float4 vSample = tex2D(PointSampler0, vSampleTexCoord);
            vSum +=  max(0, vSample - fThreshold) * vTint;
        }

        vSamplePos += vStep;
    }

    return vSum / NumSamples;
}

float4 CombinePS (in float2 in_vTexCoord    : TEXCOORD0) : COLOR0
{
    float4 vColor = tex2D(PointSampler0, in_vTexCoord);
    vColor += tex2D(PointSampler1, in_vTexCoord);
    vColor += tex2D(PointSampler2, in_vTexCoord);
    return vColor;
}

technique LensFlareFirstPass
{
    pass p0
    {
        VertexShader = compile vs_3_0 PostProcessVS();
        PixelShader = compile ps_3_0 LensFlarePS(12, vOrange, 2.00f, 0.15f);

        ZEnable = false;
        ZWriteEnable = false;
        AlphaBlendEnable = false;
        AlphaTestEnable = false;
        StencilEnable = false;
    }
}

technique LensFlareSecondPass
{
    pass p0
    {
        VertexShader = compile vs_3_0 PostProcessVS();
        PixelShader = compile ps_3_0 LensFlarePS(12, vPurple, 0.5f, 0.1f);

        ZEnable = false;
        ZWriteEnable = false;
        AlphaBlendEnable = false;
        AlphaTestEnable = false;
        StencilEnable = false;
    }
}
</code></pre><p>Obviously the code is severely unoptimized, but it&rsquo;s late and I&rsquo;m tired.  Here&rsquo;s a screen of what it looks like (ignore the obnoxious brightness and bloom, please):</p>
<p><a href="/images/converted/more-post-processing-tricks-lens-flare/screen.png"><img src="/images/converted/more-post-processing-tricks-lens-flare/screen_resized_300.png" alt="" title="Screen"></a></p>
<pre tabindex="0"><code></code></pre><hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://moorx.wordpress.com" title="mail.clang@gmail.com">moorx</a> - <time datetime="2009-12-15 04:46:31">Dec 2, 2009</time></p>
<p>looks great! thanks for sharing!</p>
<hr />
####
[gjoel]( "") - <time datetime="2009-12-15 04:49:43">Dec 2, 2009</time>
<p>Hey - the technique was taken from the kawase gdc-presentation from 2003 <a href="http://www.daionet.gr.jp/~masa/column/2003-03-21.html">http://www.daionet.gr.jp/~masa/column/2003-03-21.html</a> ( <a href="http://www.daionet.gr.jp/~masa/column/index.html">http://www.daionet.gr.jp/~masa/column/index.html</a> ) ( <a href="http://www.daionet.gr.jp/~masa/rthdribl/index.html">http://www.daionet.gr.jp/~masa/rthdribl/index.html</a> ) Cheers!</p>
<hr />
####
[Post Processing Trick from MJP &laquo; Sgt. Conker](http://www.sgtconker.com/2009/12/post-processing-trick-from-mjp/ "") - <time datetime="2009-12-15 11:17:52">Dec 2, 2009</time>
<p>[&hellip;] Matt Pettineo took a crack at a trick used in Killzone 2 for Lens Flare, and decided to share his new shader with the world. [&hellip;]</p>
<hr />
####
[Lens And Flair &laquo; Sgt. Conker](http://www.sgtconker.com/2009/12/lens-and-flair/ "") - <time datetime="2009-12-16 12:57:42">Dec 3, 2009</time>
<p>[&hellip;] Shows some code. [&hellip;]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Two Samples For The Price Of One</title>
			<link>https://therealmjp.github.io/posts/two-samples/</link>
			<pubDate>Sun, 06 Dec 2009 04:22:29 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/two-samples/</guid>
			<description>Today I have two XNA samples fresh out of the oven: a Motion Blur Sample, and Depth Of Field Sample. I figure all of the kids these days wanna add fancy post-processing tricks to their games, right? The motion blur sample shows you how to do camera motion blur using a depth buffer, or full object motion blur using a velocity buffer. The depth of field sample shows you how to do a standard blur-based DOF, a slightly-smarter blur-based DOF that doesn&amp;rsquo;t blur across edges, and the somewhat more physically accurate disc blur approach.</description>
			<content:encoded><![CDATA[<p>Today I have two XNA samples fresh out of the oven: a <a href="http://mynameismjp.wordpress.com/samples-tutorials-tools/motion-blur-sample/">Motion Blur Sample</a>, and <a href="http://mynameismjp.wordpress.com/samples-tutorials-tools/depth-of-field-sample/">Depth Of Field Sample</a>.  I figure all of the kids these days wanna add fancy post-processing tricks to their games, right?  The motion blur sample shows you how to do camera motion blur using a depth buffer, or full object motion blur using a velocity buffer. The depth of field sample shows you how to do a standard blur-based DOF, a slightly-smarter blur-based DOF that doesn&rsquo;t blur across edges, and the somewhat more physically accurate disc blur approach.</p>
<p>Get &rsquo;em while they&rsquo;re hot!</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://www.sgtconker.com/2009/12/two-samples-for-the-price-of-one-at-mjps-xna-danger-zone/" title="">Two Samples For The Price Of One at MJP’s XNA Danger Zone « Sgt. Conker</a> - <time datetime="2009-12-06 00:05:04">Dec 0, 2009</time></p>
<p>[&hellip;] Read more [&hellip;]</p>
<hr />
####
[Nelxon]( "nelxons@yahoo.com") - <time datetime="2010-12-30 09:57:44">Dec 4, 2010</time>
<p>It would be nice if these were updated to XNA 4.0&hellip; hint, hint</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>New Tutorial: Using PIX With XNA</title>
			<link>https://therealmjp.github.io/posts/pix-with-xna/</link>
			<pubDate>Fri, 16 Oct 2009 15:49:13 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/pix-with-xna/</guid>
			<description>Ladies and gentlemen, I present you with the most epic of tutorials: Using PIX With XNA. This 37-page monster teaches PIX for the XNA programmer, and includes an in-depth explanation of the XNA/D3D9 relationship as well as 6 excercises that show you the how to solve common problems (full source code and XNA 3.1 projects included). I sure hope somebody finds this thing useful&amp;hellip;it took me forever to write this thing.</description>
			<content:encoded><![CDATA[<p>Ladies and gentlemen, I present you with the most epic of tutorials: <a href="http://mynameismjp.wordpress.com/samples-tutorials-tools/pix-with-xna-tutorial/">Using PIX With XNA</a>.  This 37-page monster teaches PIX for the XNA programmer, and includes an in-depth explanation of the XNA/D3D9 relationship as well as 6 excercises that show you the how to solve common problems (full source code and XNA 3.1 projects included).  I sure hope somebody finds this thing useful&hellip;it took me <em>forever</em> to write this thing.</p>
<p>I originally intended to have this tutorial hosted on Ziggyware&hellip;in fact I finished this over a month ago and submitted it to Ziggy.  However as you may or may not know, Ziggy has become the unfortunate target of scumbag hackers who have repeatedly hijacked his site in order to deploy malware.  The whole thing absolutely sucks&hellip;I really wish that those assholes had decided to hijack a site that <em>wasn&rsquo;t</em> the most comprehensive collection of community-created XNA resources.  I hope Ziggy figures out a way to shake them and get the site up and running again&hellip;but it looks doubtful.  Honestly I don&rsquo;t think I&rsquo;d want to keep dealing with the kinds of problems he&rsquo;s gone though.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://steveproxna.blogspot.com" title="steven_boland@hotmail.com">SteveProXNA</a> - <time datetime="2011-02-15 12:31:44">Feb 2, 2011</time></p>
<p>Hi Matt, the PIX with XNA tutorial was really helpful. Had to mention on blog/twitter: <a href="http://bit.ly/eAClfB">http://bit.ly/eAClfB</a> Thanks, Steve.</p>
<hr />
####
[michal]( "") - <time datetime="2010-01-10 12:05:15">Jan 0, 2010</time>
<p>Thank you. PIX along with your tutorial helped me to find the bug in my code, that I was struggling with for a few days.</p>
<hr />
####
[Bennyboy]( "") - <time datetime="2009-11-15 20:45:37">Nov 0, 2009</time>
<p>Thanks a bunch for the tutorial. Quite informative</p>
<hr />
####
[shivajs]( "") - <time datetime="2009-10-16 18:01:11">Oct 5, 2009</time>
<p>I for one, am grateful</p>
<hr />
####
[Aaron Schultz]( "slade24@gmail.com") - <time datetime="2009-10-17 12:40:07">Oct 6, 2009</time>
<p>Haven&rsquo;t opened it yet, but I&rsquo;ve been looking for an intro to PIX from the standpoint of XNA for awhile (haven&rsquo;t done any true DirectX dev yet, and XNA is oh so comfy). Thanks a ton. Its people like you that make the XNA community work.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Scintillating Snippets: Storing Normals Using Spherical Coordinates</title>
			<link>https://therealmjp.github.io/posts/storing-normals-using-spherical-coordinates/</link>
			<pubDate>Wed, 17 Jun 2009 16:36:06 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/storing-normals-using-spherical-coordinates/</guid>
			<description>Update: n00body posted this link in the comments, which is way more in-depth than my post. Check it out!
If you&amp;rsquo;ve ever implemented a deferred renderer, you know that one of the important points is keeping your G-Buffer small enough as to be reasonable in terms of bandwidth and your number of render targets. Thanks to that constant struggle between good and evil, people have come up with some reasonable clever approaches towards packing necessary attributes in your G-Buffer.</description>
			<content:encoded><![CDATA[<p><em>Update:  n00body posted <a href="http://aras-p.info/texts/CompactNormalStorage.html">this link</a> in the comments, which is way more in-depth than my post.  Check it out!</em></p>
<p>If you&rsquo;ve ever implemented a deferred renderer, you know that one of the important points is keeping your G-Buffer small enough as to be reasonable in terms of bandwidth and your number of render targets.  Thanks to that constant struggle between good and evil, people have come up with some reasonable clever approaches towards packing necessary attributes in your G-Buffer.  One of the more popular approaches is that whole <a href="http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/http://">storing depth and reconstructing position thing</a>, and another is packing normals so that you only need 2 components instead of 3.</p>
<p>One of the more simple and common approaches is to only store the X and Y components of your view-space normals and then assume Z is positive (or negative, depending on whether you&rsquo;re using right-handed or left-handed coordinates).  As far as I know, this was first proposed <a href="http://www.guerrilla-games.com/publications/dr_kz2_rsx_dev07.pdf">here</a> by Guerilla Games. However there&rsquo;s a problem with this approach, which is that you can&rsquo;t always assume the sign of your Z component when you&rsquo;re using a perspective projection! This might seem weird at first (heck it took a while for someone to demonstrate to me why this is the case), but I assure you it&rsquo;s true.  Insomniac has some good pictures <a href="http://cmpmedia.vo.llnwd.net/o1/vault/gdc09/slides/gdc09_insomniac_prelighting.pdf">here</a> demonstrating the errors that occur.  So this means that if we want to use this technique and avoid errors, we have to pack the sign of Z somewhere in our two values. This is a little nasty, and takes away a bit of precision from one of your other values.</p>
<p>An alternative approach suggested to me a long time ago is to store the normal as a <a href="http://en.wikipedia.org/wiki/Spherical_coordinate">spherical coordinate</a>.  Since a normal is always a unit vector with length = 1, you can (safely) assume that Rho = 1 and just store Thetha and Phi.  Piece of cake!  All you have to do is implement the equations on the wiki page, take out the Rho&rsquo;s, and you&rsquo;ve got a two-component normal with excellent precision.</p>
<p>But wait, there&rsquo;s more!  It turns out if you use some trig-fu, you can actually further optimization to the conversions when Rho is equal to 1.  I was never actually good at simplifying equations with trig functions (I can do everything else, promise!) so I defer to the noble Pat Wilson who gave a quick rundown over in <a href="http://www.gamedev.net/community/forums/topic.asp?topic_id=535230">this thread</a>.  Make sure you check out his <a href="http://www.flickr.com/photos/killerbunny/sets/72157606936662291/">set of screenshots</a> that demonstrate the errors that occur from different normal storage options, so you can pick which method is right for you.</p>
<p>Also since this is Scinitillating Snippets and it wouldn&rsquo;t be much fun without a snippet, I&rsquo;ll post the HLSL functions I use for encoding and decoding my normals.  Just remember, all of the credit goes to Mr. Wilson.  I just did the pilfering!</p>
<pre tabindex="0"><code>// Converts a normalized cartesian direction vector
// to spherical coordinates.
float2 CartesianToSpherical(float3 cartesian)
{
    float2 spherical;
    spherical.x = atan2(cartesian.y, cartesian.x) / 3.14159f;
    spherical.y = cartesian.z;

    return spherical * 0.5f + 0.5f;
}

// Converts a spherical coordinate to a normalized
// cartesian direction vector.
float3 SphericalToCartesian(float2 spherical)
{
    float2 sinCosTheta, sinCosPhi;
    spherical = spherical * 2.0f - 1.0f;
    sincos(spherical.x * 3.14159f, sinCosTheta.x, sinCosTheta.y);
    sinCosPhi = float2(sqrt(1.0 - spherical.y * spherical.y), spherical.y);

    return float3(sinCosTheta.y * sinCosPhi.x, sinCosTheta.x * sinCosPhi.x, sinCosPhi.y);    
}
</code></pre><p>Also keep in mind that these functions normalize the values to the range [0,1], so that you can store in a regular fixed-point texture. If you&rsquo;re using a floating point texture you can remove the division by PI if you wish (and corresponding multiply by PI in the decode), as well as the &ldquo;multiply by 0.5, subtract by 0.5&rdquo;.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22defaultex@live.com%22">Default Ex</a> - <time datetime="2009-08-29 01:16:12">Aug 6, 2009</time></p>
<p>Oh wow, this technique does something I&rsquo;ve been spending even more instructions than this cost to produce. Hard edges (90 degree or more) sort of soften up through this technique when used with Linear filtering. Not to the point where it breaks the hard edge, but just enough to where it&rsquo;s easier on the eyes.</p>
<hr />
####
[n00body](http://n00body.squarespace.com/ "n00body.else@gmail.com") - <time datetime="2009-08-23 08:45:12">Aug 0, 2009</time>
<p>You should check out the work this guy is doing: <a href="http://aras-p.info/texts/CompactNormalStorage.html">http://aras-p.info/texts/CompactNormalStorage.html</a> He has come up with a variety of ways to store view-space normals, including a novel use of sphere mapping. For each technique, he has images of their error from the original, plus lists of compiled code and instruction counts. Definitely check it out. ;)</p>
<hr />
####
[Tomat]( "tomat1979@gmail.com") - <time datetime="2009-06-23 04:05:49">Jun 2, 2009</time>
<p>there will be a bug then you represent float3(0.0, 0.0, some_z) like vectors. and one will have a workaround for this, which also cost shaders instructions. :)</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>What&#39;s good on the menu, waiter?</title>
			<link>https://therealmjp.github.io/posts/whats-good-on-the-menu-waiter/</link>
			<pubDate>Wed, 20 May 2009 16:28:39 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/whats-good-on-the-menu-waiter/</guid>
			<description>I remember reading someone say on gamedev.net that at some point everyone tries to write their own UI system, and usually gets it wrong. Apparently he&amp;rsquo;s right (or at least about the first part), because I&amp;rsquo;ve gone ahead and written a menu/UI system. While it initially started out as part of the engine/framework I&amp;rsquo;ve been working on for my game, as I worked on it I decided it might be better off if I decoupled it from the rest of the engine components and made it a standalone library/editor package so that other people could make use of it.</description>
			<content:encoded><![CDATA[<p>I remember reading someone say on gamedev.net that at some point everyone tries to write their own UI system, and usually gets it wrong.  Apparently he&rsquo;s right (or at least about the first part), because I&rsquo;ve gone ahead and written a menu/UI system.  While it initially started out as part of the engine/framework I&rsquo;ve been working on for my game, as I worked on it I decided it might be better off if I decoupled it from the rest of the engine components and made it a standalone library/editor package so that other people could make use of it.</p>
<p>While designing and implementing I had these goals in mind:</p>
<ul>
<li>
<p>Keep it simple!  Make menu elements useful by default, but don&rsquo;t cram in tons of functionality with limited use.  Just let them be flexible enough so that they can be customized for unusual cases.</p>
</li>
<li>
<p>Cross-platform, with a focus on Xbox 360.  Should look identical on both, and expose the same functionality regardless of input method.</p>
</li>
<li>
<p>Page-based layout. A few of the other GUI packages out there seem to be aimed at recreating WinForms using XNA&hellip;and I think that&rsquo;s silly.  You don&rsquo;t want sizeable windows for a game (or at least not most games), you want menus that are logically divided up into pages that you can switch between.</p>
</li>
<li>
<p>A PC-only editor application that lets you visually design your menus.   The core library should be aware of the fact that it can run in a designer, and provide support for this.</p>
</li>
<li>
<p>Free and open-source!</p>
</li>
</ul>
<p>What I ended up with is the <a href="http://cpxms.codeplex.com/">CPX Menu System</a>.  It actually came out better than I expected&hellip;the editor is very stable and works pretty nicely.  It could use somore more fancy features (like tools for lining up menu items), but it definitely WORKS and I&rsquo;m happy about that.  As for the menu item types included in the library itself&hellip;it&rsquo;s pretty bare-bones but you can still do a lot with them.  I mean personally for my game I wouldn&rsquo;t really need a whole lot more than what I put in the sample app.</p>
<p>Probably the biggest weakness it has working with content is a bit awkward.  Early on a I struggled a lot with trying to come up with a good way to handle it&hellip;and I don&rsquo;t feel like I ever really came up with a killer solution.  As of right now the way it works is that the editor app itself does not build any content at runtime.  This isn&rsquo;t so nice, since you have to have Content compiled ahead of time before you run the app.  The upside is that editor doesn&rsquo;t depend on the content pipeline assemblies at all, so you can run it on a PC that doesn&rsquo;t have the full XNA GS install.  Probably the easiest way to manage content is to just add all of your menu content to the CPXMenu project&rsquo;s Content project.  If you do that, then you will always have the content available for the editor and your game (assuming you&rsquo;re always building the editor in VS and running it that way).  Otherwise you can tell the editor to look for content in a specific path whenever it loads a project.  This is what I did for the sample app: it has its own Content project with some custom textures, so I set the editor to look in the output folder for that project.</p>
<p>I guess that&rsquo;s it for now&hellip;at some point I suppose I&rsquo;ll announce it on Ziggyware.  Maybe after I add some documentation explaining how to <em>use</em> the damned thing.  In the meantime, here&rsquo;s some screenshots of the sample app and the editor:</p>
<p><img src="/images/converted/whats-good-on-the-menu-waiter/sample2.png" alt="screenshot1"></p>
<p><img src="/images/converted/whats-good-on-the-menu-waiter/editor.png" alt="screenshot2"></p>
<p><img src="/images/converted/whats-good-on-the-menu-waiter/sample1.png" alt="screenshot3"></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://blogs.msdn.com/xna/archive/2009/05/27/creators-club-communiqu-29.aspx" title="">XNA Team Blog : Creators Club Communiqué 29</a> - <time datetime="2009-05-27 19:47:59">May 3, 2009</time></p>
<p>[&hellip;] for the XNA Framework Content Pipeline! But that’s not all! During that time Matt also shared a menu/UI system he wrote! How cool is [&hellip;]</p>
<hr />
####
[Creators Club Communiqué 29 | Coded Style](http://www.codedstyle.com/creators-club-communique-29-3/ "") - <time datetime="2009-05-27 21:46:34">May 3, 2009</time>
<p>[&hellip;] for the XNA Framework Content Pipeline! But that’s not all! During that time Matt also shared a menu/UI system he wrote! How cool is [&hellip;]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Reconstructing Position From Depth, Continued</title>
			<link>https://therealmjp.github.io/posts/reconstructing-position-from-depth-continued/</link>
			<pubDate>Tue, 05 May 2009 20:09:33 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/reconstructing-position-from-depth-continued/</guid>
			<description>Picking up where I left off here&amp;hellip;
As I mentioned, you can also reconstruct a world-space position using the frustum ray technique. The first step is that you need your frustum corners to be rotated so that they match the current orientation of your camera. You can do this by transforming the frustum corners by a &amp;ldquo;camera world matrix&amp;rdquo;, which is a matrix representing the camera&amp;rsquo;s position and orientation in world-space.</description>
			<content:encoded><![CDATA[<p>Picking up where I left off <a href="http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/">here</a>&hellip;</p>
<p>As I mentioned, you can also reconstruct a world-space position using the frustum ray technique.  The first step is that you need your frustum corners to be rotated so that they match the current orientation of your camera.  You can do this by transforming the frustum corners by a &ldquo;camera world matrix&rdquo;, which is a matrix representing the camera&rsquo;s position and orientation in world-space.  If you don&rsquo;t have this available you can just invert your view matrix.  I&rsquo;ll demonstrate doing it right in the vertex shader for the sake of simplicity, but you&rsquo;d probably want to do it ahead of time in your application code.</p>
<pre tabindex="0"><code>// Vertex shader for rendering a full-screen quad
void QuadVS (   in float3 in_vPositionOS                : POSITION,
                in float3 in_vTexCoordAndCornerIndex    : TEXCOORD0,
                out float4 out_vPositionCS              : POSITION,
                out float2 out_vTexCoord                : TEXCOORD0,
                out float3 out_vFrustumCornerWS         : TEXCOORD1 )
{
    // Offset the position by half a pixel to correctly
    // align texels to pixels. Only necessary for D3D9 or XNA
    out_vPositionCS.x = in_vPositionOS.x - (1.0f/g_vOcclusionTextureSize.x);
    out_vPositionCS.y = in_vPositionOS.y + (1.0f/g_vOcclusionTextureSize.y);
    out_vPositionCS.z = in_vPositionOS.z;
    out_vPositionCS.w = 1.0f;

    // Pass along the texture coordinate and the position
    // of the frustum corner in world-space.  This frustum corner
    // position is interpolated so that the pixel shader always
    // has a ray from camera-&gt;far-clip plane
    out_vTexCoord = in_vTexCoordAndCornerIndex.xy;
    float3 vFrustumCornerVS = g_vFrustumCornersVS[in_vTexCoordAndCornerIndex.z];
    out_vFrustumCornerWS = mul(vFrustumCornerVS, g_matCameraWorld);
}
</code></pre><p>So what we&rsquo;ve done here is we&rsquo;ve <em>rotated</em> (not translated, since vFrusumCornerVS is only a float3) the view-space frustum corner so that it&rsquo;s now matches the camera&rsquo;s orientation.  However it&rsquo;s still centered around &lt;0,0,0&gt; and not the camera&rsquo;s world-space position, so when we reconstruct position we&rsquo;ll also add the camera&rsquo;s world-space position:</p>
<pre tabindex="0"><code>// Pixel shader function for reconstructing world-space position
float3 WSPositionFromDepth(float2 vTexCoord, float3 vFrustumRayWS)
{
    float fPixelDepth = tex2D(DepthSampler, vTexCoord).r;
    return g_vCameraPosWS + fPixelDepth * vFrustumRayWS;
}
</code></pre><p>And there it is. Easy peasy, lemon squeezy.</p>
<p>The other bit I hinted at was using this same technique with arbitray geometry, for example  the bounding volumes for a local light source.  For this we once again need a ray that points from the camera position through the pixel position to the far-clip plane.  We can do this in the pixel shader by using the view-space position of the pixel.</p>
<pre tabindex="0"><code>void VSBoundingVolume(  in float3 in_vPositionOS       : POSITION,
                        out float4 out_vPositionCS     : POSITION,
                        out float3 out_vPositionVS    : TEXCOORD0 )
{
    out_vPositionCS = mul(in_vPositionOS, g_matWorldViewProj);    

    // Pass along the view-space vertex position to the pixel shader
    out_vPositionVS = mul(in_vPositionOS, g_matWorldView);
}
</code></pre><p>Then in our pixel shader, we calculate the ray and reconstruct position like this:</p>
<pre tabindex="0"><code>float3 VSPositionFromDepth(float2 vTexCoord, float3 vPositionVS)
{
    // Calculate the frustum ray using the view-space position.
    // g_fFarCip is the distance to the camera&#39;s far clipping plane.
    // Negating the Z component only necessary for right-handed coordinates
    float3 vFrustumRayVS = vPositionVS.xyz * (g_fFarClip/-vPositionVS.z);
    return tex2D(DepthSampler, vTexCoord).x * vFrustumRayVS;
}
</code></pre><p>So there you go, I did your homework for you.  Now stop beating me up in the schoolyard!</p>
<p>EDIT: Fixed the code and explanation so that it actually works now!  Big thanks to Bill and Josh for pointing out the mistake.</p>
<p>UPDATE: More position from depth goodness <a href="http://mynameismjp.wordpress.com/2010/09/05/position-from-depth-3/">here</a></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/" title="">Scintillating Snippets: Reconstructing Position From Depth « MJP’s XNA Danger Zone</a> - <time datetime="2009-05-05 15:14:43">May 2, 2009</time></p>
<p>[&hellip;] Answers to extra credit questions here Posted in DirectX, Programming, XNA. 2 Comments [&hellip;]</p>
<hr />
####
[Phil]( "phil.teschner@gmail.com") - <time datetime="2009-05-06 13:07:11">May 3, 2009</time>
<p>I don&rsquo;t think your solution for arbitrary geometry works. You alluded to the problems yourself in your previous post. Interpolationg (xyz / z) per-vertex doesn&rsquo;t work as it is not a linear operation. You have to do the division in the pixel shader for this to work.</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2009-11-17 17:56:58">Nov 2, 2009</time>
<p>You need to do this: wsPos = mul(float4(wsPos, 1.0f), InvertView); If you don&rsquo;t convert to a float4 and set w = 1.0, then your view-space position won&rsquo;t get transformed by the translation part of your inverse view matrix (in other words, it will only get rotated).</p>
<hr />
####
[Icaton](http://www.wailingninja.com "guldbran@gmail.com") - <time datetime="2009-11-17 05:08:57">Nov 2, 2009</time>
<p>Hi! I have a problem getting the last technique to work. VSPositionFromDepth gets the position in view space right? So in order to obtain the reconstructed world space position, I multiply the resulting value with an inverse view matrix like this: //View space position float3 wsPos = VSPositionFromDepth(tex, input.vsPos); //Transform to world space //wsPos = mul(wsPos, InvertView); Where the texcoords are: input.ssPos.xy /= input.ssPos.w; //Transforming from [-1,1]-&gt;[1,-1] to [0,1]-&gt;[1,0] float2 tex = (0.5f * (float2(input.ssPos.x, -input.ssPos.y) + 1)) - halfPixel; Where SsPos equals csPos in the example. Well&hellip;the problem is that it doesn&rsquo;t work, the ws positionons are incorrect. Any ideas of what I&rsquo;m doing wrong?</p>
<hr />
####
[Game Rendering &raquo; Position Reconstruction](http://www.gamerendering.com/2009/12/07/position-reconstruction/ "") - <time datetime="2009-12-07 10:34:14">Dec 1, 2009</time>
<p>[&hellip;] “Reconstructing Position From Depth, Continued” <a href="http://mynameismjp.wordpress.com/2009/05/05/reconstructing-position-from-depth-continued/">http://mynameismjp.wordpress.com/2009/05/05/reconstructing-position-from-depth-continued/</a> Please [&hellip;]</p>
<hr />
####
[Default Ex]( "defaultex@live.com") - <time datetime="2009-08-30 20:22:20">Aug 0, 2009</time>
<p>Forgot to mention in that last one, you alike the first one multiply view space depth (negated if your not using floating point buffers) then add camera position.</p>
<hr />
####
[Default Ex]( "defaultex@live.com") - <time datetime="2009-08-30 20:20:05">Aug 0, 2009</time>
<p>A heads up to those using the first technique, it expects coordinates in the 0 to 1 range. Which should have been apparent with the texCoord parameter. My own world space arbitrary implementation: float3 GetFrustumRay(in float2 screenPosition) { float2 sp = sign(screenPosition); return float3(Camera.FrustumRay.x * sp.x, Camera.FrustumRay.y * sp.y, Camera.MaxDepth); } The Camera.FrustumRay is calculated in the application using the following: Vector2 frustumRay = new Vector2(); frustumRay.Y = (float)Math.Tan(Math.PI / 3.0 / 2.0) * camera.Viewport.MaxDepth; frustumRay.X = -(frustumRay.Y * camera.Viewport.AspectRatio);</p>
<hr />
####
[Josh]( "") - <time datetime="2009-06-20 23:11:28">Jun 6, 2009</time>
<p>Phil was correct. I tried doing the calculation in the vertex shader for my code, and it introduced very nasty visual artifacts. When I moved the calculation to the fragment shader, it produced the correct results.</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2009-07-20 09:55:34">Jul 1, 2009</time>
<p>Yup, you guys are right. For a while I was trying to figure out why I wasn&rsquo;t getting artifacts&hellip;and then I realized that in code I was calculating the ray in the pixel shader too. Whoops. :-D Thanks everyone for pointing it out, much appreciated.</p>
<hr />
####
[GALOP1N]( "galopin@gmail.com") - <time datetime="2010-03-27 05:18:24">Mar 6, 2010</time>
<p>Hi, I have a clever solution for you. It allows reconstruction of the view position in only two mul and no computation at all in the application. First you need to output the depth in view space of your image. You can choose a R32F a G16R16F or anything you want. Second, when you need to retrieve the pixel position in view space. Like in a full screen post process, draw a quad with those vertices (-1,-1) (-1,1) (1,1) (1,-1). If you need it in a real geometry, just send the xyw of the projected position and divide xy by w in the pixel shader Let&rsquo;s go with some math now: 1. we have a well know projection matrix with lot of zero and some value A 0 0 0 0 B 0 0 0 0 C -1 0 0 D 0 let&rsquo;s write the process to transform a view space position to projection space ( Vw == 1 ) Px = Vx * A + Vy * 0 + Vz * 0 + 1 * 0 Py = Vx * 0 + Vy * B + Vz * 0 + 1 * 0 Pw = Vx * 0 + Vy * 0 - 1 * Vz + 1 * 0 Now let&rsquo;s reconstruct Vx. What we know in your fragment program is the interpolated pixel position in projected space ( Px/Pw ). let&rsquo;s call it Ix for InterpolatedX so: Px/Pw = Ix Vx * A / -Vz = Ix Vx = Ix * Vz * ( -1 / A) if we do the same for Vy we got Vy = Iy * Vz * ( -1 / B). Let&rsquo;s write this in a HLSL form. // Vertex part out.position = in.position; out.projpos.xy = in.position.xy; out.magiccoef = -1.f / float2( gProj(0][0], gProj[1][1] // Pixel part float3 viewposition; viewposition.z = tex2D( /***/ ).x; viewposition.xy = viewposition.zz * in.magiccoef.xy * in.projpos.xy; You know have your view space position, you can transform it to World space with a transform with the inverse view or keep it this way and do your math in view space. Voilà :)</p>
<hr />
####
[Attack of the depth buffer &laquo; The Danger Zone](http://mynameismjp.wordpress.com/2010/03/22/attack-of-the-depth-buffer/ "") - <time datetime="2010-03-22 23:42:46">Mar 1, 2010</time>
<p>[&hellip;] by the distance to the far-clip plane.  Position was reconstructed using the method decribed here. For formats marked “Perspective Z/W”, the depth was calculated by interpolating the z [&hellip;]</p>
<hr />
####
[Tiago]( "tjcostinha@gmail.com") - <time datetime="2010-06-12 02:26:49">Jun 6, 2010</time>
<p>Some more questions lol: -g_matCameraWorld is the inverse of view matrix or inverse of world-view matrix? -g_vCameraPosWS is the camera position multiplied by the world matrix or simply the camera position vector? -How do I calculate vFrustumRayWS? float3 vFrustumRayWS = vPositionWS.xyz * (g_fFarClip/-vPositionWS.z);</p>
<hr />
####
[n00body](http://n00body.squarespace.com/ "n00body.else@gmail.com") - <time datetime="2010-05-24 09:08:53">May 1, 2010</time>
<p>Interesting trick, GALOP1N. On the whole, this actually seems like it would be cheaper than the prevalent method from Crytek. The &ldquo;magiccoef&rdquo; can be calculated outside the shaders, but is still significantly cheaper to do than the frustum corner. Then the actual stored depth needn&rsquo;t be normalized &amp; negated, so that is another savings. Of course, anything relying on the depth being in the range [0,1] might be affected, but that will be situation-specific. I&rsquo;m gonna try using this from now on, and see how well it holds up. Thanks. :)</p>
<hr />
####
[Sebastian Scholle (@sscholle)](http://twitter.com/sscholle "sscholle@twitter.example.com") - <time datetime="2015-06-29 01:44:31">Jun 1, 2015</time>
<p>where can i find a GLSL example of this code?</p>
<hr />
####
[irvingpliy.tblog.com](http://irvingpliy.tblog.com "inessteel@gmail.com") - <time datetime="2013-07-14 16:29:01">Jul 0, 2013</time>
<p>This is a really good tip especially to those fresh to the blogosphere. Short but very accurate information… Appreciate your sharing this one. A must read article!</p>
<hr />
####
[obama care taxes](http://beauctqa.blogspace.fr "marvin-burrow@gmail.com") - <time datetime="2013-07-31 03:00:24">Jul 3, 2013</time>
<p>I rarely leave comments, but after looking at a bunch of responses on Reconstructing Position From Depth, Continued | The Danger Zone. I do have a couple of questions for you if you don&rsquo;t mind. Is it simply me or does it appear like some of these remarks look as if they are left by brain dead folks? :-P And, if you are posting at other online sites, I&rsquo;d like to follow you. Would you post a list of every one of your shared sites like your Facebook page, twitter feed, or linkedin profile?</p>
<hr />
####
[hyuk jae lee]( "oiloveyouio@naver.com") - <time datetime="2016-09-05 07:29:36">Sep 1, 2016</time>
<p>hi! I am a student studying in Korea. I do not speak English well There was wondering Why do you need four frustum corner?</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Undo and Redo: Take 2</title>
			<link>https://therealmjp.github.io/posts/undo-and-redo-take-2/</link>
			<pubDate>Thu, 30 Apr 2009 19:37:03 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/undo-and-redo-take-2/</guid>
			<description>Please excuse the rhyming in the title&amp;hellip;sometimes I just can&amp;rsquo;t help myself. It&amp;rsquo;s a problem.
A few weeks ago I started working on a super-duper-secret project (to be revealed soon), a big part of which was a new editor. Since I&amp;rsquo;m the kind of guy who gets all worked up about having proper undo and redo support, I took the opportunity to make it an up-front part of my design rather than just shoving it in afterwords.</description>
			<content:encoded><![CDATA[<p>Please excuse the rhyming in the title&hellip;sometimes I just can&rsquo;t help myself.  It&rsquo;s a problem.</p>
<p>A few weeks ago I started working on a  super-duper-secret project (to be revealed soon), a big part of which was a new editor.   Since I&rsquo;m the kind of guy who gets <a href="http://mynameismjp.wordpress.com/2008/12/19/undo-and-redo/">all worked up</a> about having proper undo and redo support, I took the opportunity to make it an up-front part of my design rather than just shoving it in afterwords.</p>
<p>One of the things I&rsquo;d thought about for map editor was having a well-defined boundary between the user&rsquo;s input and actions that could be performed on the document.  For the map editor it was too late for that, but this time I could put it in from the start.  What I came up with was the ActionManager (yeah I know, bad name.  Sue me.).  It provides as public methods a variety of actions that can be performed on the document: adding a new item, removing an item, setting a property on an item, etc.   When one of these methods gets called it creates an IEditAction derivative, configures it, has the IEditAction &ldquo;do&rdquo; the action, and then pushes it onto the Undo stack.  So similar to what I had previously in my map editor, except that the EditActions actually perform the action the first time around and all the Undo/Redo stuff is wrapped up in a nice class.  It&rsquo;s also less error prone, because you go through the ActionManager layer rather than going directly to the document (this helps ensure that everything the user does goes through the proper Undo/Redo jazz).</p>
<p>I also managed to get it down to just three EditAction&rsquo;s: AddRemoveItemAction, PropertyEditAction, and CompoundAction.  The first is for adding and removing items to the document, the second is for whenever an item&rsquo;s property is modified (this is the majority of actions), and the third just represents multiple AddRemoveItemAction&rsquo;s and/or PropertyEditAction&rsquo;s that are peformed as the result of a single user action.   It still doesn&rsquo;t necessarily deal with the problem of having the number of EditAction&rsquo;s explode as the app grows, but it helps that Reflection in .NET is awesome enough to let me use PropertyEditAction for just about <em>everything</em>.</p>
<p>The one problem I still had to deal with was the stupid PropertyGrid.  The PropertyGrid is fantastic, but it&rsquo;s not realy set up for Undo and Redo.  Well that&rsquo;s a lie, it sorta is.  See it raises a PropertyValueChanged event whenever a property value changes, and the EventArgs conveniently has an OldValue property that tells you what the previous value was.  Great, right?  Right&hellip;except  for the fact that this is null when you have multple objects selected on the PropertyGrid.  Not so great.</p>
<p>This led me to approach #1:  each time an item is supposed to be set onto the PropertyGrid, create a &ldquo;proxy&rdquo; item byt cloning the original and set that onto the PropertyGrid.  Then whenever a property value is changed, I can look up the &ldquo;real item, query it for the old property value, and then actually set the property value via the ActionManager.  And this worked&hellip;at first.  Where I ran into problems was where setting properties on an item affected the state of another item.  For instance items have an &ldquo;Index&rdquo; property that controls the index within a parent item&rsquo;s children collection.  So setting that property causes the item to send a request to the parent item for a reorder of the children, and that might fail based on the state of parent.  This means that if I leave my references hooked up properly in my clone I end up with a situation where actions like that get performed twice (and usually failing the second time), or if I  &ldquo;detatch&rdquo; a clone from all outside references I lose my error verification (not to mention the fact that I have to be very very careful in how I clone something).</p>
<p>This brought me to attempt #2, which I consider uglier but has actually worked out: every time the user selects a GridItem in the PropertyGrid, save the current state of the Property for all selected items so that I have an OldValue.</p>
<pre tabindex="0"><code>private GridItem GetRootReferenceGridItem(GridItem gridItem)
{
    GridItem rootItem = gridItem;
    if (!rootItem.PropertyDescriptor.ComponentType.IsValueType)
        return rootItem;

    while (gridItem.Parent.Parent != null)
    {
        gridItem = gridItem.Parent;
        if (gridItem.PropertyDescriptor != null
            &amp;&amp; !gridItem.PropertyDescriptor.ComponentType.IsValueType)
        {
            rootItem = gridItem;
            break;
        }
    }

    return rootItem;
}

private void SetOldValues()
{
    GridItem gridItem = propertyGrid.SelectedGridItem;

    if (gridItem != null &amp;&amp; gridItem.GridItemType == GridItemType.Property)
    {
        gridItem = GetRootReferenceGridItem(gridItem);
        oldValues = new object[selectedItems.Count];
        for (int i = 0; i &lt; oldValues.Length; i++)
            oldValues[i] = gridItem.Value;
    }
    else
        oldValues = null;
}

void propertyGrid_SelectedGridItemChanged(object sender, SelectedGridItemChangedEventArgs e)
{
    if (e.NewSelection.PropertyDescriptor == null
        || e.OldSelection == null
        || e.OldSelection.PropertyDescriptor == null
        || e.NewSelection.PropertyDescriptor.Name != e.OldSelection.PropertyDescriptor.Name)
        SetOldValues();

}

void propertyGrid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
{
    object[] items = new object[selectedItems.Count];

    // Trace backwards through the chain of properties until we find
    // the first property
    List&lt;string&gt; propertyChain = new List&lt;string&gt;();
    GridItem gridItem = GetRootReferenceGridItem(e.ChangedItem);
    string propertyName = gridItem.PropertyDescriptor.Name;

    while (gridItem.Parent.Parent != null)
    {
        gridItem = gridItem.Parent;
        if (gridItem.PropertyDescriptor != null)
            propertyChain.Add(gridItem.PropertyDescriptor.Name);
    }

    // Now walk the chain and find the owner of the property or field that was modified
    for (int i = 0; i &lt; selectedItems.Count; i++)
    {
        items[i] = selectedItems[i];
        object nextItem = items[i];
        for (int j = propertyChain.Count - 1; j &gt;= 0; j--)
            items[i] = ActionManager.GetPropertyOrFieldValue(items[i], propertyChain\[j]);
    }

    actionManager.PropertyValueChanged(items, propertyName, oldValues);

    SetOldValues();
}
</code></pre><p>The main problem with this is that the PropertyGrid is now editing a &ldquo;live&rdquo; object: changes it makes to items actually affect their state.  This unfortunately broke my &ldquo;everything must go through the ActionManager&rdquo; philosophy, but I couldn&rsquo;t think of any better alternatives.  So I added a new method to the ActionManager that allows me to &ldquo;register&rdquo; that a property value was changes after the fact.  It basically works the same as the old ChangePropertyValue method, except that it doesn&rsquo;t call &ldquo;Do&rdquo; on the PropertyEditAction after it creates it.   So yeah kinda ugly&hellip;but it works.  Good enough, I guess.</p>
]]></content:encoded>
		</item>
		
		<item>
			<title>There&#39;s More Than One Way To Defer A Renderer</title>
			<link>https://therealmjp.github.io/posts/theres-more-than-one-way-to-defer-a-renderer/</link>
			<pubDate>Fri, 27 Mar 2009 19:21:49 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/theres-more-than-one-way-to-defer-a-renderer/</guid>
			<description>While the idea of deferred shading/deferred rendering isn&amp;rsquo;t quite as hot as it was year or two ago (OMG, Killzone 2 uses deferred rendering!), it&amp;rsquo;s still a cool idea that gets discussed rather often. People generally tend to be attracted to way a &amp;ldquo;pure&amp;rdquo; deferred renderer neatly and cleanly separates your geometry from your lighting, as well as the idea of being able to throw lights everywhere in their scene. However as anyone who&amp;rsquo;s done a little bit of research into the topic surely knows, it comes with a few drawbacks.</description>
			<content:encoded><![CDATA[<p>While the idea of deferred shading/deferred rendering isn&rsquo;t quite as hot as it was  year or two ago (OMG, <a href="http://www.guerrilla-games.com/publications/dr_kz2_rsx_dev07.pdf">Killzone 2 uses deferred rendering</a>!), it&rsquo;s still a cool idea that gets discussed rather often.  People generally tend to be attracted to way a &ldquo;pure&rdquo; deferred renderer neatly and cleanly separates your geometry from your lighting, as well as the idea of being able to throw lights everywhere in their scene.  However as anyone who&rsquo;s done a little bit of research into the topic surely knows, it comes with a few drawbacks.  The main ones being that for MSAA you need to individually light all your subsamples (which isn&rsquo;t doable in D3D9), and also that for non-opaque objects you have use forward rendering anyway.</p>
<p>The neat thing about the concepts involved with deferred shading is that you&rsquo;re not all locked into the typical &ldquo;render depth+normals+diffuse+specular to a fat G-Buffer and then shade&rdquo; approach.  I&rsquo;m not sure enough people are aware of this, and appreciate it.  For example, you can just <a href="http://mynameismjp.wordpress.com/2009/01/19/deferred-shadow-maps-sample/">defer your shadow map calculations</a> to gain the related performance and organization benefits, and then use standard forward rendering techniques for everything else.  Or you can <a href="http://diaryofagraphicsprogrammer.blogspot.com/2008/03/light-pre-pass-renderer.html">reconfigure the deferred lighting pipeline</a> to gain back the ability to have multiple materials, or the ability to multisample without shading individual subsamples.  Surely there are even more possibilities!</p>
<p>Recently while working on my own game, I was grappling with the issue of having my engine support more local light sources in a scene.   I was using standard forward lighting with up to 3 lights per pass (which was fine), but I really wanted to keep my DrawPrimitives calls to a minium (due to how painful they can be on the 360).  This was problem since I&rsquo;m aggressively batching my mesh rendering using instancing, and sorting instances by which light affects them would cause by batches to increase.  Thus, I was using 3 &ldquo;global&rdquo; light sources per frame.  This has obvious drawbacks.</p>
<p>While I was thinking over solutions, I considered the importance of smaller local lights that are relatively far away in the scene.  At further distances, it&rsquo;s not necessarilly too important to have &ldquo;correct&rdquo; lighting.  In fact, we basically just need something that&rsquo;s the right color, makes the area brighter, and doesn&rsquo;t shade surfaces facing away from the light source.  So I thought: &ldquo;I already have view-space depth&hellip;if I can calculate view-space normals I canget what I want by using a deferred pass&rdquo;.  So I did exactly this&hellip;and it didn&rsquo;t work very well.  The problem was that even though you can a calculate view-space normal from a depth value by calculating the partial derivatives and taking a cross product, the normals you calculate aren&rsquo;t smoothly interpolated between vertices.  So what you get is something that looks an awful lot like flat shading.  Ewwwwwwwwwwww.</p>
<p>This lead to approach #2:  in the depth-only pass, render to a RGBA16F surface instead of a R32F surface and render out depth + view-space normals as interpolated from the vertex normals.  This worked much better!  The only remaining issue (aside from the fact that I just hard-code a diffuse albedo and specular albedo), is that normal-maps aren&rsquo;t used.  However even with that those problems the results are still decent, as long as surface colors are primarily determined by your forward rendering pass and the local light are just &ldquo;extra&rdquo;.  Here&rsquo;s screenshots of a test scene with forward rendering, and then with the point lights deferred:</p>
<p><img src="/images/converted/theres-more-than-one-way-to-defer-a-renderer/jumpswitchpc-2009-03-15-22-59-11-31.png" alt="Point lights"></p>
<p><img src="/images/converted/theres-more-than-one-way-to-defer-a-renderer/jumpswitchpc-2009-03-15-23-02-18-66.png" alt="Point lights, deferred"></p>
<p>The results are clearly not as good as a full forward pass when you have them side-by-side, but I think they&rsquo;re probably good enough&hellip;especially if I only use this technique for lights that are small or far-away.  The trick is going to be transferring smoothly from deferred to forward, but that&rsquo;s certainly doable.</p>
<p>One downside that came with this was that since I was just additively blending in the lights, I couldn&rsquo;t use my beloved LogLuv encoding for HDR.  My next-best option of the 360 was to normalize R10G10B10A2 to a range greater than [0,1].  I ended up having to normalize to [0,8] to get the dynamic range I wanted, and unfortunately this can give some visible banding in certain cases.  And alternative I&rsquo;ll have to explore is rendering just the point lights to an R10G10B10A2 buffer, and then sending this to my forward rendering pass to be sampled and added to the result.  If I did this I could also use the light prepass approach, and gain back material parameters and proper MSAA for the point lights.</p>
<p>Anyway I&rsquo;m not saying that what I&rsquo;m doing is that particularly interesting or useful, I&rsquo;m just trying to demonstrate that there are many possibilities to explore.  It&rsquo;s good to think out of the box every once in a while!</p>
]]></content:encoded>
		</item>
		
		<item>
			<title>Scintillating Snippets: Reconstructing Position From Depth</title>
			<link>https://therealmjp.github.io/posts/reconstructing-position-from-depth/</link>
			<pubDate>Tue, 10 Mar 2009 19:06:31 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/reconstructing-position-from-depth/</guid>
			<description>There are times I wish I&amp;rsquo;d never responded to this thread over at GDnet, simply because of the constant stream of PM&amp;rsquo;s that I still get about it. Wouldn&amp;rsquo;t it be nice if I could just pull out all the important bits, stick it on some blog, and then link everyone to it? You&amp;rsquo;re right, it would be!
First things first: what am I talking about? I&amp;rsquo;m talking about something that finds great use for deferred rendering: reconstructing the 3D position of a previously-rendered pixel (either in view-space or world-space) from a single depth value.</description>
			<content:encoded><![CDATA[<p>There are times I wish I&rsquo;d never responded to <a href="http://www.gamedev.net/community/forums/topic.asp?topic_id=474166&amp;PageSize=25&amp;WhichPage=1">this thread</a> over at GDnet, simply because of the constant stream of PM&rsquo;s that I still get about it.  Wouldn&rsquo;t it be nice if I could just pull out all the important bits, stick it on some blog, and then link everyone to it?  You&rsquo;re right, it would be!</p>
<p>First things first: what am I talking about?  I&rsquo;m talking about something that finds great use for deferred rendering: reconstructing the 3D position of a previously-rendered pixel (either in view-space or world-space) from a single depth value.  In practice, it&rsquo;s really not terribly complicated.  You intrinsically know (or can figure out) the 2D position of any pixel when you&rsquo;re shading it, which means that if you can sample a depth value you can get the whole 3D position.  However it&rsquo;s still easy to get tripped up due to the fact that there&rsquo;s several ways to go about it, coupled with the fact that many beginners aren&rsquo;t very proficient at debugging their shaders.</p>
<p>Let&rsquo;s talk about the first way to do it: storing post-projection z/w, combining it with x/w and y/w, transforming by the inverse of the projection matrix, and dividing by w.  In HLSL it looks something like this&hellip;</p>
<pre tabindex="0"><code>// Depth pass vertex shader
output.vPositionCS = mul(input.vPositionOS, g_matWorldViewProj);
output.vDepthCS.xy = output.vPositionCS.zw;

// Depth pass pixel shader (output z/w)
return input.vDepthCS.x / input.vDepthVS.y;

// Function for converting depth to view-space position
// in deferred pixel shader pass.  vTexCoord is a texture
// coordinate for a full-screen quad, such that x=0 is the
// left of the screen, and y=0 is the top of the screen.
float3 VSPositionFromDepth(float2 vTexCoord)
{
    // Get the depth value for this pixel
    float z = tex2D(DepthSampler, vTexCoord);  
    // Get x/w and y/w from the viewport position
    float x = vTexCoord.x * 2 - 1;
    float y = (1 - vTexCoord.y) * 2 - 1;
    float4 vProjectedPos = float4(x, y, z, 1.0f);

    // Transform by the inverse projection matrix
    float4 vPositionVS = mul(vProjectedPos, g_matInvProjection);  

    // Divide by w to get the view-space position
    return vPositionVS.xyz / vPositionVS.w;  
}
</code></pre><p>For many this is the preferred approach since it works with hardware depth buffers.  It also may seem natural to some: we get depth by projection, we get position by un-projecting.  But what if we don&rsquo;t have access to a hardware depth buffer?  If you&rsquo;re targeting the PC and D3D9,  sampling from a depth buffer as if it were a texture is not straightforward since it requires driver hacks.  If you&rsquo;re using XNA, it&rsquo;s not possible at all since the framework generally attempts to main cross-plaftorm compatibility between the PC and the Xbox 360.  In these cases, we can simply render out a depth buffer ourselves using the vertex and pixel shader bits I posted above.  But is this really a good idea?  z/w is non-linear, and most of the precision will be dedicated to areas very close to the near-clip plane.</p>
<p>A different approach would be to render out normalized view-space z as our depth.  Since it&rsquo;s view-space it&rsquo;s linear which means we get uniform precision distribution, and this also means we don&rsquo;t need to bother with projection or unprojection to reconstruct position.  Instead we can take the approach of <a href="http://ati.amd.com/developer/siggraph06/Wenzel-Real-time_Atmospheric_Effects_in_Games.pdf" title="CryTek">CryTek</a> and multiply the depth value with a ray pointing from the camera to the far-clip plane.  In HLSL it goes something like this:</p>
<pre tabindex="0"><code>// Shaders for rendering linear depth

void DepthVS(   in float4 in_vPositionOS    : POSITION,
                out float4 out_vPositionCS  : POSITION,
                out float  out_fDepthVS     : TEXCOORD0    )
{    

    // Figure out the position of the vertex in
    // view space and clip space
    float4x4 matWorldView = mul(g_matWorld, g_matView);
    float4 vPositionVS = mul(in_vPositionOS, matWorldView);
    out_vPositionCS = mul(vPositionVS, g_matProj);
    out_fDepthVS = vPositionVS.z;
}

float4 DepthPS(in float in_fDepthVS : TEXCOORD0) : COLOR0
{
    // Negate and divide by distance to far-clip plane
    // (so that depth is in range [0,1])
    // This is for right-handed coordinate system,
    // for left-handed negating is not necessary.
    float fDepth = -in_fDepthVS/g_fFarClip;
    return float4(fDepth, 1.0f, 1.0f, 1.0f);
}

// Shaders for deferred pass where position is reconstructed
// Vertex shader for rendering a full-screen quad
void QuadVS (	in float3 in_vPositionOS		        : POSITION,
		        in float3 in_vTexCoordAndCornerIndex	: TEXCOORD0,
		        out float4 out_vPositionCS		        : POSITION,
		        out float2 out_vTexCoord		        : TEXCOORD0,
		        out float3 out_vFrustumCornerVS		    : TEXCOORD1	)
{
	// Offset the position by half a pixel to correctly
	// align texels to pixels. Only necessary for D3D9 or XNA
	out_vPositionCS.x = in_vPositionOS.x - (1.0f/g_vOcclusionTextureSize.x);
	out_vPositionCS.y = in_vPositionOS.y + (1.0f/g_vOcclusionTextureSize.y);
	out_vPositionCS.z = in_vPositionOS.z;
	out_vPositionCS.w = 1.0f;

	// Pass along the texture coordinate and the position
	// of the frustum corner in view-space.  This frustum corner
    // position is interpolated so that the pixel shader always
    // has a ray from camera-&gt;far-clip plane
	out_vTexCoord = in_vTexCoordAndCornerIndex.xy;
	out_vFrustumCornerVS = g_vFrustumCornersVS[in_vTexCoordAndCornerIndex.z];
}

// Pixel shader function for reconstructing view-space position
float3 VSPositionFromDepth(float2 vTexCoord, float3 vFrustumRayVS)
{
	float fPixelDepth = tex2D(DepthSampler, vTexCoord).r;
	return fPixelDepth * vFrustumRayVS;
}
</code></pre><p>As you can see the reconstruction is quite nice with linear depth, we only need a single multiply instead of the 4 MADD&rsquo;s and a divide needed for unprojection.  If you&rsquo;re curious on how to get the frustum corner position I use, it&rsquo;s rather easy with a little trig.  <a href="http://www.lighthouse3d.com/opengl/viewfrustum/index.php?defvf">This tutorial</a> walks you through it.  Or if you&rsquo;re using XNA, there&rsquo;s a super-convient BoundingFrustum class that can take care of it for you.  My code for getting the positions looks something like this:</p>
<pre tabindex="0"><code>Matrix viewProjMatrix = viewMatrix * projMatrix;
BoundingFrustum frustum = new BoundingFrustum(viewProjMatrix);
frustum.GetCorners(frustumCornersWS);
Vector3.Transform(frustumCornersWS, ref viewMatrix, frustumCornersVS);
for (int i = 0; i &lt; 4; i++)
    farFrustumCornersVS[i] = frustumCornersVS[i + 4];
</code></pre><p>The farFrustumCornersVS array is what I send to my vertex shader as shader constants. Then you just need to have an index in your quad vertices that tells you which vertex belongs to which corner (which you could also do with shader math, if you want).  Another approach would be to simply store the corner positions directly in the vertices as texCoord&rsquo;s.</p>
<p>Extra Credit:  this technique can also be used to to reconstruct world-space position, if that&rsquo;s what you&rsquo;re after.  All you need to do is <em>rotate</em> (not translate) your frustum corner positions by the inverse of your view matrix to get them back into world space.  Then when you multiply the interpolated ray with your depth value, you simply add the camera position to the value (ends up being a single MADD).</p>
<p>Extra-Extra Credit: you can use this technique with arbitrary geometry too, not just quads.  You just need to figure out a texture coordinate for each pixel, which you can do by either interpolating the clip-space position and dividing x and y by w, or by using the VPOS semantic.  Then for your frustum ray you just calculate the eye-&gt;vertex vector and scale it so that it points all the way back to the far-clip plane.</p>
<p>UPDATE:  Answers to extra credit questions <a href="http://mynameismjp.wordpress.com/2009/05/05/reconstructing-position-from-depth-continued/">here</a></p>
<p>UPDATE 2: More info <a href="http://mynameismjp.wordpress.com/2010/09/05/position-from-depth-3/">here</a></p>
<p><em>Closing the comments for now, because I keep getting spam comments</em></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22ome_daiber@hotmail.com%22">Phil</a> - <time datetime="2010-04-07 21:48:06">Apr 3, 2010</time></p>
<p>It looks like this technique of using the far bounding frustum coordinates will only work if you are using a perspective projection, is that right? I have a parallel projection, and multiplying the depth by the frustum ray just doesn&rsquo;t seem to make sense.</p>
<hr />
####
[ozlael](http://ozlael.egloos.com "") - <time datetime="2010-04-08 18:04:23">Apr 4, 2010</time>
<p>thank you for your post. can i translate into korean and put on my blog?</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2010-04-08 18:17:38">Apr 4, 2010</time>
<p>Phil: indeed the technique is meant for a perspective projection. For an ortho projection it&rsquo;s unnecessary since you can directly calculate view-space X and Y coordinates based on your clip-space XY and your projection parameters. You can do the same for view-space Z once you&rsquo;ve sampled it from your depth buffer. ozael: absolutely, that&rsquo;s not problem at all.</p>
<hr />
####
[Real-Time Rendering &middot; 7 Things for February 6](http://www.realtimerendering.com/blog/7-things-for-february-6/ "") - <time datetime="2010-02-06 10:21:51">Feb 6, 2010</time>
<p>[&hellip;] A month and a half ago I listed some articles about reconstructing the position or linear z-depth in a shader. Here’s another. [&hellip;]</p>
<hr />
####
[Dave]( "mizrael@yahoo.it") - <time datetime="2009-05-05 13:59:48">May 2, 2009</time>
<p>Thank you for your beautiful article, it&rsquo;s exactly what I needed! Just 2 things: 1) I need to reconstruct the world space position&hellip; can you explain a bit more the process? What do you mean for &ldquo;rotate the frustm corners by the matrix&rdquo; ? 2) For point lights I use a sphere mesh to approximate the light volume. How can I build the world position from that? I mean, using a full screen quad , I can easily compute the current frustum corner..but with a sphere? Again, thank you my friend!</p>
<hr />
####
[Reconstructing Position From Depth, Continued &laquo; MJP’s XNA Danger Zone](http://mynameismjp.wordpress.com/2009/05/05/reconstructing-position-from-depth-continued/ "") - <time datetime="2009-05-05 15:12:01">May 2, 2009</time>
<p>[&hellip;] Reconstructing Position From Depth, Continued May 5, 2009 — mpettineo Picking up where I left off here… [&hellip;]</p>
<hr />
####
[realtimecollisiondetection.net - the blog &raquo; Catching up (part 2)](http://realtimecollisiondetection.net/blog/?p=93 "") - <time datetime="2009-06-08 03:52:11">Jun 1, 2009</time>
<p>[&hellip;] Pettineo has several good posts on his blog. The one about Reconstructing Position From Depth should be useful to [&hellip;]</p>
<hr />
####
[Reconstructing view-space position from depth | J.Coluna](http://jcoluna.wordpress.com/2011/01/27/reconstructing-view-space-position-from-depth/ "") - <time datetime="2011-01-27 15:17:28">Jan 4, 2011</time>
<p>[&hellip;] was using screen-aligned quads before, it was easy to compute each pixel view-space position using this. However, as I’m using a mesh now, I had to figure out a way to recreate the position using [&hellip;]</p>
<hr />
####
[Light Pre Pass in XNA: Basic Implementation &laquo; Michael Quandt](http://mquandt.com/blog/2009/12/light-pre-pass-in-xna-basic-implementation/ "") - <time datetime="2009-12-01 08:52:17">Dec 2, 2009</time>
<p>[&hellip;] more information on how to reconstruct a position based on a depth value, read this. There are also alternative, and improved methods listed there, which can be used depending on your [&hellip;]</p>
<hr />
####
[Paul Johnson](http://www.rubicondev.com "paul@rubicondev.com") - <time datetime="2009-10-12 05:39:19">Oct 1, 2009</time>
<p>:) Thanks. My email&rsquo;s been off over the weekend.</p>
<hr />
####
[Paul Johnson](http://www.rubicondev.com "paul@rubicondev.com") - <time datetime="2009-10-11 10:24:04">Oct 0, 2009</time>
<p>Very interesting, thanks. All that&rsquo;s missing to make this perfect is an explanation of how to map a position in view space back into screen/texture space again. My brain melts each time I try to do this and it&rsquo;s stopping me from implementing a better SSAO algorithm than I have now.</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2009-10-11 11:53:53">Oct 0, 2009</time>
<p>Hi Paul. I replied in that thread on the XNA forums, but I&rsquo;ll reply here too. In my SSAO shader I just take the view-space position, transform it by the projection matrix, and then use this function to get a texture coordinate: // Gets the screen-space texel coord from clip-space position float2 CalcSSTexCoord (float4 vPositionCS) { float2 vSSTexCoord = vPositionCS.xy / vPositionCS.w; vSSTexCoord = vSSTexCoord * 0.5f + 0.5f; vSSTexCoord.y = 1.0f - vSSTexCoord.y; vSSTexCoord += 0.5f / g_vTexDimensions; return vSSTexCoord; } g_vTexDimensions would be the dimensions of the texture you&rsquo;re sampling. There might be a cheaper way of doing this, but projecting definitely works.</p>
<hr />
####
[Greg](http://www.caffeinatedgames.com "greg@caffeinatedgames.com") - <time datetime="2009-07-29 17:52:53">Jul 3, 2009</time>
<p>Thank you for posting this all together! I can delete my 6 book marks to different parts of it now :) Cheers, Greg</p>
<hr />
####
[Prompt](http://www.alvaromartin.net "the.prompt@gmail.com") - <time datetime="2009-07-20 12:05:20">Jul 1, 2009</time>
<p>I&rsquo;m trying to do that but I have some problems. I described here in the GDNet forum using your technique, maybe you can help me. <a href="http://www.gamedev.net/community/forums/topic.asp?topic_id=541689">http://www.gamedev.net/community/forums/topic.asp?topic_id=541689</a> Thanks for all !</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2010-03-24 09:55:07">Mar 3, 2010</time>
<p>Right, if you store perspective z/w in most cases you&rsquo;ll have inadequate precision in areas closer to the far clip plane. But it&rsquo;s really a distribution of precision issue more than anything, due to the non-linear curve of z/w. My latest blog (<a href="http://mynameismjp.wordpress.com/2010/03/22/attack-of-the-depth-buffer/">http://mynameismjp.wordpress.com/2010/03/22/attack-of-the-depth-buffer/</a>) demonstrates the sort of error you can expect for different depth formats&hellip;perhaps I&rsquo;ll link it here so that people are aware of this issue. Thanks for bringing it up. :)</p>
<hr />
####
[Light Pre-Pass Round 2 &laquo; MQuandt.Blog](http://mquandt.com/blog/2010/03/light-pre-pass-round-2/ "") - <time datetime="2010-03-11 04:21:04">Mar 4, 2010</time>
<p>[&hellip;] <a href="http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/">http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/</a> [&hellip;]</p>
<hr />
####
[Reconstructing Position from Depth &ndash; Frustum Ray Technique &laquo; MQuandt.Blog](http://mquandt.com/blog/2010/03/reconstructing-position-from-depth-frustum-ray-technique/ "") - <time datetime="2010-03-14 13:03:36">Mar 0, 2010</time>
<p>[&hellip;] by Crytek in a presentation about Atmospheric Scattering [Wenzel2006], and mentioned by MJP in his article [series] about reconstructing position from [&hellip;]</p>
<hr />
####
[Attack of the depth buffer &laquo; The Danger Zone](http://mynameismjp.wordpress.com/2010/03/22/attack-of-the-depth-buffer/ "") - <time datetime="2010-03-22 23:42:39">Mar 1, 2010</time>
<p>[&hellip;] we only use depth buffers for visibility and stenciling, as we now make use of the depth buffer to reconstruct world-space or view-space position of our geometry at any given pixel.  This can be a powerful performance optimization, since the [&hellip;]</p>
<hr />
####
[Orcun]( "madorcun@hotmail.com") - <time datetime="2010-06-23 05:16:19">Jun 3, 2010</time>
<p>Hi MPJ, I have spend nearly a week while reading your thread in gamedev and trying several ssao implamentations. I&rsquo;m trying to make a ssao protetype in render monkey before integrating it in to the engine that I working on. Currently my engine does not have developer friendly shader management facilities so I&rsquo;m doing my best to make the effect as good as posible bofore integration. I haver read and understood all of the methods that you mentioned hoverever the biggest piece is missing. I can not calculate and feed view frustum corners to vertex shader because of rendermonkey. I&rsquo;m trying to generate them by using screen space quad mesh (which is a simple 3d rectangle with dimensions) vertices and I can not get result. Can you write how to calculate view vectors by using only screen aligned quad in vertex shader. Here is my code which does not work; G-Buffer Vertex Shader: VS_OUTPUT vs_main(VS_INPUT Input) { VS_OUTPUT Output; Output.Position = mul(Input.Position + float4(ModelPosition.xyz, 0.0f), ViewProjectionMatrix); float3 ObjectPosition = mul(Input.Position, ViewMatrix); Output._Position = ObjectPosition; Output.NormalDepth.xyz = mul(Input.Normal, ViewMatrix); Output.NormalDepth.w = ObjectPosition.z; return Output; } G-Buffer Pixel Shader: PS_OUTPUT ps_main( PS_INPUT Input ) { PS_OUTPUT Output; Output.Position = float4(Input.Position, 1.0f); Output.NormalDepth.xyz = Input.NormalDepth.xyz; Output.NormalDepth.w = Input.NormalDepth.w / FarZ; return Output; } SSAO Vertex Shader; VS_OUTPUT vs_main(float4 Position : POSITION, float2 Texcoord : TEXCOORD0) { VS_OUTPUT Output; Output.Position = float4(Position.xy, 0.0f, 1.0f) + float4(-PixelSize.x, PixelSize.y, 0.0f, 0.0f); Output.ScreenTexcoord = Texcoord; Output.ViewVector.x = Position.x * tan(FOV / 2) * (ScreenSize.x / ScreenSize.y); Output.ViewVector.y = Position.y * tan(FOV / 2); Output.ViewVector.z = 1; return Output; } SSAO PixelShader: float3 GetViewPosition(in float2 Texcoord, in float3 ViewVector) { return ViewVector * tex2D(NormalDepthInput, Texcoord).w; } Thanks a lot.</p>
<hr />
####
[Nik]( "nsmoly@yahoo.com") - <time datetime="2010-03-24 09:49:47">Mar 3, 2010</time>
<p>Sorry for doubting your article, but I think that the projective transform is not inversible in general - you loose information when you project. Having depth buffer values helps to restore some of the information, but since depth map has a certain resolution then this approach will not work if the camera is far away from the projected object. Most of the object may have very similar depth value in the depth map and so it will look flat after &ldquo;unprojecting&rdquo;. I think you should point this out, since it is misleading to say that projective transforms can be undone in general&hellip;</p>
<hr />
####
[mystran]( "mystran@gmail.com") - <time datetime="2010-05-10 04:52:59">May 1, 2010</time>
<p>The way I wrote this yesterday, instead of using constants for the frustum corners, I just did mul(float4(x,y,1,1), mInvProj) per vertex to get far-plane positions in view-space. Then in pixel shader multiply with stored (floating point) view-z/farPlane. At least visually it looks like it should and saves you from having to work out the frustum coordinates on CPU, so you could draw large number of screen-aligned triangles together, yet still do the unprojection work only once-per-vertex.</p>
<hr />
####
[Camus]( "camus_mm@hotmail.com") - <time datetime="2011-01-14 02:57:32">Jan 5, 2011</time>
<p>It works: <a href="http://img3.imageshack.us/img3/6453/versusuu.png">http://img3.imageshack.us/img3/6453/versusuu.png</a> Thanks for open my eyes, behold the linearity.</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-01-14 22:50:01">Jan 5, 2011</time>
<p>Messing with your z value like that can (and will) screw up rasterization, early z-cull, and z compression (which is why people don&rsquo;t do it). One workable alternative is to use a floating point depth buffer, and flip the near and far planes of your projection (and also flip your depth test directions). When you do that, the non-linear distribution of precision in a floating point value <em>mostly</em> cancels out the non-linearity of z/w. That helps with precision, but not with the issue of applying a non-uniform bias.</p>
<hr />
####
[Camus]( "camus_mm@hotmail.com") - <time datetime="2011-01-30 22:08:26">Jan 0, 2011</time>
<p>Hi mpettineo, again, I just want to point out about the frustum corners method, it is valid only for the current pixel, can&rsquo;t be used directly to read from neighbor samples, because of the interpolation, it can be modified doing a manual lerp: lerp(-corner.x,corner.x,uv.x); lerp(-corner.y,corner.y,uv.y); I was into it the whole week trying to figure out that. Because many people are getting confused with this. Also, as you said, Z/FarPlane, can be used only if it&rsquo;s done in the pixel program, interpolated by the vertex program falls into a nonlinear function with werid results, I don&rsquo;t know why this page is still alive: <a href="http://www.mvps.org/directx/articles/linear_z/linearz.htm">http://www.mvps.org/directx/articles/linear_z/linearz.htm</a> IT IS WRONG!, thanks for your time anyway, has been very helpful :)</p>
<hr />
####
[Camus]( "camus_mm@hotmail.com") - <time datetime="2011-01-13 22:24:59">Jan 4, 2011</time>
<p>Hi, the second method of render z on view space as depth, can be used to store shadow depth? Because I think it will solve in part the bias problem, since it is lineal the distribution, a constant bias will be enough to fix it, seems too good to be true, I&rsquo;m missing something?</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2011-01-13 23:20:20">Jan 4, 2011</time>
<p>If you&rsquo;re manually writing out shadow depth to a render target, then you can use whatever depth metric you&rsquo;d like. Linear Z/FarClip definitely works for that purpose. It&rsquo;s only an issue if you only want to render to a depth buffer, in which case you don&rsquo;t have a choice but to use z/w.</p>
<hr />
####
[Camus]( "camus_mm@hotmail.com") - <time datetime="2011-01-14 21:23:25">Jan 5, 2011</time>
<p>Me again, I figured out how to render to a depth buffer without using z/w. The pixel fragment should write zero as always, the vertex fragment change from this: Out.position = mul(In.position, WorldViewProj); to this: float4 vpos = mul(In.position, WorldViewProj); vpos.z = (vpos.z*vpos.w)/FarPlane; Out.position = vpos; It allows me to perform the depth comparison by hardware, I still don&rsquo;t understand why everybody, even the SDK shadow map sample uses z/w, there is even papers written with the purpose of fixing the z-fighting in shadow map.</p>
<hr />
####
[Reconstructing Position From Depth &laquo; TepNetwork](http://www.3dgametechnology.com/wp/reconstructing-position-from-depth/ "") - <time datetime="2011-02-22 21:32:45">Feb 2, 2011</time>
<p>[&hellip;] Matt posted an excellent article about reconstructing position from depth. Check out his article here: <a href="http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/">http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/</a> [&hellip;]</p>
<hr />
####
[Kreation&#8217;s Edge &raquo; Deferred Rendering Notes](http://www.kreationsedge.com/?p=710 "") - <time datetime="2010-10-02 16:01:49">Oct 6, 2010</time>
<p>[&hellip;] by Blizzard / Crytek which makes use of view space rays. MJP wrote an excellent blog on this method here. You can also try accessing the hardware depth buffer directly as [&hellip;]</p>
<hr />
####
[XNA 4.0 Light Pre-Pass | J.Coluna](http://jcoluna.wordpress.com/2011/01/18/xna-4-0-light-pre-pass/ "") - <time datetime="2011-01-19 10:34:24">Jan 3, 2011</time>
<p>[&hellip;] the frustum corners: I’m using the technique that uses the frustum corners and the depth buffer to recompute each pixel’s position in view space, so I compute it here [&hellip;]</p>
<hr />
####
[Richard Osborne](http://www.cyberkreations.com/kreationsedge "rosborne255@yahoo.com") - <time datetime="2010-08-14 19:53:58">Aug 6, 2010</time>
<p>@Orcun: checkout this thread, <a href="http://www.gamedev.net/community/forums/topic.asp?topic_id=506573">http://www.gamedev.net/community/forums/topic.asp?topic_id=506573</a> The method from Shader x5 book uses a technique that doesn&rsquo;t require the frustum corners and the results are pretty nice. @author, Thanks for taking the time to blog on this subject and providing the XNA example!</p>
<hr />
####
[Position From Depth 3: Back In The Habit &laquo; The Danger Zone](http://mynameismjp.wordpress.com/2010/09/05/position-from-depth-3/ "") - <time datetime="2010-09-05 23:11:57">Sep 0, 2010</time>
<p>[&hellip;] a lot of people still have trouble with reconstructing position from depth thing, judging by the emails I get and also the threads I see in the gamedev forums made by people [&hellip;]</p>
<hr />
####
[Camus]( "camus_mm@hotmail.com") - <time datetime="2011-01-13 23:57:41">Jan 4, 2011</time>
<p>Thank you so much, I will run some tests :)</p>
<hr />
####
[Screen Space Ambient Occlusion | m_panknin;](http://blog.mpanknin.de/?p=829 "") - <time datetime="2012-08-29 02:03:19">Aug 3, 2012</time>
<p>[&hellip;] article for details. Position reconstruction is done via the frustum corner method as described here. The amount of occlusion contribution of taken samples is based on this tutorial. However I [&hellip;]</p>
<hr />
####
[Obtaining attention area stage from the level consistency | CodersDiscuss.com](http://codersdiscuss.com/16310/obtaining-attention-area-stage-from-the-level-consistency/ "") - <time datetime="2014-06-03 02:53:23">Jun 2, 2014</time>
<p>[…] find out about two techniques in this article and by my […]</p>
<hr />
####
[DEFERRED RENDERING IN MODERN OPENGL – PART 2 &#8211; The Pixel Vault](https://thepixelvault.wordpress.com/2016/03/05/deferred-rendering-in-modern-opengl-part-2/ "") - <time datetime="2016-03-05 09:13:28">Mar 6, 2016</time>
<p>[…] This implementation is by no means perfect, or even good. Remember i am still a beginner at this and this post was only meant to give you an understanding of how you could go about implementing your own Deferred Renderer. Anyways as for improvements, one thing i am considering is to reconstruct World Space Positions using the Depth Buffer, because this will allow us to remove the Position Buffer from the G-Buffer thus making it smaller. Matt Pettineo from Ready-At-Dawn has some great tutorials about it on his blog. […]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Scintillating Snippets: Programatically Adding Content To A Content Project</title>
			<link>https://therealmjp.github.io/posts/snippet-content-project/</link>
			<pubDate>Thu, 19 Feb 2009 21:36:05 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/snippet-content-project/</guid>
			<description>One of the tools I made for my current project is a model editor. Basically it can import in .fbx or .x models, and then you can apply my custom effects, set parameters, set textures, and then save it using my custom model format I named &amp;ldquo;.jsm&amp;rdquo; (it&amp;rsquo;s just XML&amp;hellip;don&amp;rsquo;t tell anyone!). Anyway one of the neat features I wanted it to have was the ability to add a model to my game&amp;rsquo;s Content project so that you wouldn&amp;rsquo;t have to manually do it through Visual Studio.</description>
			<content:encoded><![CDATA[<p>One of the tools I made for my current project is a model editor.  Basically it can import in .fbx or .x models, and then you can apply my custom effects, set parameters, set textures, and then save it using my custom model format I named &ldquo;.jsm&rdquo; (it&rsquo;s just XML&hellip;don&rsquo;t tell anyone!).  Anyway one of the neat features I wanted it to have was the ability to add a model to my game&rsquo;s Content project so that you wouldn&rsquo;t have to manually do it through Visual Studio.  And since the Content Pipeline uses MSBuild, this is easy to do:</p>
<pre tabindex="0"><code>// Load up the content project
Engine.GlobalEngine.BinPath = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
Project contentProject = new Project();
contentProject.Load(projectFileName);

// Add it
BuildItem newItem = contentProject.AddNewItem(&#34;Compile&#34;, &#34;Models\\\\&#34; + modelName + &#34;.fbx&#34;);
newItem.SetMetadata(&#34;Link&#34;, &#34;Models\\\\&#34; + modelName + &#34;.fbx&#34;);
newItem.SetMetadata(&#34;Name&#34;, modelName);
newItem.SetMetadata(&#34;Importer&#34;, &#34;FbxImporter&#34;);
newItem.SetMetadata(&#34;Processor&#34;, &#34;ModelProcessor&#34;);

// Save it
contentProject.Save(projectFileName);
</code></pre><p>This is of course the generic version and not the actual code I used, but you get the idea.  The &ldquo;projectFileName&rdquo; string should contain a path to your Content.contentproj file in your Content subfolder.  &ldquo;modelName&rdquo; would just be a name for your model, minus the extension.    What&rsquo;s going on is pretty simple:  I load up the Content project using the Engine and Project classes found in Microsoft.Build.BuildEngine.  Then I create a new BuildItem for the model, which I add to the Project.   When I create the BuildItem, the string I send to the constructor contains the path to the model file <em>relative to the .contentproj file</em>.  The first bit of metadata specifies that I want to add the file as a link, not as a copy.  The string specifies how the file shows up in the project hierarchy (AKA, how it will show up when you expand the Content node in Visual Studio).  The second bit of metadata is just a name associated with the file.  Then the third specifies the ContentImporter to use, and the fourth specifies the ContentProcessor to use.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22simonb26@yahoo.co.uk%22">Simon</a> - <time datetime="2009-07-09 11:11:46">Jul 4, 2009</time></p>
<p>Hi, Thanks for the cool snippet! So how would you go about deleting a model from the Content project. Is there a way to do it using the &ldquo;Name&rdquo; or the &ldquo;Link&rdquo; of the model?</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2009-07-09 11:28:33">Jul 4, 2009</time>
<p>Hey Simon. I haven&rsquo;t tried it myself, but I believe you can just use Project.RemoveItem or Project.RemoveItemsByName and then call Save to save the project.</p>
<hr />
####
[Simon]( "simonb26@yahoo.co.uk") - <time datetime="2009-07-09 12:05:32">Jul 4, 2009</time>
<p>Hi, I can&rsquo;t use Project.RemoveItemsByName because all of the items are called &ldquo;Compile&rdquo;. And if I were to use Project.RemoveItem I would have to loop through all of the projects BuildItem&rsquo;s find which one to remove. Have you got any more suggestions?</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Deferred Cascaded Shadow Maps</title>
			<link>https://therealmjp.github.io/posts/deferred-cascaded-shadow-maps/</link>
			<pubDate>Wed, 18 Feb 2009 04:22:32 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/deferred-cascaded-shadow-maps/</guid>
			<description>For my next sample I was planning on extending my deferred shadow maps sample to implement cascaded shadow maps. I got an email asking about how to make the sample look decent with large viewing distances which is exactly the problem CSM&amp;rsquo;s solve. So I decided to bump up my plans a little early and get the code up and running. It&amp;rsquo;ll be a while before I get the write-up finished, but until then feel free to play around with code (PC and 360 projects included).</description>
			<content:encoded><![CDATA[<p>For my next sample I was planning on extending my <a href="http://www.ziggyware.com/readarticle.php?article_id=235">deferred shadow maps sample</a> to implement cascaded shadow maps.  I got an email asking about how to make the sample look decent with large viewing distances which is exactly the problem CSM&rsquo;s solve.  So I decided to bump up my plans a little early and get the code up and running.  It&rsquo;ll be a while before I get the write-up finished, but until then feel free to play around with <a href="http://cid-538e432ea49f5bde.skydrive.live.com/self.aspx/Public/Samples%20And%20Tutorials/DeferredCascadedShadowMaps.zip">code</a> (PC and 360 projects included).</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22scorchmon@yahoo.com%22">Justin Krenz</a> - <time datetime="2010-08-21 00:05:52">Aug 6, 2010</time></p>
<p>I&rsquo;ve updated the code to support stable cascaded shadow maps (so that the shadows don&rsquo;t shimmer when the camera moves or rotates). I used the code from the DirectX SDK&rsquo;s cascaded shadow map sample. You can view the changes here (updates the CalculateFrustum function on line 223 in ShadowRenderer.cs): <a href="http://pastebin.com/Yn5SVPUP">http://pastebin.com/Yn5SVPUP</a></p>
<hr />
####
[Garold](http://cloneofduty.com "gruddo@gmail.com") - <time datetime="2012-07-22 11:35:27">Jul 0, 2012</time>
<p>Thanks,just what I needed! Presentations, GDC Powerpoints and suchlike only help so far.:)</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Profiling Events vs. Virtual Functions On The 360</title>
			<link>https://therealmjp.github.io/posts/profiling-events/</link>
			<pubDate>Fri, 23 Jan 2009 17:31:19 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/profiling-events/</guid>
			<description>Over the past week or so I&amp;rsquo;ve been completely reworking my collision system in order to better decouple it from other areas of code, and also make it more flexible. One part I got stuck on for a bit was deciding on the mechanism to use for notifying owners of collision components when the component collides with something. I narrowed it down to two options:
notify owners via the ICollisionOwner interface I was using OR</description>
			<content:encoded><![CDATA[<p>Over the past week or so I&rsquo;ve been completely reworking my collision system in order to better decouple it from other areas of code, and also make it more flexible.  One part I got stuck on for a bit was deciding on the mechanism to use for notifying owners of collision components when the component collides with something.  I narrowed it down to two options:</p>
<ul>
<li>notify owners via the ICollisionOwner interface I was using</li>
</ul>
<p>OR</p>
<ul>
<li>use an Event</li>
</ul>
<p>I was leaning more towards events because I felt their semantics naturally fit with the usage pattern I was working.  If game entities want to be notified, they simply subscribe and they get notified.  This seemed cleaner and easier to understand than letting each collision component have some sort of  &ldquo;NotifyOwner&rdquo; flag, and then call a virtual function if the flag was true.  However I was a little worried about performance&hellip;I hadn&rsquo;t really used delegates on the 360 before and I wanted to make sure that the overhead wasn&rsquo;t going to be something astronomical before proceeding. So I set up a simple test harness that vaguely resembled how I was going to use events:</p>
<pre tabindex="0"><code>public delegate void EventDelegate(object sender, ref Vector3 parameter);

public class EventServer
{
    public event EventDelegate SomeEvent;

    public void RaiseEvent()
    {
        Vector3 param = new Vector3();

        if (SomeEvent != null)
            SomeEvent(this, ref param);

        //for (int i = 0; i &lt; Handlers.Count; i++)
        //{
        //    if (Handlers[i].HandlesEvent)
        //        Handlers[i].HandleEventVirtual(this, ref param);
        //}
    }

    public List&lt;IEventHandler&gt; Handlers = new List&lt;IEventHandler&gt;();
}


public interface IEventHandler
{
    void HandleEventVirtual(object sender, ref Vector3 parameter);

    bool HandlesEvent
    {
        get;
    }
}

public class EventHandler : IEventHandler
{
    EventServer server;
    bool handleEvent;

    public EventHandler(EventServer server, bool handleEvent)
    {
        this.server = server;
        this.handleEvent = handleEvent;  

        if (handleEvent)
            server.SomeEvent += new EventDelegate(HandleEvent);
    }

    void HandleEvent(object sender, ref Vector3 parameter)
    {
        parameter.Y += 0.001f;
    }

    public virtual void HandleEventVirtual(object sender, ref Vector3 parameter)
    {
        parameter.X += 0.001f;
    }

    public bool HandlesEvent
    {
        get { return handleEvent; }
    }
}

public class EventHandler2 : EventHandler
{
    public EventHandler2(EventServer server, bool handleEvent)
        : base(server, handleEvent)
    {

    }

    public override void HandleEventVirtual(object sender, ref Vector3 parameter)
    {
        base.HandleEventVirtual(sender, ref parameter);
        parameter.Normalize();
    }
}
</code></pre><p>This is a pretty simple set up: a class that will dole out events to a collection of handlers, with a derivative of the handler class also being thrown in just to make sure the compiler doesn&rsquo;t do anything funky that will prevent us from actually getting virtual functions.  To test events we leave it like this, to test virtual functions we comment out the event invocation and use the virtual function call instead.  Any .NET junkies might notice I&rsquo;ve violated the guidelines for creating custom event handlers by not using a an EventArgs derivate&hellip;the reason why is because EventArgs in a class, so creating a new instance would generate garbage everytime the event fires.  And as we all know..the GC is <em>not</em> our friend on the Xbox.</p>
<p>I set it up to run with various amounts of event handlers distributed across various amounts of event servers.  I then set up the game class to fire off all the event servers in the Update function and use a Stopwatch to time how long it took.  I also averaged the timing results across 64 frames to smooth out the results.  This is what I got:</p>
<pre tabindex="0"><code>50:1         9
             22

500:1        710
             220

5000:1       163000 (3.26ms)
             2200

5000:10      18600
             2200

5000:100     1000
             2200

5000:1000    820
             2200
</code></pre><p>The table shows the EventHandler:EventServer ratio, and on the right is the of time taken for invocation (in ticks).  The number on top is from using Events, the bottom from using virtual functions.  The first few results are pretty interesting:  the virtual function method scales linearly with the amount of handlers we have, while the the time required for firing events goes up exponentially.   The bottom half of the results are even more interesting: the time taken goes way down as we start to distribute the handlers more evenly across servers.  In fact it goes down so much, it becomes quicker than virtual functions!.  Crazy.</p>
<p>Anyway I had my answer: events would be fine with my setup.  I can&rsquo;t foresee any reason why more than one handler would subscribe to the same collision component, and even if it did the overhead is basically miniscule for the numbers I&rsquo;ll be working with.  But it&rsquo;s always fun to experiment, right?</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://www.sgtconker.com/2009/11/scrolls-from-the-past-profiling/" title="">Scrolls from the past: Profiling « Sgt. Conker</a> - <time datetime="2009-11-04 04:36:49">Nov 3, 2009</time></p>
<p>[&hellip;] Over a year later, bittermanandy was writing a chronicle about what recipes and rituals he found most useful in his delvings into XNA, and in one of these chronicles, he touched on the subject of the CLR Profiler again. That same year, jwatte gave away a free potion that could be used for performing the profiling ritual in the kingdom of the Xbox 360, which was not possible using NProf. MJP made a comparison between events and virtual functions, using the Stopwatch spell, and left his writing for all to read in one of his scrolls. [&hellip;]</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Deferred Shadow Maps Sample</title>
			<link>https://therealmjp.github.io/posts/deferred-shadow-maps-sample/</link>
			<pubDate>Tue, 20 Jan 2009 01:24:19 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/deferred-shadow-maps-sample/</guid>
			<description>Got a new sample ready, this one shows how you can defer shadow map calculations to a separate screen-space pass using a depth buffer. Check it out on Ziggyware!
Comments: sam - Feb 4, 2009
This sample does not works for me. I see the blank screen. My Video card is GF 9800 GT.
#### [Alejandro Martinez](http://www.gaspgames.com/www.battletennis.com &#34;amartinez1660@gmail.com&#34;) - Feb 2, 2010 1./2. Points taken! 3. That&amp;rsquo;s quite a boost for the shadow map render and sampling (HW PCF or Ati&amp;rsquo;s Fetch4).</description>
			<content:encoded><![CDATA[<p>Got a new sample ready, this one  shows how you can defer shadow map calculations to a separate screen-space pass using a depth buffer.  Check it out on <a href="http://www.ziggyware.com/readarticle.php?article_id=235">Ziggyware</a>!</p>
<h2 id="deferredshadowmapsimagesconverteddeferred-shadow-maps-sampledeferredshadowmapspng-deferredshadowmaps"><img src="/images/converted/deferred-shadow-maps-sample/deferredshadowmaps.png" alt="deferredshadowmaps" title="deferredshadowmaps"></h2>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://www.coding4xna.com" title="come4sam@hotmail.com">sam</a> - <time datetime="2009-02-19 02:53:25">Feb 4, 2009</time></p>
<p>This sample does not works for me. I see the blank screen. My Video card is GF 9800 GT.</p>
<hr />
#### 
[Alejandro Martinez](http://www.gaspgames.com/www.battletennis.com "amartinez1660@gmail.com") - <time datetime="2010-02-02 17:24:58">Feb 2, 2010</time>
<p>1./2. Points taken! 3. That&rsquo;s quite a boost for the shadow map render and sampling (HW PCF or Ati&rsquo;s Fetch4). Guess I&rsquo;ll likely stick to linear shadow maps. By the way found this <a href="http://www.mvps.org/directx/articles/linear">http://www.mvps.org/directx/articles/linear</a>_z/linearz.htm, they premultiply in the vertex shader vPos.Z *= vPos.W / farClip. So when the hardware converts to homogenous space (vPos / W) the Z component stays linearized (Z * W) / W. You might find it interesting, however, as you say not too much that can be done with a hardware Depth Buffer in XNA (regarding shadows). Maybe save texCoords and interpolants in pixelShader 3.0 directly using vPos semantic for the shadowMap generation? 4. Maybe I could try it someday, just for the fun of it and understanding the manipulation of heightfields and the like. Thanks again Matt.</p>
<hr />
#### 
[Alejandro Martinez](http://www.gaspgames.com/www.battletennis.com "amartinez1660@gmail.com") - <time datetime="2010-02-02 17:28:06">Feb 2, 2010</time>
<p>Forgot something that I found interesting also. The farClip division can be done in the application, right into the camera&rsquo;s viewProjection Matrix. So it only adds one multiply operation.</p>
<hr />
#### 
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2010-02-02 15:41:30">Feb 2, 2010</time>
<p>Hi Alejandro. -The reason that a deferred shadow pass *can* be faster is because you&rsquo;ll be efficient with your quad usage. When you rasterize thousands of small triangles, you&rsquo;ll end up with a lot of quads of pixel shaders where &lt;4 are running, but if you render two triangles that cover the entire screen you&rsquo;ll be using all 4 in most cases. Taking the shadow calculations out of your main pixel shader for opaques can also lower your GPR usage, which will increase the number of threads you can have in flight. But like anything related to performance it depends on the hardware and what else is going on in your renderer. I&rsquo;d suggest doing some profiling if you want solid answers. -I don&rsquo;t think they do 4 shadows at once. It wouldn&rsquo;t make sense, because for any local spot/point lights you wouldn&rsquo;t want to do the shadow calculations for the entire screen. You would instead want to use a scissor rectangle or a bounding volume, much like you would with deferred rendering. Plus if you do it this way it lets you reuse your shadow map memory, since you only need one shadow map in memory at a time. -Non-linear depth typically isn&rsquo;t ideal, since it has a non-uniform distribution of precision. You end up with much of your precision dedicated to the area close to the near clip plane. So if you can help it, it&rsquo;s best to use a linear depth metric. Many people will use non-linear depth because they&rsquo;ll use hardware Z buffers for rendering their shadow maps, since this is generally quicker (most hardware can write Z-only at double speed) and also lets you use the vendor-specific hardware PCF or Fetch4 extensions. But of course in XNA you can&rsquo;t use these things so you&rsquo;re stuck outputting to a floating-point texure, in which case you might as well store linear depth. -I&rsquo;d imagine the FPS flashlight is really the only case that would work well for screen-space shadows, but shadow maps are already quite good for this case since you don&rsquo;t need to render many objects to the shadow map (narrow view frustum) and because you can usually get away a low-res shadow map. You&rsquo;d also probably need a decent number of samples to avoid artifacts, in which case you&rsquo;ll start to negate any performance benefits. But of course you don&rsquo;t know for sure until you try it. :P</p>
<hr />
#### 
[Alejandro Martinez](http://www.gaspgames.com/www.battletennis.com "amartinez1660@gmail.com") - <time datetime="2010-02-02 05:17:24">Feb 2, 2010</time>
<p>Hi there! Quite advanced tutorials and samples you have here. Learning quite a lot with these, still I don&rsquo;t understand some parts of it. (Feel free to edit/trim/publish, it&rsquo;s a bit long). I have several questions regarding this deferred shadow map sample: - I understand the benefit gain (program-wise) by detaching the shadows from the geometry render. Even turning it off in your example is a simple switch to a 1x1 white-cleared renderTarget!. However I don&rsquo;t see a clear performance gain, in the pixel shader each pixel has to be transformed to light space to calculate the texcoord that will be used to sample from the shadowMap. Is it really faster than doing it in the vertexShader at the geometry pass? - For 2, 3, and 4 shadows it would be needed 2/3/4 lightSpace transforms per pixel, am I right? (I still quite don&rsquo;t get how the Crytek team render four shadows at the same time, if that&rsquo;s what it is they do). - Is there a way to actually bypass that lightspace transformation like the viewSpace reconstruction from depth using a linear Depth? - All shadow maps examples I have seen and the one I have implemented use a non-linear depth buffer, is there an specific benefit/rule to that? - (Crazy idea, just though): Screen Space this and Screen Space that&hellip; Could it be possible to do &ldquo;ScreenSpace Shadows?&rdquo;. With the limited geometry information stored in the depth I believe that it might be possible to create a shadowMap on the fly with the points recovered from the camera depthMap. It may work with not too perpendicular lights? Like a FPS flashlight or a top down game with a sun fairly high sunlight. From your knowledge in the matter, is this possible? Thanks. Alejandro.</p>
<hr />
#### 
[moki](http://vectorslaved0tcom "moki@mailinator.com") - <time datetime="2011-06-21 02:31:11">Jun 2, 2011</time>
<p>Sorry to revive an old thread&hellip; Just read elsewhere on the net, Crysis does 4 shadows by putting one lights shadow into each of the color channels of a render target, and doing the shadows deferred in screenspace. I want to get this working in webgl.. researching it now.</p>
<hr />
#### 
[Ian](http://xomo.info/gaymaletube/ "") - <time datetime="2014-05-01 13:52:04">May 4, 2014</time>
<p>Where&rsquo;s the nearest cash machine? <a href="http://xomo.info/gaydar/">gaydar</a> I want to get fucked right now</p>
<hr />
#### 
[Joseph](http://buycytotec.net/ "") - <time datetime="2014-09-28 13:05:41">Sep 0, 2014</time>
<p>I&rsquo;ve got a very weak signal <a href="http://buycytotec.net/">http://buycytotec.net/</a> cytotec without prescription a. Policy Information Handout</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Teach Your Effects A New Trick</title>
			<link>https://therealmjp.github.io/posts/teach-your-effects-a-new-trick/</link>
			<pubDate>Mon, 19 Jan 2009 19:51:51 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/teach-your-effects-a-new-trick/</guid>
			<description>The Effects Framework is a pretty damn awesome tool. However I&amp;rsquo;m afraid that&amp;rsquo;s not totally obvious to a lot of newbies, who either just don&amp;rsquo;t what it can do or haven&amp;rsquo;t been exposed to some of the situations where Effect&amp;rsquo;s can really come in handy.
One neat thing Effect&amp;rsquo;s can do that isn&amp;rsquo;t obvious from the documentation or samples is auto-generate variants of shaders for you based on the value of uniform parameters.</description>
			<content:encoded><![CDATA[<p>The Effects Framework is a pretty damn awesome tool.  However I&rsquo;m afraid that&rsquo;s not totally obvious to a lot of newbies, who either just don&rsquo;t what it can do or haven&rsquo;t been exposed to some of the situations where Effect&rsquo;s can really come in handy.</p>
<p>One neat thing Effect&rsquo;s can do that isn&rsquo;t obvious from the documentation or samples is auto-generate variants of shaders for you based on the value of uniform parameters.  For instance let&rsquo;s take a common scenario: lets say you have a shader for model, and you need it to work for either a point light, a spot light, or a directional light one-at-a-time.  You might write your shader code like this:</p>
<pre tabindex="0"><code>int g_iLightType;

float4 ModelPixelShader(in PSInput input) : COLOR0
{
    float4 vColor;
    if (g_iLightType == LIGHT_TYPE_POINT)
        vColor = DoPointLighting(input);
    else if (g_iLightType == LIGHT_TYPE_SPOT)
        vColor = DoSpotLighting(input);
    else
        vColor = DoDirectionalLighting(input);

    return vColor;
}
</code></pre><p>Alright, so this works.  The app sets the  g_iLightType shader parameter, and the right calculations get used. However is it optimal?  We&rsquo;ve got these if statements in there, and maybe we&rsquo;re not sure what they&rsquo;ll get compiled into depending on the shader profile we&rsquo;re targetting.  And maybe we&rsquo;re not sure what the heck the driver is going to do once it gets the compiled shader.  Wouldn&rsquo;t it be nice if we could avoid that?  Of course it would.  So let&rsquo;s make some small changes:</p>
<pre tabindex="0"><code>float4 ModelPixelShader(in PSInput input, uniform int iLightType) : COLOR0

{   
    float4 vColor;
    if (iLightType == LIGHT_TYPE_POINT)
        vColor = DoPointLighting(input);
    else if (iLightType == LIGHT_TYPE_SPOT)
        vColor = DoSpotLighting(input);
    else
        vColor = DoDirectionalLighting(input);
}


technique PointLight
{
    pass p0
    {
        VertexShader = compile vs_2_0 ModelVertexShader();
        PixelShader = compile ps_2_0 ModelPixelShader(LIGHT_TYPE_POINT);       
    }
}

technique SpotLight
{
    pass p0
    {
        VertexShader = compile vs_2_0 ModelVertexShader();
        PixelShader = compile ps_2_0 ModelPixelShader(LIGHT_TYPE_SPOT);       
    }
}

technique DirectionalLight
{
    pass p0
    {
        VertexShader = compile vs_2_0 ModelVertexShader();
        PixelShader = compile ps_2_0 ModelPixelShader(LIGHT_TYPE_DIRECTIONAL);       
    }
}
</code></pre><p>Very similar, but one big difference: the HLSL code branches on a uniform int parameter to the pixel shader function, whose value is set in our technique declaration.  This means that the Effect knows that this parameter has a constant value for that entire technique, which allows it to generate a seperate shader for each technique where the parameter is a constant and not a variable.  Since it&rsquo;s a constant for each shader variant, no branching of any sort is necessary.  Now our app just picks the technique it wants for each light source it&rsquo;s handling, rather than setting a shader parameter.</p>
<p>Now keep in mind that using separate shaders like this will have performance implications:  switching vertex or pixel shaders has an associated overhead, and if you auto-generate different variants like we did above you&rsquo;ll be switching shaders more than if you used one big shader.  Whether or not it&rsquo;s a performance win will depend on what you&rsquo;re doing.  However, it&rsquo;s <em>always</em> good to be aware of all the neat tricks your tools can pull off.</p>
]]></content:encoded>
		</item>
		
		<item>
			<title>Fun With Compiled Content</title>
			<link>https://therealmjp.github.io/posts/fun-with-compiled-content/</link>
			<pubDate>Sun, 18 Jan 2009 21:36:14 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/fun-with-compiled-content/</guid>
			<description>EDIT: I realized it was probably a much smarter idea to just zip up the code along with the designer code and upload it somewhere. So here it is.
Wouldn&amp;rsquo;t it be neat to be able to have a dialog you could pop up that would show all the pre-compiled content of a certain Type, with it all listed in a nice tree showing the directory structure? Of course it would!</description>
			<content:encoded><![CDATA[<p>EDIT:  I realized it was probably a much smarter idea to just zip up the code along with the designer code and upload it somewhere.  So <a href="http://members.gamedev.net/MJP/ContentBrowser.zip">here it is</a>.</p>
<p>Wouldn&rsquo;t it be neat to be able to have a dialog you could pop up that would show all the pre-compiled content of a certain Type, with it all listed in a nice tree showing the directory structure?  Of course it would!  Only a crazy person would think otherwise.  Well the good news is I already did this, so feel free to plunder the code for your own use.</p>
<pre tabindex="0"><code>public partial class ContentBrowser : Form

{
    private static Dictionary&lt;string, TreeNode&gt; contentTrees = new Dictionary&lt;string, TreeNode&gt;();
    private static string contentDirectory;
    private static ContentManager contentManager;

    private static string[] contentTypes = {    &#34;Texture&#34;,
                                                &#34;Texture2D&#34;,
                                                &#34;Texture3D&#34;,
                                                &#34;TextureCube&#34;,
                                                &#34;SpriteFont&#34;,
                                                &#34;Model&#34;,
                                                &#34;Effect&#34;    };

    /// &lt;summary&gt;
    /// Traverses the specified content directory for all loadable content, and stores
    /// it as static data for use when an instance of the Form is created.
    /// &lt;/summary&gt;
    /// &lt;param name=&#34;services&#34;&gt;IServiceProvider implementation, contains IGraphicsDeviceService&lt;/param&gt;
    /// &lt;param name=&#34;contentDirectory&#34;&gt;The content directory to traverse&lt;/param&gt;
    /// &lt;param name=&#34;ownerWindow&#34;&gt;Owner window for the status dialog&lt;/param&gt;
    public static void Initialize(ServiceContainer services, string contentDirectory, IWin32Window ownerWindow)
    {
        ContentBrowser.contentDirectory = contentDirectory;

        // Make a content manager
        contentManager = new ContentManager(services, contentDirectory);

        // Make a small progress dialog so the user knows something is going on
        Form notificationDialog = new Form();
        notificationDialog.FormBorderStyle = FormBorderStyle.FixedDialog;
        notificationDialog.Size = new Size(350, 150);
        notificationDialog.Text = &#34;JSMapEditor&#34;;
        notificationDialog.StartPosition = FormStartPosition.CenterScreen;
        notificationDialog.ShowInTaskbar = false;
        notificationDialog.ShowIcon = false;
        notificationDialog.ControlBox = false;

        Label statusLabel = new Label();
        statusLabel.Size = new Size(200, 50);
        statusLabel.Location = new System.Drawing.Point(100, 50);
        statusLabel.Text = &#34;Loading Content&#34;;
        notificationDialog.Controls.Add(statusLabel);           
        notificationDialog.Show(ownerWindow);

        // Do the content loading/enumeration on a worker thread so we
        // can keep pumping messages on this thread
        Stopwatch timer = new Stopwatch();
        timer.Start();
        int count = 0;
        long time = 0;
        long lastTime = 0;
        long loadTime = 0;
        Thread workerThread = new Thread(EnumerateContent);
        workerThread.Start();

        while (!workerThread.Join(0))
        {
            Application.DoEvents();

            time = timer.ElapsedMilliseconds;
            loadTime += time - lastTime;
            lastTime = time;

            if (loadTime &gt; 300)
            {
                statusLabel.Text = &#34;Loading Content&#34;;
                for (int i = 1; i &lt;= count % 4; i++)
                    statusLabel.Text += &#34;.&#34;;
                count++;
                loadTime -= 300;
            }
        }

        notificationDialog.Hide();

        // Dispose of the content
        contentManager.Dispose();      contentManager = null;      GC.Collect();
    }

    /// &lt;summary&gt;
    /// Enumerates all loadable content for types in contentTypes, and stores
    /// the resulting tree in contentTrees
    /// &lt;/summary&gt;
    private static void EnumerateContent()
    {
        // Recursively build the content tree
        foreach (string contentType in contentTypes)
        {
            TreeNode rootNode = new TreeNode(&#34;Content\\&#34;);
            BuildContentTree(rootNode, contentManager.RootDirectory, contentType);
            contentTrees.Add(contentType, rootNode);
        }
    }

    /// &lt;summary&gt;
    /// Builds the tree by looking for acceptable content.  Recursively calls itself
    /// to traverse subdirectories
    /// &lt;param name=&#34;parentNode&#34;&gt;The TreeNode representing the current directory&lt;/param&gt;
    /// &lt;param name=&#34;directory&#34;&gt;The current direcotry to traverse&lt;/param&gt;
    /// &lt;param name=&#34;contentType&#34;&gt;The name of the content Type to look for&lt;/param&gt;
    /// &lt;/summary&gt;
    private static void BuildContentTree(TreeNode parentNode, string directory, string contentType)
    {
        // Find all the subdirectories, and recursively search them
        string[] subdirectories = Directory.GetDirectories(directory);
        foreach (string subdirectory in subdirectories)
        {
            string relativePath = subdirectory.Substring(directory.Length + 1);
            TreeNode directoryNode = new TreeNode(relativePath + &#34;\\&#34;);
            BuildContentTree(directoryNode, subdirectory, contentType);
            if (directoryNode.Nodes.Count &gt; 0)
                parentNode.Nodes.Add(directoryNode);
        }

        // Check out all the .xnb files, see if we can load them as the target type
        string[] contentFiles = Directory.GetFiles(directory, &#34;*.xnb&#34;);
        foreach (string contentFile in contentFiles)
        {
            string loadName = Path.GetDirectoryName(contentFile) + &#34;\\&#34;
                            + Path.GetFileNameWithoutExtension(contentFile);
            if (TryLoadContent(loadName, contentType))
            {
                TreeNode contentNode = new TreeNode(Path.GetFileNameWithoutExtension(contentFile));
                contentNode.Tag = loadName;
                parentNode.Nodes.Add(contentNode);
            }
        }
    }

    /// &lt;summary&gt;
    /// Checks whether the filename is valid by attempting to
    /// load it with the ContentManager.
    /// &lt;/summary&gt;
    /// &lt;param name=&#34;contentFile&#34;&gt;The filename to check&lt;/param&gt;
    /// &lt;param name=&#34;contentType&#34;&gt;The name of the content Type to check the content against&lt;/param&gt;
    /// &lt;returns&gt;True if successful&lt;/returns&gt;
    private static bool TryLoadContent(string contentFile, string contentType)
    {
        try
        {
            object content = contentManager.Load&lt;object&gt;(contentFile);
            if (content.GetType().Name == contentType)
                return true;
            else
                return false;
        }
        catch (ContentLoadException)
        {
            return false;
        }
    }

    private string selectedContentFile;
    public string SelectedContentFile
    {
        get { return selectedContentFile; }
    }

    /// &lt;summary&gt;
    /// Creates an instance of ContentBrowser
    /// &lt;/summary&gt;
    /// &lt;param name=&#34;value&#34;&gt;The default content filename&lt;/param&gt;
    /// &lt;param name=&#34;contentType&#34;&gt;The type of content to browse&lt;/param&gt;
    public ContentBrowser(String value, Type contentType)
    {
        InitializeComponent();

        contentTree.Nodes.Add(contentTrees[contentType.Name]);
        contentTree.ExpandAll();

        selectedContentFile = value;
    }

    /// &lt;summary&gt;
    /// Called when the Form is closed
    /// &lt;/summary&gt;
    /// &lt;param name=&#34;e&#34;&gt;&lt;/param&gt;
    protected override void OnClosed(EventArgs e)
    {
        contentTree.Nodes.Clear();
        base.OnClosed(e);
    }

    /// &lt;summary&gt;
    /// Event handler for the TreeView&#39;s mouse clicks
    /// &lt;/summary&gt;
    /// &lt;param name=&#34;sender&#34;&gt;contentTree&lt;/param&gt;
    /// &lt;param name=&#34;e&#34;&gt;Event args&lt;/param&gt;
    private void contentTree_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
    {
        // Tag != null means it&#39;s a content node
        if (e.Node.Tag != null)
        {
            selectedContentFile = (string)e.Node.Tag;
            selectedContentFile = selectedContentFile.Substring(contentDirectory.Length + 1);
        }
    }
}
</code></pre><p>Okay so a few notes on usage&hellip;it uses the string array &ldquo;contentTypes&rdquo; to know which types to check for.  You should fill this out with whatever Type&rsquo;s you&rsquo;re loading from the ContentManager.  The static Initialize method should either be called when your app starts up, and you&rsquo;ll need to do it before you can actually create an instance of ContentBrowser.  It shows a little loading dialog while it&rsquo;s working, so you have something to show the user while it&rsquo;s happening.  You could take that out, if you wanted.</p>
<p>I also made a UITypeEditor so that you can have this dialog to set a property in a PropertyGrid:</p>
<pre tabindex="0"><code>/// &lt;summary&gt;
/// Used to allow the user to browse for content in the PropertyGrid
/// &lt;/summary&gt;
/// &lt;typeparam name=&#34;T&#34;&gt;The Type of content to display in the ContentBrowser&lt;/typeparam&gt;
public class ContentEditor&lt;T&gt; : UITypeEditor
{
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {           
         return UITypeEditorEditStyle.Modal;
    }

    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        IWindowsFormsEditorService editorService = null;
        if (provider != null)
        {
            editorService = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
        }

        if (editorService != null)
        {
            // Pop up our dialog
            ContentBrowser browser = new ContentBrowser((string)value, typeof(T));
            if (editorService.ShowDialog(browser) == DialogResult.OK)
                value = browser.SelectedContentFile;
        }

        return value;
    }
}
</code></pre>]]></content:encoded>
		</item>
		
		<item>
			<title>Book Recommendation: Real-Time Collision Detection</title>
			<link>https://therealmjp.github.io/posts/book-recommendation-real-time-collision-detection/</link>
			<pubDate>Sat, 17 Jan 2009 23:27:18 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/book-recommendation-real-time-collision-detection/</guid>
			<description>I just recently ordered and received Real-Time Collision Detection from Amazon, and it was worth every penny. Collision detection was never something I was never particularly interested in, and in that past I was always willing to just leave it all up to a physics package to handle. But as anyone else working on an XNA game for the 360 knows, a physics engine isn&amp;rsquo;t really a practical option this time around.</description>
			<content:encoded><![CDATA[<p>I just recently ordered and received <a href="http://realtimecollisiondetection.net/" title="Real-TimeCollision Detection">Real-Time Collision Detection</a> from <a href="http://www.amazon.com/Real-Time-Collision-Detection-Interactive-Technology/dp/1558607323">Amazon</a>, and it was worth every penny.  Collision detection was never something I was never particularly interested in, and in that past I was always willing to just leave it all up to a physics package to handle.  But as anyone else working on an XNA game for the 360 knows, a physics engine isn&rsquo;t really a practical option this time around.  I&rsquo;ve had some basic stuff working for a long time now (thanks to <a href="http://fabio.policarpo.nom.br/">Fabio Policarpo&rsquo;s</a> BoxCollider), but my level designer has made some requests that have led me to finally dig in and start making my system robust while keeping it performance.  Fortunately the book I ordered cam to the rescue:  it has some fantastic overviews and demonstration of just about every aspect of collision detection.  The explanations are concise and get right to the important points, and derivation of the mathematical formulas are always followed by a sample implementation in C++ (If you&rsquo;re like me and you hate pseudo-code, then this truly is a wonderful thing).</p>
<p>So kids if you don&rsquo;t have it yet&hellip;go get this book.</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22%22">Rim</a> - <time datetime="2009-01-19 06:49:48">Jan 1, 2009</time></p>
<p>Yes, sir :)</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Undo and Redo: Yes you have to implement it</title>
			<link>https://therealmjp.github.io/posts/undo-and-redo/</link>
			<pubDate>Fri, 19 Dec 2008 19:05:15 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/undo-and-redo/</guid>
			<description>A professional developer on the gamedev.net forums once said &amp;ldquo;if you&amp;rsquo;ve implemented Undo and Redo in your app, then you&amp;rsquo;re in the top 1% of applicants for a tools developer position&amp;rdquo;. That&amp;rsquo;s funny to me, because I have no idea how you could possibly have a useful tool without such a fundamental element of GUI application development. I mean&amp;hellip;people screw up. It&amp;rsquo;s nice for users to know that their mistake can go away with a single press of &amp;ldquo;ctrl+z&amp;rdquo;.</description>
			<content:encoded><![CDATA[<p>A professional developer on the gamedev.net forums once said &ldquo;if you&rsquo;ve implemented Undo and Redo in your app, then you&rsquo;re in the top 1% of applicants for a tools developer position&rdquo;.  That&rsquo;s funny to me, because I have no idea how you could possibly have a useful tool without such a fundamental element of GUI application development.  I mean&hellip;people screw up.  It&rsquo;s nice for users to know that their mistake can go away with a single press of &ldquo;ctrl+z&rdquo;.</p>
<p>When I first started working on the map editor for my game <a href="http://mynameismjp.wordpress.com/jumpswitch/">JumpSwitch</a>, I put Undo/Redo functionality in there right away.  Like I said, it&rsquo;s fundamental.  The problem was, I implemented it probably the most naive way possible.  See I handled saving and loading by having my Level class dump a whole bunch of data into a LevelData class, and then serializing that to XML.  So I thought &ldquo;hey, I&rsquo;ll just hang onto an array of these LevelData instances and then load them up when the user wants to Undo or Redo!&rdquo;  Yeah I know, dumb.  Feel free to laugh.  Worked okay when my level had under a dozen entities hanging around, but anything after that meant you were waiting 3 or more seconds.  Not exactly what I&rsquo;d call a fast and responsive UI.</p>
<p>So&hellip;.what had to be done?  I had to stop being lazy and do Undo and Redo for real.  This we got a little more sophisticated:</p>
<p>-Make an abstract &ldquo;EditAction&rdquo; class, that represents a single action taken by the user to edit the level.  Give it virtual methods for &ldquo;Undo&rdquo; and &ldquo;Redo&rdquo;
-Make a few classes that inherit from EditAction and implement Undo and Redo for a specific action (moving an object, rotating an object, adding/removing an object, changing a Property, etc.)
-MapEditorLevel class has two stacks of EditAction&rsquo;s: an Undo stack and a Redo stack.  When the user performs an action, it&rsquo;s pushed onto the Undo stack and the Redo stack is cleared.  When the user does an Undo, the Undo stack is popped, Undo is called on that EditAction, and the action is pushed onto the Redo stack. When the user does a Redo, the Redo stack is popped, Redo is called, and the action is pushed onto the Undo stack.</p>
<pre tabindex="0"><code>    /// &lt;summary&gt;
    /// Represents a single action taken by the user to edit the level,
    /// and that can be Undone/Redone.
    /// &lt;/summary&gt;
    public abstract class EditAction
    {
        protected MapEditorLevel level;

        public EditAction(MapEditorLevel level)
        {
            this.level = level;
        }

        /// &lt;summary&gt;
        /// Undoes the action
        /// &lt;/summary&gt;
        public abstract void Undo();

        /// &lt;summary&gt;
        /// Redoes the action
        /// &lt;/summary&gt;
        public abstract void Redo();
    }

    /// &lt;summary&gt;
    /// Handles undo/redo for a rotation of a GameObject
    /// &lt;/summary&gt;
    public class RotateAction : EditAction
    {
        GameObject[] targetObjects;
        Matrix rotation;
        Matrix inverseRotation;

        public RotateAction(MapEditorLevel level, GameObject[] targetObjects, Matrix rotation)
            : base(level)
        {
            this.targetObjects = targetObjects;
            this.rotation = rotation;
            inverseRotation = Matrix.Invert(rotation);
        }

        public override void Undo()
        {
            foreach (GameObject targetObject in targetObjects)
                targetObject.PropogateRotation(ref inverseRotation);
        }

        public override void Redo()
        {
            foreach (GameObject targetObject in targetObjects)
                targetObject.PropogateRotation(ref rotation);
        }
    }

    /// &lt;summary&gt;
    /// Handles UndoRedo for properties
    /// &lt;/summary&gt;
    public class PropertyEditAction : EditAction
    {
        PropertyInfo property;
        object[] oldValues;
        object newValue;
        object[] propertyOwners;

        public PropertyEditAction(MapEditorLevel level, object[] propertyOwners, object[] oldValues, string propertyName)
            : base(level)
        {
            this.oldValues = oldValues;
            this.propertyOwners = propertyOwners;

            Type ownerType = propertyOwners[0].GetType();

            // Find the property
            property = ownerType.GetProperty(propertyName);

            // Get the new value
            newValue = property.GetValue(propertyOwners[0], null);
        }

        public override void  Undo()
        {
            for (int i = 0; i &lt; propertyOwners.Length; i++)
                property.SetValue(propertyOwners[i], oldValues[i], null);
        }

        public override void Redo()
        {
            for (int i = 0; i &lt; propertyOwners.Length; i++)
                property.SetValue(propertyOwners[i], newValue, null);
        }
    }

    // You get the idea...
</code></pre><p>Alright, looks like a solid plan.  It worked out very nicely for movement and rotation.  Movement is just translation which is a vector, so if you negate it you get the Undo.  For the rotations, it&rsquo;s a matrix so you just invert it.  Piece of cake.  Adding and removing&hellip;a little more tricky since I had to also keep track of an object&rsquo;s parent so I&rsquo;d know where to re-add the object.  But still not too bad.  Changing properties&hellip;not so nice.  The problem is that when the user changes the value of a property with the PropertyGrid, it fires a PropertyValueChanged event that lets you know what changed, and what the old value was.  Perfect, right?  Well yes&hellip;but only for single objects.  When you have multiple objects selected in the PropertyGrid you get &ldquo;null&rdquo; instead of the old value.  Fantastic.  Workaround  time&hellip;</p>
<ul>
<li>Handle the SelectedGridItemChanged event for the PropertyGrid</li>
<li>Store the current value of the selected Property for all selected objects in an array</li>
<li>Handle PropertyValueChanged</li>
<li>Make an array of current Property values</li>
<li>Send it all off to the MapEditorLevel so it can create an appropriate EditAction and push it onto the stack</li>
</ul>
<p>Okay, so this works. Only problem left with that is that when you set the value of a Property through PropertyInfo.SetValue, the PropertyGrid doesn&rsquo;t reflect the changes.  Still haven&rsquo;t figured out a workaround for that one&hellip;</p>
<p>So in the end it was a little messy, but not that bad.  It only took me a single evening in fact.  Certainly nothing worthy of that frightening 99% statistic, IMO.   Besides&hellip;if you don&rsquo;t implement it, it&rsquo;s going to be first thing your designers and artists complain to you about anyway.  :P</p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="%22robthebloke@hotmail.com%22">rob bateman (aka robthebloke)</a> - <time datetime="2009-03-10 06:00:06">Mar 2, 2009</time></p>
<p>hehe, i think that comment regarding the top 1% of tool devs sounds a lot like i said it (because no-one actually implements it - and it&rsquo;s the bain of my life). Nice article btw, it&rsquo;s surprising how many people still overlook the basics of undo/redo, it can be relatively straightforward - but it can quickly become quite hacky and laborious to maintain. My next question then, is how would your system work with app level scripting? How will your method be applied to custom user scripts (to create game objects, modify all objects of a similar type etc) in a way that enables undo/redo to work for custom user tools? Think about that for a minute before continuing &hellip;. There are a few ways you can approach it, but logically extending your approach will leave you with an approach fairly similar to maya&rsquo;s. Ultimately all commands in maya are represented with an MPxCommand object, which performs the undo/redo actions. With maya&rsquo;s way of doing this, you&rsquo;d start by modifying your EditAction class to contain a list of sub actions. Your Edit Action class can now represent a script call (where each unique command has it&rsquo;s own EditAction class), and the undo/redo steps actually becomes a hierarchy of undo/redo actions rather than a single step. e.g. proc foo() { createBox(); } proc bar() { setValue(&ldquo;someObject.translate&rdquo;,1,2,3); foo(); } so bar() will store 2 sub actions, setValue() and foo(). foo() will store the subaction createBox(). (It might be worth pointing you at some docs for MPxCommand at this point: <a href="http://caad.arch.ethz.ch/info/maya/manual/DevKit/PlugInsAPI/classDoc/MPxCommand.html">http://caad.arch.ethz.ch/info/maya/manual/DevKit/PlugInsAPI/classDoc/MPxCommand.html</a> ) - doIt builds up an internal list of what the command is going to do - redoIt performs those actions and nothing else. - undoIt simply performs the inverse of those actions. It&rsquo;s important to note that doIt() will always call redoIt() after it&rsquo;s built up the internal list. That way you save on code duplication. It&rsquo;s then worth noting that every single user action (be it clicking on a button, moving an object with a mouse, opening a file etc) has to be executed as a script command. This basically simplifies all of the undo/redo for the whole app into a single command interface. Maya does have a fairly nice class that aims to try to wrap up a lot of the common undo/redo actions (see: <a href="http://caad.arch.ethz.ch/info/maya/manual/DevKit/PlugInsAPI/classDoc/MDagModifier.html">http://caad.arch.ethz.ch/info/maya/manual/DevKit/PlugInsAPI/classDoc/MDagModifier.html</a> ). The only problem is that quite often, that class just isn&rsquo;t good enough, and so you have to do all of that manually. which sucks. a lot. A bigger problem with this technique is that you end up having to implement an awful lot of boilerplate code for a lot of commands, eg&hellip; <a href="http://caad.arch.ethz.ch/info/maya/manual/Commands/index.html">http://caad.arch.ethz.ch/info/maya/manual/Commands/index.html</a> and whilst it works&hellip;. it quickly becomes a royal PITA to maintain, with a hell of a lot of bugs thrown in for comedy value. I have seen a couple of clever ways to automate the whole undo/redo process, which are a dream to work with. Unfortunately i can&rsquo;t comment on those due to NDA&rsquo;s, but it might give you something to think about ;) Rob.</p>
<hr />
####
[mpettineo](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2009-03-10 16:54:15">Mar 2, 2009</time>
<p>Indeed&hellip;I didn&rsquo;t have to create too many more EditAction derivatives before I realized how much of a pain the system was to extend and maintain. It really doesn&rsquo;t help that I keep making big changes to the map editor&hellip;unfortunately it&rsquo;s the first (3D) editor I&rsquo;ve ever made and without experience behind me I just keep running into problems that seem obvious in hindsight. But I&rsquo;m learning I suppose. :) That Maya API stuff is really interesting. Like you said&hellip;on one hand it works since the basic underlying system is generic, but after a while it all just explodes into this huge monster. I have enough trouble keeping my EditAction&rsquo;s from glitching&hellip;I can&rsquo;t imagine trying to maintain the amount of commands they have available. When I first starting implementing the system I have right now, I had a few visions in my head of a system where the user/map boundary was designed in such a way that any modifying of the map was just a generic transaction that could be easily understood by the Undo/Redo system without having to write specific code to handle it. What I&rsquo;m doing with the PropertyGrid is actually sorta in that direction&hellip;I do a lot of custom stuff with the PropertyGrid control and (for the most part) my Undo/Redo stuff works for that without me having to do anything special. I figure if you planned out right, you could probably extend that somehow (probably using a lot of Attributes for metadata) to make an automatic (or mostly automatic) system&hellip;at least for a managed app, anyway. I&rsquo;d love to refactor what I have into something that would make this work&hellip;but unfortunately I&rsquo;ve got a thousand other things to do for the project and almost zero free time to do them. In fact I wish I had more time to devote to tool development in general&hellip;I really enjoy it but at the moment I have to keep bouncing back and forth to different areas and subsystems. Anyway, thank you so much for comments! It&rsquo;s nice to discuss these things with someone&hellip;it&rsquo;s not a popular topic on the forums I&rsquo;m afraid. I also greatly appreciate the insights you&rsquo;ve offered&hellip;especially the ones that aren&rsquo;t NDA&rsquo;ed. :P</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>LogLuv Encoding for HDR</title>
			<link>https://therealmjp.github.io/posts/logluv-encoding-for-hdr/</link>
			<pubDate>Fri, 12 Dec 2008 17:00:59 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/logluv-encoding-for-hdr/</guid>
			<description>Okay so this marks the third time I&amp;rsquo;ve posted this blog entry somewhere. What can I say&amp;hellip;I like it! I also think it&amp;rsquo;s something useful for just about anyone trying to do HDR on the 360 through XNA, and I&amp;rsquo;m hoping some people will stumble upon it.
Designing an effective and performant HDR implementation for my game&amp;rsquo;s engine was a step that was complicated a bit by a few of the quirks of running XNA on the Xbox 360.</description>
			<content:encoded><![CDATA[<p><em>Okay so this marks the third time I&rsquo;ve posted this blog entry somewhere.  What can  I say&hellip;I like it!  I also think it&rsquo;s something useful for just about anyone trying to do HDR on the 360 through XNA, and I&rsquo;m hoping some people will stumble upon it.</em></p>
<p>Designing an effective and performant HDR implementation for my game&rsquo;s engine was a step that was complicated a bit by a few of the quirks of running XNA on the Xbox 360.  As a quick refresher for those who aren&rsquo;t experts on the subject, HDR is most commonly implemented by rendering the scene to a floating-point buffer and then performing a tone-mapping pass to bring the colors back into he visible range. Floating-point formats (like A16B16G16R16F, AKA HalfVector4) are used because their added precision and floating-point nature allows them to comfortbly store linear RGB values in ranges beyond the [0,1] typically used for shader output to the backbuffer, which is crucial as HDR requires having data with a wide dynamic range. They&rsquo;re also convenient, as this it allows values to be stored in the same format they&rsquo;re manipulated in the shaders. Newer GPU&rsquo;s also support full texture filtering and alpha-blending with fp surfaces, which prevents the need for special-case handling of things like non-opaque geometry. However as with most things, what&rsquo;s convient is not always the best option. During planning, I came up with the following list of pro&rsquo;s and con&rsquo;s for various types of HDR implementations:</p>
<p>Standard HDR, fp16 buffer
+Very easy to integrate (no special work needed for the shaders)
+Good precision
+Support for blending on SM3.0+ PC GPU&rsquo;s
+Allows for HDR bloom effects
-Double the bandwidth and storage requirements of R8G8B8A8
-Weak support for multi-sampling on SM3.0 GPU&rsquo;s (Nvidia NV40 and G70/G71 can&rsquo;t do it)
-Hardware filtering not available on ATI SM2.0 and SM3.0 GPU&rsquo;s
-No blending on the Xbox 360
-Requires double space in framebuffer on the 360, which increases the number of tiles needed</p>
<p>HDR with tone-mapping applied directly in the pixel shader (Valve-style)
+Doesn&rsquo;t require output to an HDR format, no floating-point or encoding required
+Multi-sampling and blending is supported, even on old hardware
-Can&rsquo;t do HDR bloom, since only an LDR image is available for post-processing
-Luminance can&rsquo;t be calculated directly, need to use fancy techniques to estimate it
-Increases shader complexity and combinations</p>
<p>HDR using an encoded format
+Allows for a standard tone-mapping chain
+Allows for HDR bloom effects
+Most formats offer a very wide dynamic range
+Same bandwidth and storage as LDR rendering
+Certain formats allow for multi-sampling and/or linear filtering with reasonable quality
-Alpha-blending usually isn&rsquo;t an option, since the alpha-channel is used by most formats
-Linear filtering and multisampling usually isn&rsquo;t mathmatically correct, although often the results are &ldquo;good enough&rdquo;
-Additional shader math needed for format conversions
-Adds complexity to shaders</p>
<p>My early prototyping used a standard tone-mapping chain and I didn&rsquo;t want to ditch that, nor did I want to move away from what I was comfortable with.  This pretty much eliminated the second option for me off the bat&hellip;although I was unlikely to choose it anyway due its other drawbacks (having nice HDR bloom was something I felt was an important part of the look I wanted for my game, and in my opinion Valve&rsquo;s method doesn&rsquo;t do a great job of determining average luminance).  When I tried out the first method I found that it worked as well as it always did on the PC (I&rsquo;ve used it before), but on the 360 it was another story.  I&rsquo;m not sure why exactly, but for some reason it simply does not like the HalfVector4 format.  Performance was terrible, I couldn&rsquo;t blend, I got all kinds of strange rendering artifacts (entire lines of pixels missing), and I&rsquo;d get bizarre exceptions if I enabled multisampling. Loads of fun, let me tell you.</p>
<p>This left me with option #3.  I wasn&rsquo;t a fan of this approach initially, as my original design plan called for things to be simple and straightforward whenever possible.  I didn&rsquo;t really want to have two versions of my material shaders to support encoding, nor did I want to integrate decoding into the other parts of the pipeline that needed it.  But unfortunately, I wasn&rsquo;t really left with any other options after I found there were <a href="https://connect.microsoft.com/feedback/ViewFeedback.aspx?FeedbackID=343887&amp;SiteID=226">no plans</a> to bring the support for the 360&rsquo;s special fp10 backbuffer format to XNA (which would have conveniently solved my problems on the 360).  So, I started doing my research.  Naturally the first place I looked was to actual released commercial game.  Why?  Because usually when a technique is used in a shipped game, it means it&rsquo;s gone though the paces and has been determined to actually be feasible and practical in game environment.  Which of course naturally led me to consider NAO32.</p>
<p>NAO32 is a format that gained some fame in the dev community when ex-Ninja Theory programmer Marco Salvi shared some details on the technique over on the beyond3D forums.  Used in the game Heavenly Sword, it allowed for multisampling to be used in conjuction with HDR on a platform (PS3) whose GPU didn&rsquo;t support multisampling of floating-point surfaces (The RSX is heavily based on Nvidia G70).  In this technique, color is stored in the <a href="http://www.anyhere.com/gward/papers/jgtpap1.pdf">LogLuv format</a> using a standard R8G8B8A8 surface.  Two components are used to store X and Y at 8-bit precision, and the other two are used to store the log of luminance at 16-bit precision.  Having 16 bits for luminance allows for a wide dynamic range to be stored in this format, and storing the log of the luminance allows for linear filtering in multisampling or texture sampling.  Since he first explained it other games have also used it, such as Naughty Dog&rsquo;s Uncharted.  It&rsquo;s likely that it&rsquo;s been used in many other PS3 games, as well.</p>
<p>My actual shader implementation was helped along quite a bit by <a href="http://realtimecollisiondetection.net/blog/?p=15">Christer Ericson&rsquo;s blog post</a>, which described how to derive optimized shader code for encoding RGB into the LogLuv format.  Using his code as a starting point, I came up with the following HLSL code for encoding and decoding:</p>
<pre tabindex="0"><code>// M matrix, for encoding

const static float3x3 M = float3x3(
    0.2209, 0.3390, 0.4184,
    0.1138, 0.6780, 0.7319,
    0.0102, 0.1130, 0.2969);

// Inverse M matrix, for decoding
const static float3x3 InverseM = float3x3(
    6.0013,    -2.700,    -1.7995,
    -1.332,    3.1029,    -5.7720,
    .3007,    -1.088,    5.6268);   

float4 LogLuvEncode(in float3 vRGB)
{
    float4 vResult;
    float3 Xp_Y_XYZp = mul(vRGB, M);
    Xp_Y_XYZp = max(Xp_Y_XYZp, float3(1e-6, 1e-6, 1e-6));
    vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;
    float Le = 2 * log2(Xp_Y_XYZp.y) + 127;
    vResult.w = frac(Le);
    vResult.z = (Le - (floor(vResult.w*255.0f))/255.0f)/255.0f;
    return vResult;
}

float3 LogLuvDecode(in float4 vLogLuv)
{
    float Le = vLogLuv.z * 255 + vLogLuv.w;
    float3 Xp_Y_XYZp;
    Xp_Y_XYZp.y = exp2((Le - 127) / 2);
    Xp_Y_XYZp.z = Xp_Y_XYZp.y / vLogLuv.y;
    Xp_Y_XYZp.x = vLogLuv.x * Xp_Y_XYZp.z;
    float3 vRGB = mul(Xp_Y_XYZp, InverseM);
    return max(vRGB, 0);
}
</code></pre><p>Once I had this implemented and worked through a few <a href="http://www.gamedev.net/community/forums/topic.asp?topic_id=500219">small glitches</a>;, results were much improved in the 360 version of my game. Performance was much much better, I could multi-sample again, and the results looked great. So while things didn&rsquo;t exactly work out in an ideal way, I&rsquo;m pleased enough with the results.</p>
<p>If you&rsquo;re interested in this, be sure to check out my <a href="http://mynameismjp.wordpress.com/samples-tutorials-tools/xna-hdr/">sample</a></p>
<hr>
<h3 id="comments">Comments:</h3>
<h4 id="heading"></h4>
<p><a href="http://blog.daikonforge.com" title="takuan@daikonforge.com">Takuan</a> - <time datetime="2008-12-12 14:25:01">Dec 5, 2008</time></p>
<blockquote>
<p>I also think it’s something useful for just about anyone trying to do HDR on the 360 through XNA, and I’m hoping some people will stumble upon it.</p>
</blockquote>
<p>I didn&rsquo;t exactly stumble on it, I found your blog through GameDev.net, but it is indeed useful!</p>
<hr />
####
[matt77hias]( "matthias.moulin@gmail.com") - <time datetime="2018-04-08 03:20:38">Apr 0, 2018</time>
<p>Thanks!</p>
<hr />
####
[matt77hias]( "matthias.moulin@gmail.com") - <time datetime="2018-04-05 11:42:29">Apr 4, 2018</time>
<p>Two small questions: 1) What is the reasoning behing your &ldquo;Xp_Y_XYZp&rdquo; local variable name? 2) What do the matrices M and inverseM convert to/from? It is not just linear to LogLUV and vice versa since the conversion isn&rsquo;t finished yet?</p>
<hr />
####
[MJP](http://mynameismjp.wordpress.com/ "mpettineo@gmail.com") - <time datetime="2018-04-05 21:52:11">Apr 4, 2018</time>
<ol>
<li>The code was taken from Christer&rsquo;s blog post, so he&rsquo;s the one who came up with the variable names. If you read through the linked post, he explains how he simplified the math into what&rsquo;s represented in the code. He calls it that because one of the intermediate steps is converting to CIE XYZ, and he folds a dot product into the matrix multiply to produce X, Y, and the dot product result (that he calls &lsquo;XYZ&rsquo;). He also folds in the multiplication of two other constants, to produce what he calls X` and XYZ`, hence the &lsquo;p&rsquo; in the variable name (short for &lsquo;prime&rsquo;, I assume). 2) See above. It&rsquo;s a combination of converting from RGB -&gt; XYZ, and some of the related math for converting to LogLuv.</li>
</ol>
<hr />
####
[matt77hias]( "matthias.moulin@gmail.com") - <time datetime="2018-04-11 03:32:15">Apr 3, 2018</time>
<p>Sorry for bothering you with this again, but I read the article of Christer which started with a linear RGB to XYZ conversion and sequentially added extra operations to the conversion matrix M. I am not very familiar with display formats, so feel free to correct me. The CCIR became the ITU-R, so I presume that &ldquo;CCIR 709&rdquo; is the same as &ldquo;ITU-R Recommendation BT.709&rdquo; (a.k.a. &ldquo;Rec. 709&rdquo; or &ldquo;BT.709&rdquo;)? If this is the case, the used RGB-to-XYZ conversion (cfr. paper by Gregory Ward) is different from the one used in Real-Time Rendering (3th edition), pbrt-v2, pbrt-v3 and Mitsuba (which all use the same coefficients)? Thanks in advance.</p>
<hr />
]]></content:encoded>
		</item>
		
		<item>
			<title>Jamming to the oldies</title>
			<link>https://therealmjp.github.io/posts/oldies/</link>
			<pubDate>Fri, 12 Dec 2008 16:39:21 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/oldies/</guid>
			<description>Just wanted to post some links to old blog entries from gamedev.net&amp;hellip;
Working With Unicode in the Windows API
Don&amp;rsquo;t Cast Function Pointers (Unless You Really Know What You&amp;rsquo;re Doing)
Posting WM_DESTROY is *not* how you destroy a window</description>
			<content:encoded><![CDATA[<p>Just wanted to post some links to old blog entries from gamedev.net&hellip;</p>
<p><a href="http://www.gamedev.net/community/forums/mod/journal/journal.asp?jn=475298&amp;#3182072"><strong>Working With Unicode in the Windows API</strong></a></p>
<p><a href="http://www.gamedev.net/community/forums/mod/journal/journal.asp?jn=475298&amp;#3294497"><strong>Don&rsquo;t Cast Function Pointers (Unless You Really Know What You&rsquo;re Doing)</strong></a></p>
<p><a href="http://www.gamedev.net/community/forums/mod/journal/journal.asp?jn=475298&amp;#3291725"><strong>Posting WM_DESTROY is *not* how you destroy a window</strong></a></p>
]]></content:encoded>
		</item>
		
		<item>
			<title>Cleared for takeoff</title>
			<link>https://therealmjp.github.io/posts/cleared-for-takeoff/</link>
			<pubDate>Wed, 10 Dec 2008 21:22:13 +0000</pubDate>
			
			<guid>https://therealmjp.github.io/posts/cleared-for-takeoff/</guid>
			<description>Look out interwebs, I have a new blog to call my own!
*insert trumpet fanfare here*
Here&amp;rsquo;s what I plan on doing with it: -integrating some of the stuff I&amp;rsquo;ve been posting on gamedev.net and xnainfo.com in one place -put up some samples/tutorials/tools/code I&amp;rsquo;ve come up with -yammer on about JumpSwitch, the Xbox 360 game I&amp;rsquo;m working on -set up a page to show some sweet pics and vids of JumpSwitch, so everyone can see how awesome it is -post links to cool and helpful stuff I find -try to somewhat serious (not likely to happen)</description>
			<content:encoded><![CDATA[<p><img src="/images/converted/takeoff.jpg" alt="takeoff!"></p>
<p>Look out interwebs, I have a new blog to call my own!</p>
<p>*insert trumpet fanfare here*</p>
<p>Here&rsquo;s what I plan on doing with it:
-integrating some of the stuff I&rsquo;ve been posting on gamedev.net and xnainfo.com in one place
-put up some samples/tutorials/tools/code I&rsquo;ve come up with
-yammer on about JumpSwitch, the Xbox 360 game I&rsquo;m working on
-set up a page to show some sweet pics and vids of JumpSwitch, so everyone can see how awesome it is
-post links to cool and helpful stuff I find
-try to somewhat serious (not likely to happen)</p>
<p>Exciting, right?</p>
]]></content:encoded>
		</item>
		
	</channel>
</rss>
