VkPhysicalDevice Feature Chaining Helper

Mason

New Member
Messages
1
Reaction score
1
It was quite a bit of boilerplate to setup switching between different feature requirements.

I'm sure everyone has their own preferences on abstraction shape, but here's a little helper struct that handles linking and such so you can simply pre-define collections of VKPhysicalDeviceFeatures into a VkFeatureChain struct and use where you need it.

Hope it's useful for someone!


The Primary Struct​

C#
/// <summary>
/// VkFeature structs used to define feature chain queries and device feature sets. Useful for declaring a set
/// of feature profiles. Features are linked through their Next pointers.
/// </summary>
private unsafe struct VkFeatureChain
{
    // Every feature struct starts with sType and Next, followed by Bool32 flags
    private const int HeaderSize = 16;

    public VkPhysicalDeviceFeatures2 Core;
    public VkPhysicalDeviceVulkan12Features V12;
    public VkPhysicalDeviceVulkan13Features V13;
    public VkPhysicalDeviceVulkan14Features V14;

    // Constructors are called explicitly so each struct gets its sType
    public VkFeatureChain()
    {
        Core = new();
        V12 = new();
        V13 = new();
        V14 = new();
    }

    /// <summary>
    /// Links the Next pointers in place. The chain must not be copied or moved after linking.
    /// </summary>
    public void Link()
    {
        Core.Next = Unsafe.AsPointer(ref V12);
        V12.Next = Unsafe.AsPointer(ref V13);
        V13.Next = Unsafe.AsPointer(ref V14);
        V14.Next = null;
    }

    /// <summary>
    /// Returns true if every flag set in <paramref name="required"/> is also set in this chain.
    /// </summary>
    public readonly bool Supports(in VkFeatureChain required) =>
        IsSubset(required.Core, Core) &&
        IsSubset(required.V12, V12) &&
        IsSubset(required.V13, V13) &&
        IsSubset(required.V14, V14);

    /// <summary>
    /// Compares the Bool32 flags after the header as raw uints.
    /// </summary>
    private static bool IsSubset<T>(in T required, in T supported) where T : unmanaged
    {
        var req = MemoryMarshal.Cast<byte, uint>(MemoryMarshal.AsBytes(new ReadOnlySpan<T>(in required))[HeaderSize..]);
        var sup = MemoryMarshal.Cast<byte, uint>(MemoryMarshal.AsBytes(new ReadOnlySpan<T>(in supported))[HeaderSize..]);

        for (var i = 0; i < req.Length; i++)
        {
            if ((req[i] & ~sup[i]) != 0)
                return false;
        }

        return true;
    }
}

Usage​

C#
/// <summary>
/// Physical Device Feature Requirements
/// </summary>
private static readonly VkFeatureChain RequiredVkFeatures = new()
{
    V12 = { TimelineSemaphore = true },
    V13 = { DynamicRendering = true, Synchronization2 = true },
};

private static readonly VkFeatureChain VkFeaturesBaseline = new()
{
    V12 = { TimelineSemaphore = true },
    V13 = { DynamicRendering = true, Synchronization2 = true },
};

private static readonly VkFeatureChain VkFeaturesExtended = new()
{
    V13 =
    {
        Maintenance4 = true,
        ShaderDemoteToHelperInvocation = true,
        PipelineCreationCacheControl = true,
        SubgroupSizeControl = true,
        ComputeFullSubgroups = true,
        ShaderZeroInitializeWorkgroupMemory = true,
    },
    V12 = { HostQueryReset = true, SeparateDepthStencilLayouts = true },
};

/// <summary>
/// Create the logical device for the physical device
/// </summary>
private unsafe bool CreateLogicalDevice()
{
    // Query the features this physical device supports
    var supported = new VkFeatureChain();
    supported.Link();
    _physicalDevice!.GetFeatures2(ref supported.Core);

    if (!supported.Supports(RequiredVkFeatures))
    {
        Console.WriteLine("Physical device doesn't meet the feature requirements.");
        return false;
    }

    // Grab the features we want to use and link
    var enabledFeatures = VkFeaturesBaseline;
    enabledFeatures.Link();

    /* ------- Your Extension / Queue logic ------- */

    // Create the logical device
    _logicalDevice = _physicalDevice!.CreateDevice(new Device.CreateInfo
    {
        Extensions = extensions,
        QueueCreateInfos = new ReadOnlySpan<VkDeviceQueueCreateInfo>(in graphicsQueueInfo),
        Next = &enabledFeatures.Core,
    });
        
}
 
Back
Top