Overview
Table of Contents
- Overview
- Table of Contents
- Background
- Managed Chains (main article here)
- Structure Chaining (main article here)
- Raw Chaining (main article here)
- Vulkan Chaining
- Managed Chains (recommended)
- Structure Chaining
- Raw Chaining
Background
Several of the APIs exposed by Silk.NET pass data using
a Singly Linked List of structures. We refer to these lists as 'chains'.
This is achieved by have a standardised pointer (void*
) that can be used to point to the next item, with
the nullptr
(i.e. 0
) used to indicate the end of the list.
However, pointers are particularly problematic in managed programming as the runtime is often free to move objects on
the heap around during Garbage Collection. As such, C# requires you to explicitly mark code that works with pointers
as unsafe
, to let the compiler know that you are aware of the dangers. The Runtime usually manages pointers for you
under the hood, repointing them if it moves any objects around and, as such, many developers do not use them frequently
enough to be aware of the pitfalls, and the potential bugs that can occur can be subtle and hard to trace.
Silk.NET always exposes the lowest level API possible, allowing you to directly manipulate these chains via directly setting the pointers as appropriate. However, it also exposes 2 new methods of maintaining these chains more easily and safely.
Currently, these new methods are only available in the Silk.NET.Vulkan
package when using the Vulkan API, however, if
popular, they will be considered for more of the exposed APIs in future.
Chaining methodologies
There are 3, chaining methodologies supported by Silk.Net.
Managed Chaining
This is the recommended approach. Chain
is a lightweight C# object that manages the memory of chain, and ensure
internal pointers remain valid throughout it's lifetime. For example:
using (var features2 = Chain.Create
(
default(PhysicalDeviceFeatures2),
default(PhysicalDeviceDescriptorIndexingFeatures),
default(PhysicalDeviceAccelerationStructureFeaturesKHR)
)) {
vk.GetPhysicalDeviceFeatures2(device, features2);
var depthBounds = features2.Head.Features.DepthBounds;
var runtimeDescriptorArray = features2.Item1.RuntimeDescriptorArray;
var accelerationStructure = features2.Item2.AccelerationStructure;
}
Structure Chaining
If you are creating a chain once, and then throwing it away, it can be done so safely, so long as the structures you
create never leave the stack. Silk.NET provides fluent extension methods that allow you to manipulate IChainable
structures directly, performing the pointer logic for you, and providing compile-time type validation. Although,
avoiding the heap entirely, there are some scenarios where this may still be slower than
using Managed Chains, and so this approach should only be considered when looking to optimise hot
paths. For example:
PhysicalDeviceFeatures2
.Chain(out var features2)
.AddNext(out PhysicalDeviceDescriptorIndexingFeatures indexingFeatures)
.AddNext(out PhysicalDeviceAccelerationStructureFeaturesKHR accelerationStructureFeaturesKhr);
vk.GetPhysicalDeviceFeatures2(device, &features2);
var depthBounds = features2.Features.DepthBounds;
var runtimeDescriptorArray = indexingFeatures.RuntimeDescriptorArray;
var accelerationStructure = accelerationStructureFeaturesKhr.AccelerationStructure;
Raw Chaining
Where you are looking to optimise a hot path, and every cycle counts, direct modification of chains is always available. Again though, you should benchmark against the above two methodologies to ensure you are gaining the benefit you hope for. For example:
var accelerationStructureFeaturesKhr = new PhysicalDeviceAccelerationStructureFeaturesKHR
{SType = StructureType.PhysicalDeviceAccelerationStructureFeaturesKhr};
var indexingFeatures = new PhysicalDeviceDescriptorIndexingFeatures
{
SType = StructureType.PhysicalDeviceDescriptorIndexingFeatures,
PNext = &accelerationStructureFeaturesKhr
};
var features2 = new PhysicalDeviceFeatures2
{
SType = StructureType.PhysicalDeviceFeatures2,
PNext = &indexingFeatures
};
vk.GetPhysicalDeviceFeatures2(device, &features2);
var depthBounds = features2.Features.DepthBounds;
var runtimeDescriptorArray = indexingFeatures.RuntimeDescriptorArray;
var accelerationStructure = accelerationStructureFeaturesKhr.AccelerationStructure;