Skip to content

Aardvark Rendering 5.6 changelog

Martin edited this page Jan 12, 2026 · 4 revisions

Aardvark.Rendering 5.6 comes with new features, improvements, and changes that require manual intervention when upgrading existing code. This document outlines the most important changes.

64-bit floating-point support for shaders

It is now possible to write shaders that make use of 64-bit floating-point data. Up until now, 32-bit and 64-bit floating-point types were treated equally and could be used interchangeably in shaders. FShade compiled those types and values as 32-bit floats, regardless of the original type.

With this Aardvark.Rendering and FShade release, this feature is removed and floating-point types are compiled accurately. This means that existing shaders, which predominantly used 64-bit floats, have to be fixed when upgrading. To this end, use regular expressions with the find-and-replace tool of your IDE or editor:

  • ((?:[0-9]*\.[0-9]+)|(?:[0-9]+[eE][-+]?[0-9]+))$1f
  • ((?:[vV][2-4])|(?:[mM][2-4][2-4]))d$1f
  • float|doublefloat32
  • ConstantConstantF

Make sure to restrict the search to shader-related code only. If you miss a shader, there is a chance that the F# code will not compile due to type changes of built-in uniforms in Aardvark.Rendering. Unfortunately, some broken shaders may compile fine and only fail at runtime. Because of this, the .NET tool fshadeaot has a feature for detecting shaders that still use 64-bit floats:

dotnet tool install -g fshadeaot
fshadeaot -d ./MyApp.dll

This will search the specified assembly and its dependencies recursively for shaders that still need to fixed:

Sky.Shaders.vsSky uses double-based types: Sky.Shaders.VertexSky, Aardvark.Base.V2d, Aardvark.Base.V4d, Aardvark.Base.M44d, Aardvark.Base.V3d, System.Double

The new FShade 5.7 release comes with many more changes and improvements such as support for 8-bit and 16-bit integers and color types. Refer to the release notes for a complete list.

ImGui integration

Aardvark.Rendering.ImGui is a wrapper and an Aardvark backend for the excellent Dear ImGui library. It allows users to quickly build GUIs for their applications without having to worry about state synchronization between the application and the user interface.

The integration of ImGui into Aardvark.Rendering means that users may enrich their demo applications showcasing rendering techniques with a proper GUI without having to rely on Aardvark.Media, which is often overkill for simple demo controls. Various examples in Aardvark.Rendering have been improved with simple user interfaces, most notably the reversed depth example.

Adding ImGui to an application is easy:

  1. Reference Aardvark.Rendering.ImGui in your project.
  2. Initialize ImGui for your window:
    use gui = window.InitializeImGui()
    The window must be created with a GLFW application (Aardvark.Application.Slim or Aardvark.Applcation.Utilities). If you use the window builder, call window.Control.InitializeImGui() instead.
  3. Define your GUI by setting the render function of ImGui:
    gui.Render <- fun () ->
        ImGui.ShowDemoWindow()
    This function is called every frame to render the GUI. The methods from the ImGui class contain all the relevant widgets and controls. For convenience, Aardvark.Rendering.ImGui provides variants for cval and other Aardvark-specific types. As a reference, take a look at the demo window and its source code.
  4. Add the GUI to your scene graph:
    let cmd = RenderCommand.Ordered [scene; gui]
    window.Scene <- Sg.execute cmd
    Note that Aardvark.Rendering.ImGui.Instance implements ISg and can be added to the scene graph directly. Make sure that the GUI is rendered after the rest of the scene graph by using render commands.

Vulkan backend improvements

This release comes with new features and substantial internal changes to the Vulkan backend improving both performance and reliability.

Command queues and buffers

The management of command queues and submission of command buffers has been reworked from scratch. Queue submissions are no longer managed by dedicated worker threads. Instead, thread-exclusive access to an available queue has to be acquired before directly submitting commands to that queue. This minimizes overhead in single threaded scenarios and results in measurable performance improvements in real-world applications.

Vulkan Memory Allocator

Our custom memory manager has been replaced by the more sophisticated and well-regarded Vulkan Memory Allocator. In contrast to our previous solution, VMA handles the heterogenous memory types of Vulkan devices effectively and deals with low-memory situations more reliably.

