Theme Catppuccin
Make yourself at homeCatppuccin

A softer light for long learning sessions.

Appearance

Loading your preference…

Dark palette

Choose a dark palette to try it.

Light uses Latte. Your choices stay in this browser.

Back to your source shelf

Inside the companion

Vulkan · Signal Garden

Query a device and study a clear-color window with explicit frame and presentation lifetimes.

Project files 6

Ask the Vulkan device what it supports

Report physical-device capabilities before choosing a rendering path.

127 lines capabilities.cpp
#include <vulkan/vulkan.h>

#include <algorithm>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>

namespace {
void check(VkResult result, const char* operation) {
    if (result != VK_SUCCESS)
        throw std::runtime_error(std::string(operation) + ": VkResult " +
                                 std::to_string(static_cast<int>(result)));
}

template<class T, class Enumerate>
std::vector<T> enumerate(Enumerate call) {
    // Enumeration can change between count and data calls. Bound retries.
    for (int attempt = 0; attempt != 8; ++attempt) {
        std::uint32_t count = 0;
        check(call(&count, static_cast<T*>(nullptr)), "enumeration count");
        if (count > 65536) throw std::runtime_error("implausible enumeration count");
        if (count == 0) return {};
        std::vector<T> values(count);
        const auto result = call(&count, values.data());
        if (result == VK_INCOMPLETE) continue;
        check(result, "enumeration data");
        values.resize(count);
        return values;
    }
    throw std::runtime_error("enumeration did not stabilize");
}

struct Instance {
    VkInstance value = VK_NULL_HANDLE;
    explicit Instance(const VkInstanceCreateInfo& info) {
        check(vkCreateInstance(&info, nullptr, &value), "vkCreateInstance");
    }
    ~Instance() { vkDestroyInstance(value, nullptr); }
    Instance(const Instance&) = delete;
    Instance& operator=(const Instance&) = delete;
};

void version(std::uint32_t v) {
    std::cout << VK_API_VERSION_MAJOR(v) << '.' << VK_API_VERSION_MINOR(v)
              << '.' << VK_API_VERSION_PATCH(v);
}
} // namespace

int main() try {
    std::uint32_t loader_version = VK_API_VERSION_1_0;
    const auto query_version = reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
        vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion"));
    if (query_version) check(query_version(&loader_version), "loader version");
    std::cout << "Loader API: "; version(loader_version); std::cout << '\n';
    if (loader_version < VK_API_VERSION_1_3)
        throw std::runtime_error("this course probe requires a Vulkan 1.3 loader");

    const auto layers = enumerate<VkLayerProperties>([](auto* n, auto* p) {
        return vkEnumerateInstanceLayerProperties(n, p);
    });
    const char* validation = "VK_LAYER_KHRONOS_validation";
    const bool have_validation = std::any_of(layers.begin(), layers.end(),
        [&](const auto& layer) { return std::strcmp(layer.layerName, validation) == 0; });
    VkApplicationInfo app{};
    app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    app.pApplicationName = "Signal Garden capability report";
    app.apiVersion = VK_API_VERSION_1_3;
    VkInstanceCreateInfo info{};
    info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    info.pApplicationInfo = &app;
    info.enabledLayerCount = have_validation ? 1u : 0u;
    info.ppEnabledLayerNames = have_validation ? &validation : nullptr;
    Instance instance(info);
    std::cout << "Validation layer: " << (have_validation ? "enabled" : "absent") << '\n';
    const auto devices = enumerate<VkPhysicalDevice>([&](auto* n, auto* p) {
        return vkEnumeratePhysicalDevices(instance.value, n, p);
    });
    if (devices.empty()) throw std::runtime_error("no physical devices reported");
    for (const auto device : devices) {
        VkPhysicalDeviceProperties properties{};
        vkGetPhysicalDeviceProperties(device, &properties);
        std::cout << "\nDevice: " << properties.deviceName << "\nAPI: ";
        version(properties.apiVersion);
        std::cout << "\nvendorID: " << properties.vendorID
                  << "\nminUniformBufferOffsetAlignment: "
                  << properties.limits.minUniformBufferOffsetAlignment
                  << "\nnonCoherentAtomSize: " << properties.limits.nonCoherentAtomSize
                  << "\nmaxComputeWorkGroupInvocations: "
                  << properties.limits.maxComputeWorkGroupInvocations << '\n';
        if (properties.apiVersion >= VK_API_VERSION_1_3) {
            VkPhysicalDeviceVulkan13Features features13{};
            features13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
            VkPhysicalDeviceFeatures2 features{};
            features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
            features.pNext = &features13;
            vkGetPhysicalDeviceFeatures2(device, &features);
            std::cout << "synchronization2: " << features13.synchronization2
                      << "\ndynamicRendering: " << features13.dynamicRendering << '\n';
        } else { std::cout << "Course Vulkan 1.3 feature query skipped: older device API\n"; }
        std::uint32_t queue_count = 0;
        vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_count, nullptr);
        std::vector<VkQueueFamilyProperties> queues(queue_count);
        vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_count, queues.data());
        queues.resize(queue_count);
        for (std::size_t i = 0; i < queues.size(); ++i)
            std::cout << "queue family " << i << ": count=" << queues[i].queueCount
                      << " flags=" << queues[i].queueFlags << '\n';
        const auto extensions = enumerate<VkExtensionProperties>([&](auto* n, auto* p) {
            return vkEnumerateDeviceExtensionProperties(device, nullptr, n, p);
        });
        const bool swapchain = std::any_of(extensions.begin(), extensions.end(),
            [](const auto& extension) {
                return std::strcmp(extension.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0;
            });
        std::cout << "VK_KHR_swapchain: " << swapchain << '\n';
    }
    std::cout << "\nNo surface created: presentation support is NOT established.\n"
                 "No logical device created: queried features are NOT enabled.\n";
    return 0;
} catch (const std::exception& error) {
    std::cerr << "Signal Garden: " << error.what() << '\n';
    return 1;
}

This is the file included with the course. Copy and download give you its complete source.

Taking your work offline? Download all course companions