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
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; |
|
}
|
|
|