Device selection

A proper API for device selection has been added. Previously, the selection was controlled by globally registering a function that selects a device from a list of available devices. This release introduces the IDeviceChooser interface that can be passed to the constructors of any application type. By default, the DeviceChooserAuto implementation is used to select the first dedicated device without requiring any user input.

Raytracing improvements

This release brings new features and improvements for raytracing.

Pool uniforms

The ManagedTracePool now exposes a uniform provider via the Uniforms property, containing the storage buffers of the pool. As a result, users no longer have to manually add these buffers to their own uniform provider. Instead, the provider of the pool can be combined with the provider containing custom uniforms. This is demonstrated in the Raytracing example.

Opacity micromaps

Opacity micromaps are hierarchical data structures that encode opacity information for acceleration structures at a subtriangle level. They can be built from an alpha map and baked into the acceleration structure. During ray traversal, the micromap is queried to determine if the intersection is with opaque or transparent geometry. If the query returns a definitive result, the ray traversal continues accordingly without invoking the any-hit shader. If the query is inconclusive (i.e., the subtriangle covers both transparent and opaque geometry), the any-hit shader is invoked to determine the opacity state by sampling the alpha texture.

Using a micromap may reduce the number of any-hit shader invocations resulting in better performance. Aardvark.OMM is a wrapper around the NVIDIA OMM SDK to bake, save, and load opacity micromaps. The repository comes with an example that demonstrates how to use OMMs with the Aardvark raytracing API.

Note that this feature is not available on all NVIDIA hardware. Use IRuntime.SupportsMicromaps to query for support.

VK_KHR_ray_tracing_position_fetch

This extension allows raytracing shaders to fetch the position of an intersection directly from the acceleration structure, rather than fetching it from a buffer via indices. Note that this extension may not be available on older hardware. Use IRuntime.SupportsPositionFetch to query the availability of this feature. The Raytracing example demonstrates this feature.

VK_NV_ray_tracing_invocation_reorder

Shader invocation or execution reordering is a feature that aims to improve performance by reducing execution and data divergence. This is achieved by splitting the regular TraceRay() call into two separate steps: Ray traversal and execution of the closest-hit or miss shader.

In between those steps the threads may be reordered according to a sort key:

let ho = HitObject()
ho.TraceRay<V3f>(scene, origin, direction) |> ignore // Perform ray traversal and store results in hit object
Thread.Reorder(ho, key, 1)                           // Reorder threads based on hit information and user-provided hint
ho.ExecuteShader(payload)                            // Execute miss or closest-hit shader after reordering

The sort key can be based on:

  1. the hit information encoded in the HitObject. The exact properties from the hit object which are used to reorder the threads are implementation-defined.
  2. a user-provided hint. The second parameter indicates the number of least significant bits of the hint that are taken into account.
  3. a combination of the hit information and a user-provided hint (as in the example above).

This feature is only available on NVIDIA Ada Lovelace GPUs (RTX 4000 series) or newer. You can query for its support with IRuntime.SupportsInvocationReorder.

VK_NV_ray_tracing_validation

This extension identifies potential problems with raytracing applications at driver level that cannot be detected by the validation layers. In order to use it, you have to set an environment variable NV_ALLOW_RAYTRACING_VALIDATION=1 and construct your application with ValidationLayerConfig.RaytracingValidation = true. Note that using this feature may incur a performance overhead.

Debug labels and markers

Some resources (e.g., render tasks, textures, and buffers) now have a Name property that can be used to assign human-readable debug labels. These debug labels are shown in frame captures of programs like NVIDIA Nsight and RenderDoc.

To use this feature, create your application with debug = true or debug = DebugLevel.Normal. For Vulkan you may also want to generate debug information for shaders by passing a DebugConfig with GenerateShaderDebugInfo = true.

Breaking changes

In addition to the removal of the double-as-float feature in FShade shaders, this release comes with a number of breaking changes, which are summarized in this section.

Texture parameters

