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;
}
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.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 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");
}
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);
&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");
}
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]);
}
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
Post a Comment