Vulkan Tutorial in C - 004 - Initialization Part 2

Finishing up win32_init_vulkan

To recap, we're in the process of implementing the following function:
VulkanContext
win32_init_vulkan(HINSTANCE instance, s32 windowX, s32 windowY, u32 windowWidth,
                  u32 windowHeight, char *windowTitle)
{
    VulkanContext vk = {0};

    // First 5 steps done in the previous post

    // Pick a physical device and the graphicsAndPresent queue family
    // Create logical device
    // Get graphicsAndPresentQueue from device
    // Create swapchain
    // Get swapchain images and create their views

    return vk;
}

Pick a physical device and the graphicsAndPresent queue family

u32 deviceCount = 0;
vkEnumeratePhysicalDevices(vk.instance, &deviceCount, NULL);
assert(deviceCount <= 8); // Ensure there are no more than 8 devices

VkPhysicalDevice devices[8] = {NULL};
vkEnumeratePhysicalDevices(vk.instance, &deviceCount, devices);

// Choose the first available device as a fallback
vk.physicalDevice = devices[0];

// Search for a dedicated GPU (discrete GPU)
for (u32 i = 0; i < deviceCount; i++)
{
    VkPhysicalDeviceProperties props;
    vkGetPhysicalDeviceProperties(devices[i], &props);
    
    // If the device is a dedicated (discrete) GPU, prefer it
    if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
    {
        // Choose it as the physical device and break the loop
        vk.physicalDevice = devices[i];
        break;
    }
}
assert(vk.physicalDevice); // Ensure a physical device has been selected

// Query the queue family properties for the chosen physical device
u32 queueFamilyPropertyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice,
                                         &queueFamilyPropertyCount, NULL);
// Ensure there are no more than 3 queue families
assert(queueFamilyPropertyCount <= 3);

VkQueueFamilyProperties queueFamilyProperties[3] = {0};
vkGetPhysicalDeviceQueueFamilyProperties(vk.physicalDevice,
                                         &queueFamilyPropertyCount,
                                         queueFamilyProperties);

// Assume first queue family supports Graphics and Present capabilities
u32 queueFamilyIndex = 0;

// Ensure the queue family supports graphics
assert(queueFamilyProperties[queueFamilyIndex].queueFlags
       & VK_QUEUE_GRAPHICS_BIT);

VkBool32 presentSupport = VK_FALSE;
vkGetPhysicalDeviceSurfaceSupportKHR(vk.physicalDevice, queueFamilyIndex,
                                     vk.surface,
                                     &presentSupport);
assert(presentSupport); // Ensure present support is available

// Store the queue family index that supports both graphics and present
vk.graphicsAndPresentQueueFamily = queueFamilyIndex;
I won't be looping through queue families, assigning scores to GPUs based on their features, or anything fancy.
I simply assume the first queue family will support graphics and present capabilities and assert that in the code. In my setup that works. If it doesn't for you, let me know how you handled it ;)

Queue Priority

This concept is confusing and I don't think I need it because I only have one command buffer--so I'm just ignoring it.

Create logical device

f32 queuePriorities[] = { 1.0f };
VkDeviceQueueCreateInfo queueCreateInfo =
{
    VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
    NULL,
    0,
    vk.graphicsAndPresentQueueFamily,
    array_count(queuePriorities),
    queuePriorities
};

VkDeviceQueueCreateInfo queueCreateInfos[] = {queueCreateInfo};

// Enable required device extensions (swapchain)
char *deviceExtensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};

VkDeviceCreateInfo deviceCreateInfo =
{
    VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
    NULL,
    0,
    array_count(queueCreateInfos),
    queueCreateInfos,
    0, // enabledLayerCount deprecated
    NULL, // ppEnabledLayerNames deprecated
    array_count(deviceExtensions),
    deviceExtensions,
    NULL // pEnabledFeatures
};

// Create the actual logical device finally
if (vkCreateDevice(vk.physicalDevice, &deviceCreateInfo, NULL,
                   &vk.device) != VK_SUCCESS)
{
    assert(!"Failed to create logical device");
}

Get graphicsAndPresentQueue from device

vkGetDeviceQueue(vk.device, vk.graphicsAndPresentQueueFamily, 0,
                 &vk.graphicsAndPresentQueue);
assert(vk.graphicsAndPresentQueue);

Create swapchain

// Query surface capabilities
VkSurfaceCapabilitiesKHR surfaceCapabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk.physicalDevice, vk.surface,
                                          &surfaceCapabilities);

// Save the swapchain image format and extents
vk.swapchainImageFormat = VK_FORMAT_B8G8R8A8_SRGB;
vk.swapchainExtents = surfaceCapabilities.currentExtent;

VkSwapchainCreateInfoKHR swapchainCreateInfo =
{
    VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
    NULL,
    0,
    vk.surface,
    array_count(vk.swapchainImages), // minImageCount (2)
    vk.swapchainImageFormat,
    VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, // imageColorSpace
    vk.swapchainExtents, // imageExtent
    1, // imageArrayLayers
    VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, // imageUsage
    VK_SHARING_MODE_EXCLUSIVE,
    0, // queueFamilyIndexCount
    NULL, // pQueueFamilyIndices
    surfaceCapabilities.currentTransform, // preTransform
    VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
    VK_PRESENT_MODE_FIFO_KHR,
    VK_TRUE, // clipped
    NULL // oldSwapchain
};

if (vkCreateSwapchainKHR(vk.device, &swapchainCreateInfo, NULL,
                         &vk.swapchain) != VK_SUCCESS)
{
    assert(!"Failed to create the swapchain");
}

Get swapchain images and create their views

u32 imageCount = 0;
vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount, NULL);
assert(imageCount == array_count(vk.swapchainImages));

vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &imageCount,
                        vk.swapchainImages);

// For each swapchain image
for (u32 i = 0; i < imageCount; i++)
{
    assert(vk.swapchainImages[i]);
    
    VkComponentMapping swizzle =
    {
        VK_COMPONENT_SWIZZLE_IDENTITY,
        VK_COMPONENT_SWIZZLE_IDENTITY,
        VK_COMPONENT_SWIZZLE_IDENTITY,
        VK_COMPONENT_SWIZZLE_IDENTITY
    };
    
    VkImageSubresourceRange subRange =
    {
        VK_IMAGE_ASPECT_COLOR_BIT,
        0, // baseMipLevel
        1, // levelCount
        0, // baseArrayLayer
        1 // layerCount
    };
    
    VkImageViewCreateInfo viewInfo =
    {
        VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
        NULL,
        0,
        vk.swapchainImages[i],
        VK_IMAGE_VIEW_TYPE_2D,
        vk.swapchainImageFormat,
        swizzle,
        subRange
    };
    
    // Create the image view
    vkCreateImageView(vk.device, &viewInfo, NULL,
                      &vk.swapchainImageViews[i]);
    
    assert(vk.swapchainImageViews[i]);
}

Initialization Done! Well, kinda...

Phew! We’ve finally wrapped up the win32_init_vulkan function. Though, there’s still a ton of app-specific initialization to handle, which I decided to leave for later.

In the next post, we’ll begin to initialize the app-specific Vulkan objects.

Next

Comments

Popular Posts