TextureParams is now a bit field instead of a record with boolean fields. The utilities of the TextureParams module have been removed and have to be replaced:

  • TextureParams.emptyTextureParams.None
  • TextureParams.srgbTextureParams.PreferSrgb
  • TextureParams.compressedTextureParams.Compress
  • TextureParams.mipmappedTextureParams.WantMipMaps
  • TextureParams.mipmappedSrgbTextureParams.WantMipMaps ||| TextureParams.PreferSrgb
  • TextureParams.mipmappedCompressedTextureParams.WantMipMaps ||| TextureParams.Compress

If you use TextureParams to construct a PixTexture2d orFileTexture with mipmapping, consider using the constructor accepting a boolean parameter wantMipMaps instead. This parameter is optional and true by default. For example:

FileTexture("image.jpg", TextureParams.mipmapped)

is equivalent to

FileTexture("image.jpg")

Indirect buffers and draw calls

Support for indirect buffers has been improved, including the possibility to use a subrange of a buffer as source for an indirect buffer. This results in breaking changes to the API defined in the IndirectBuffer module:

  • ofBuffer (indexed: bool) (stride: int) (count: int) (buffer: IBuffer) now accepts an additional parameter offset: uint64 after indexed.
  • ofArray' (indexed: bool) (calls: DrawCallInfo[]) accepts two additional parameters first: int and count: int after indexed. The same change applies to ofList', ofSeq' and ofSeq have been added for convenience.

DrawCalls.Direct has previously stored a list of DrawCallInfo structs. This has been changed to DrawCallInfo[].

Raytracing API changes

The raytracing API also sees various changes that will break existing code:

  • Various record types have been reworked as proper classes for better C# interoperability.
  • RaytracingSceneDescription has been renamed to RaytracingScene.
  • The AccelerationStructureUsage type is now a bit field and has been combined with the allowUpdate parameter of IRuntime.CreateAccelerationStructure(). The new Compact usage indicates that the acceleration structure is to be compacted after building. This reduces memory usage but may incur a runtime overhead for the build. Set DebugConfig.PrintAccelerationStructureCompactionInfo to true to print statistics about the compaction step. This feature is enabled by default for DebugLevel.Normal and above.
  • Similarly, GeometryMode is now also a bit field. Opaque has been renamed to ForceOpaque, and Transparent has been renamed to ForceNoOpaque. DisableOpacityMicromaps and ForceOpacityMicromapsTwoState are new values related to opacity micromaps.

Other breaking changes

The following minor changes may also require your attention:

  • [Sg] Simplified samplerState applicator. Now, takes a SamplerState argument instead of SamplerState option.
  • Changed type of Handle property in resource interfaces to uint64 (https://github.com/aardvark-platform/aardvark.rendering/issues/106)
  • Removed RenderTask.cache and RenderTask.postProcess
  • Removed DefaultSemantic.ColorTexture and DefaultSemantic.DepthTexture
  • Changed return type of TryGetUniform and TryGetAttribute from Option to ValueOption
  • Changed return type of IGeometryPool.TryGetBufferView from Option to ValueOption
  • Replaced CreateTextureAttachment() with GetOutputView() extensions for adaptive textures. These are consistent with the extensions for non-adaptive textures (note parameter order).
  • [Application.Utilities] Removed preventDisposal parameter in ISimpleRenderWindow.Run(). Disposing the window automatically after Run() is problematic if GPU resources are created after creating the window. This commit changes the Run() method to never call Dispose() automatically.
  • [BufferView] Reworked BufferView constructors:
    • Removed offset and stride parameters for ISingleValueBuffer constructor.
    • Added optional elementType parameter for Array and ISingleValueBuffer constructors.
  • Added optional discard parameter for buffer upload and copy in IBufferRuntime. The parameter indicates if the implementation may discard the current content of the destination buffer. This may improve performance in scenarios where the buffer is used for rendering operations and updated every frame. On macOS with OpenGL updating a buffer that is currently in use will lead to implicit synchronization and massive stalls. Discarding the current buffer lets OpenGL allocate new storage and perform the copy operation without waiting for the rendering operation.
  • [ManagedPool] Reworked how attribute data of geometry is represented and handled within the pool. Previously, PooledGeometry has been added as an alternative to AdaptiveGeometry to allow for passing SymbolDict for performance reasons. This is reverted, instead AdaptiveGeometry can now hold any IDictionary.