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.
#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
