amber and luci learning vulkan contains notes and decently commented code (hopefully)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

258 lines
7.9 KiB

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <vulkan/vulkan.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#define WIDTH 2560
#define HEIGHT 1440
#define VALIDATE true
#define ARRSIZE(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
const char* validation_layers[] = {
"VK_LAYER_KHRONOS_validation",
};
typedef struct ApplicationState {
GLFWwindow* window;
//information about the connection between application and vulkan itself
VkInstance instance;
//the physical gpu
VkPhysicalDevice physical_device;
//the logical device including the features we wish to use
VkDevice device;
//the queue for draw commands
VkQueue graphics_queue;
} ApplicationState;
typedef struct QueueFamilyIndices {
bool graphics_present;
uint32_t graphics_queue;
} QueueFamilyIndices;
void create_window(ApplicationState* state) {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
state->window = glfwCreateWindow(WIDTH, HEIGHT, "vulkan learning example", NULL, NULL);
}
//check if all of our validation layers are supported
bool check_validation_layer_support() {
uint32_t layer_count;
vkEnumerateInstanceLayerProperties(&layer_count, NULL);
VkLayerProperties* available_layers = malloc(sizeof(VkLayerProperties) * layer_count);
vkEnumerateInstanceLayerProperties(&layer_count, available_layers);
printf("supported validation layers:\n");
for (int i = 0; i < layer_count; i++) {
printf("\t%s\n", available_layers[i].layerName);
}
for (int i = 0; i < ARRSIZE(validation_layers); i++) {
bool found = false;
for (int j = 0; j < layer_count; j++) {
if (!strcmp(available_layers[j].layerName, validation_layers[i])) {
found = true;
}
}
if (!found) {
return false;
}
}
free (available_layers);
return true;
}
//we first create an instance, filling it in with our application information.
void create_instance(ApplicationState* state) {
if (VALIDATE && !check_validation_layer_support()) {
printf("Requested validation layers but they're not supported");
exit(1);
}
VkApplicationInfo appinfo = {0};
appinfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appinfo.pApplicationName = "Vulkan learning triangle example";
appinfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appinfo.pEngineName = "No Engine";
appinfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appinfo.apiVersion = VK_API_VERSION_1_0;
VkInstanceCreateInfo create_info = {0};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.pApplicationInfo = &appinfo;
//now let's require the global extensions that are required for GLFW to work
uint32_t glfw_extension_num = 0;
const char** glfw_extensions;
glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_num);
create_info.enabledExtensionCount = glfw_extension_num;
create_info.ppEnabledExtensionNames = glfw_extensions;
if (VALIDATE) {
create_info.enabledLayerCount = ARRSIZE(validation_layers);
create_info.ppEnabledLayerNames = validation_layers;
} else {
create_info.enabledLayerCount = 0;
}
//now we actually create the instance
if (vkCreateInstance(&create_info, NULL, &state->instance) != VK_SUCCESS) {
printf("error creating instance\n");
exit(1);
}
printf("Instance successfully created\n");
//let's just list all supported vulkan instance extensions
uint32_t extension_num = 0;
vkEnumerateInstanceExtensionProperties(NULL, &extension_num, NULL);
VkExtensionProperties* extensions = malloc(sizeof(VkExtensionProperties) * extension_num);
vkEnumerateInstanceExtensionProperties(NULL, &extension_num, extensions);
printf("instance extensions:\n");
for (int i = 0; i < extension_num; i++) {
printf("\t%s\n", extensions[i].extensionName);
}
free(extensions);
}
QueueFamilyIndices find_queue_families(VkPhysicalDevice device) {
struct QueueFamilyIndices ret = {0};
uint32_t queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, NULL);
VkQueueFamilyProperties* queue_fam_props = malloc(sizeof(VkQueueFamilyProperties) * queue_family_count);
vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_fam_props);
for (int i = 0; i < queue_family_count; i++) {
if (queue_fam_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
ret.graphics_present = true;
ret.graphics_queue = i;
}
}
free(queue_fam_props);
return ret;
}
void pick_physical_device(ApplicationState* state) {
uint32_t device_count = 0;
vkEnumeratePhysicalDevices(state->instance, &device_count, NULL);
if (device_count == 0) {
printf("vulkan is not supported\n");
exit(1);
}
VkPhysicalDevice* devices = malloc(sizeof(VkPhysicalDevice) * device_count);
vkEnumeratePhysicalDevices(state->instance, &device_count, devices);
//now let's check if any device is suitable for our usage
int i;
for (i = 0; i < device_count; i++) {
VkPhysicalDevice device = devices[i];
VkPhysicalDeviceProperties device_props;
vkGetPhysicalDeviceProperties(device, &device_props);
VkPhysicalDeviceFeatures device_features;
vkGetPhysicalDeviceFeatures(device, &device_features);
QueueFamilyIndices families = find_queue_families(device);
//for now we only require that a graphics queue is present
if (families.graphics_present) {
printf("graphics queue at %x\n", families.graphics_queue);
break;
}
}
state->physical_device = devices[i];
free(devices);
}
void create_logical_device(ApplicationState* state) {
//we already ensured that a graphics queue exists
QueueFamilyIndices indices = find_queue_families(state->physical_device);
//multiple of these will be needed once we require other kinds of queues
VkDeviceQueueCreateInfo queue_create_info = {0};
queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info.queueFamilyIndex = indices.graphics_queue;
queue_create_info.queueCount = 1;
float prio = 1.0f;
queue_create_info.pQueuePriorities = &prio;
//for now we won't use any features
VkPhysicalDeviceFeatures device_features = {0};
//now create a logical device knowing which features and queues we need
VkDeviceCreateInfo create_info = {0};
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
create_info.pQueueCreateInfos = &queue_create_info;
create_info.queueCreateInfoCount = 1;
create_info.pEnabledFeatures = &device_features;
create_info.enabledExtensionCount = 0;
if (VALIDATE) {
create_info.enabledLayerCount = ARRSIZE(validation_layers);
create_info.ppEnabledLayerNames = validation_layers;
} else {
create_info.enabledLayerCount = 0;;
}
if (vkCreateDevice(state->physical_device, &create_info, NULL, &state->device) != VK_SUCCESS) {
printf("Error creating logical device\n");
exit(1);
}
//get a handle to the queue
vkGetDeviceQueue(state->device, indices.graphics_queue, 0, &state->graphics_queue);
printf("Logical device created\n");
}
void init_vulkan(ApplicationState* state) {
create_instance(state);
pick_physical_device(state);
create_logical_device(state);
}
void main_loop(ApplicationState* state) {
while(!glfwWindowShouldClose(state->window)) {
glfwPollEvents();
}
}
void terminate(ApplicationState* state) {
vkDestroyDevice(state->device, NULL);
vkDestroyInstance(state->instance, NULL);
glfwDestroyWindow(state->window);
glfwTerminate();
}
int main() {
ApplicationState state = {0};
create_window(&state);
init_vulkan(&state);
main_loop(&state);
terminate(&state);
return 0;
}