summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/renderer/backends/backend_vulkan.c176
-rw-r--r--src/renderer/backends/vulkan_helpers.h149
-rw-r--r--src/renderer/render.c6
3 files changed, 325 insertions, 6 deletions
diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c
index c21dfc2..481792a 100644
--- a/src/renderer/backends/backend_vulkan.c
+++ b/src/renderer/backends/backend_vulkan.c
@@ -1,5 +1,9 @@
-#include "str.h"
#define CEL_PLATFORM_LINUX
+#include <assert.h>
+#include <vulkan/vk_platform.h>
+#include <vulkan/vulkan.h>
+#include <vulkan/vulkan_core.h>
+#include "str.h"
#include "darray.h"
#include "defines.h"
@@ -17,12 +21,29 @@
#include <glad/glad.h>
#include <glfw3.h>
-#include <vulkan/vulkan.h>
-#include <vulkan/vulkan_core.h>
+
+typedef struct vulkan_device {
+ VkPhysicalDevice physical_device;
+ VkDevice logical_device;
+ vulkan_swapchain_support_info swapchain_support;
+ i32 graphics_queue_index;
+ i32 present_queue_index;
+ i32 compute_queue_index;
+ i32 transfer_queue_index;
+ VkPhysicalDeviceProperties properties;
+ VkPhysicalDeviceFeatures features;
+ VkPhysicalDeviceMemoryProperties memory;
+} vulkan_device;
typedef struct vulkan_context {
VkInstance instance;
VkAllocationCallbacks* allocator;
+ VkSurfaceKHR surface;
+ vulkan_device device;
+
+#if defined(DEBUG)
+ VkDebugUtilsMessengerEXT vk_debugger;
+#endif
} vulkan_context;
static vulkan_context context;
@@ -33,6 +54,85 @@ typedef struct vulkan_state {
KITC_DECL_TYPED_ARRAY(VkLayerProperties)
+bool select_physical_device(vulkan_context* ctx) {
+ u32 physical_device_count = 0;
+ VK_CHECK(vkEnumeratePhysicalDevices(ctx->instance, &physical_device_count, 0));
+ if (physical_device_count == 0) {
+ FATAL("No devices that support vulkan were found");
+ return false;
+ }
+ TRACE("Number of devices found %d", physical_device_count);
+
+ VkPhysicalDevice physical_devices[physical_device_count];
+ VK_CHECK(vkEnumeratePhysicalDevices(ctx->instance, &physical_device_count, physical_devices));
+
+ for (u32 i = 0; i < physical_device_count; i++) {
+ VkPhysicalDeviceProperties properties;
+ vkGetPhysicalDeviceProperties(physical_devices[i], &properties);
+
+ VkPhysicalDeviceFeatures features;
+ vkGetPhysicalDeviceFeatures(physical_devices[i], &features);
+
+ VkPhysicalDeviceMemoryProperties memory;
+ vkGetPhysicalDeviceMemoryProperties(physical_devices[i], &memory);
+
+ vulkan_physical_device_requirements requirements = {};
+ requirements.graphics = true;
+ requirements.present = true;
+ requirements.compute = true;
+ requirements.transfer = true;
+
+ requirements.sampler_anistropy = true;
+ requirements.discrete_gpu = true;
+ requirements.device_ext_names[0] = str8lit(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+ requirements.device_ext_name_count = 1;
+
+ vulkan_physical_device_queue_family_info queue_info = {};
+
+ bool result = physical_device_meets_requirements(physical_devices[i], ctx->surface, &properties,
+ &features, &requirements, &queue_info,
+ &ctx->device.swapchain_support);
+
+ if (result) {
+ INFO("GPU Driver version: %d.%d.%d", VK_VERSION_MAJOR(properties.driverVersion),
+ VK_VERSION_MINOR(properties.driverVersion), VK_VERSION_PATCH(properties.driverVersion));
+
+ INFO("Vulkan API version: %d.%d.%d", VK_VERSION_MAJOR(properties.apiVersion),
+ VK_VERSION_MINOR(properties.apiVersion), VK_VERSION_PATCH(properties.apiVersion));
+
+ // TODO: print gpu memory information -
+ // https://youtu.be/6Kj3O2Ov1RU?si=pXfP5NvXXcXjJsrG&t=2439
+
+ ctx->device.physical_device = physical_devices[i];
+ ctx->device.graphics_queue_index = queue_info.graphics_family_index;
+ ctx->device.present_queue_index = queue_info.present_family_index;
+ ctx->device.compute_queue_index = queue_info.compute_family_index;
+ ctx->device.transfer_queue_index = queue_info.transfer_family_index;
+ ctx->device.properties = properties;
+ ctx->device.features = features;
+ ctx->device.memory = memory;
+ break;
+ }
+ }
+
+ if (!ctx->device.physical_device) {
+ ERROR("No suitable physical devices were found :(");
+ return false;
+ }
+
+ INFO("Physical device selected: %s\n", ctx->device.properties.deviceName);
+ return true;
+}
+
+bool vulkan_device_create(vulkan_context* ctx) {
+ if (!select_physical_device(ctx)) {
+ return false;
+ }
+
+ return true;
+}
+void vulkan_device_destroy(vulkan_context* ctx) {}
+
bool gfx_backend_init(renderer* ren) {
INFO("loading Vulkan backend");
@@ -111,11 +211,58 @@ bool gfx_backend_init(renderer* ren) {
return false;
}
+ // Debugger
+#if defined(DEBUG)
+ DEBUG("Creating Vulkan debugger")
+ u32 log_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
+ VkDebugUtilsMessengerCreateInfoEXT debug_create_info = {
+ VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT
+ };
+ debug_create_info.messageSeverity = log_severity;
+ debug_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
+ debug_create_info.pfnUserCallback = vk_debug_callback;
+
+ PFN_vkCreateDebugUtilsMessengerEXT func =
+ (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(context.instance,
+ "vkCreateDebugUtilsMessengerEXT");
+ assert(func);
+ VK_CHECK(func(context.instance, &debug_create_info, context.allocator, &context.vk_debugger));
+ DEBUG("Vulkan debugger created");
+
+#endif
+
+ // Surface creation
+ DEBUG("Create SurfaceKHR")
+ VkSurfaceKHR surface;
+ VK_CHECK(glfwCreateWindowSurface(context.instance, ren->window, NULL, &surface));
+ context.surface = surface;
+ DEBUG("Vulkan surface created")
+
+ // Device creation
+ if (!vulkan_device_create(&context)) {
+ FATAL("device creation failed");
+ return false;
+ }
+
INFO("Vulkan renderer initialisation succeeded");
return true;
}
-void gfx_backend_shutdown(renderer* ren) {}
+void gfx_backend_shutdown(renderer* ren) {
+ DEBUG("Destroying Vulkan debugger");
+ if (context.vk_debugger) {
+ PFN_vkDestroyDebugUtilsMessengerEXT func =
+ (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
+ context.instance, "vkDestroyDebugUtilsMessengerEXT");
+ func(context.instance, context.vk_debugger, context.allocator);
+ }
+
+ DEBUG("Destroying Vulkan instance...");
+ vkDestroyInstance(context.instance, context.allocator);
+}
void clear_screen(vec3 colour) {}
@@ -131,4 +278,25 @@ void uniform_f32(u32 program_id, const char* uniform_name, f32 value) {}
void uniform_i32(u32 program_id, const char* uniform_name, i32 value) {}
void uniform_mat4f(u32 program_id, const char* uniform_name, mat4* value) {}
+VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback(
+ VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags,
+ const VkDebugUtilsMessengerCallbackDataEXT callback_data, void* user_data) {
+ switch (severity) {
+ default:
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
+ ERROR(callback_data.pMessage);
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
+ WARN(callback_data.pMessage);
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
+ INFO(callback_data.pMessage);
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
+ TRACE(callback_data.pMessage);
+ break;
+ }
+ return VK_FALSE;
+}
+
#endif \ No newline at end of file
diff --git a/src/renderer/backends/vulkan_helpers.h b/src/renderer/backends/vulkan_helpers.h
index 058ea91..8f4d48a 100644
--- a/src/renderer/backends/vulkan_helpers.h
+++ b/src/renderer/backends/vulkan_helpers.h
@@ -2,9 +2,14 @@
#include <assert.h>
#include <vulkan/vulkan.h>
+#include <vulkan/vulkan_core.h>
#include "darray.h"
#include "defines.h"
+#include "log.h"
+#include "str.h"
+
+#define VULKAN_PHYS_DEVICE_MAX_EXTENSION_NAMES 36
DECL_TYPED_ARRAY(const char*, cstr)
@@ -16,4 +21,146 @@ static void plat_get_required_extension_names(cstr_darray* extensions) {
// TODO(omni): port to using internal assert functions
#define VK_CHECK(vulkan_expr) \
- { assert(vulkan_expr == VK_SUCCESS); } \ No newline at end of file
+ { assert(vulkan_expr == VK_SUCCESS); }
+
+// TODO: typedef struct vk_debugger {} vk_debugger;
+
+typedef struct vulkan_physical_device_requirements {
+ bool graphics;
+ bool present;
+ bool compute;
+ bool transfer;
+ str8 device_ext_names[VULKAN_PHYS_DEVICE_MAX_EXTENSION_NAMES];
+ size_t device_ext_name_count;
+ bool sampler_anistropy;
+ bool discrete_gpu;
+} vulkan_physical_device_requirements;
+
+typedef struct vulkan_physical_device_queue_family_info {
+ u32 graphics_family_index;
+ u32 present_family_index;
+ u32 compute_family_index;
+ u32 transfer_family_index;
+} vulkan_physical_device_queue_family_info;
+
+#define VULKAN_MAX_DEFAULT 32
+
+typedef struct vulkan_swapchain_support_info {
+ VkSurfaceCapabilitiesKHR capabilities;
+ VkSurfaceFormatKHR formats[VULKAN_MAX_DEFAULT];
+ u32 format_count;
+ VkPresentModeKHR present_modes[VULKAN_MAX_DEFAULT];
+ u32 mode_count;
+} vulkan_swapchain_support_info;
+
+VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback(
+ VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags,
+ const VkDebugUtilsMessengerCallbackDataEXT callback_data, void* user_data);
+
+void vulkan_device_query_swapchain_support(VkPhysicalDevice device, VkSurfaceKHR surface,
+ vulkan_swapchain_support_info* out_support_info) {
+ // TODO: add VK_CHECK to these calls!
+
+ // Surface capabilities
+ vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &out_support_info->capabilities);
+
+ // Surface formats
+ vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &out_support_info->format_count,
+ 0); // Get number of formats
+ if (out_support_info->format_count > 0) {
+ vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &out_support_info->format_count,
+ out_support_info->formats);
+ }
+
+ // Present Modes
+ vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &out_support_info->mode_count,
+ 0); // Get number of formats
+ if (out_support_info->mode_count > 0) {
+ vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &out_support_info->mode_count,
+ out_support_info->present_modes);
+ }
+}
+
+static bool physical_device_meets_requirements(
+ VkPhysicalDevice device, VkSurfaceKHR surface, const VkPhysicalDeviceProperties* properties,
+ const VkPhysicalDeviceFeatures* features,
+ const vulkan_physical_device_requirements* requirements,
+ vulkan_physical_device_queue_family_info* out_queue_info,
+ vulkan_swapchain_support_info* out_swapchain_support) {
+ // TODO: pass in an arena
+
+ out_queue_info->graphics_family_index = -1;
+ out_queue_info->present_family_index = -1;
+ out_queue_info->compute_family_index = -1;
+ out_queue_info->transfer_family_index = -1;
+
+ if (requirements->discrete_gpu) {
+ if (properties->deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
+ TRACE("Device is not a physical GPU. Skipping.");
+ return false;
+ }
+ }
+
+ u32 queue_family_count = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, 0);
+ VkQueueFamilyProperties queue_families[queue_family_count];
+ vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families);
+
+ INFO("Graphics | Present | Compute | Transfer | Name");
+ u8 min_transfer_score = 255;
+ for (u32 i = 0; i < queue_family_count; i++) {
+ u8 current_transfer_score = 0;
+
+ // Graphics queue
+ if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ out_queue_info->graphics_family_index = i;
+ current_transfer_score++;
+ }
+
+ // Compute queue
+ if (queue_families[i].queueFlags & VK_QUEUE_COMPUTE_BIT) {
+ out_queue_info->compute_family_index = i;
+ current_transfer_score++;
+ }
+
+ // Transfer queue
+ if (queue_families[i].queueFlags & VK_QUEUE_TRANSFER_BIT) {
+ // always take the lowest score transfer index
+ if (current_transfer_score <= min_transfer_score) {
+ min_transfer_score = current_transfer_score;
+ out_queue_info->transfer_family_index = i;
+ }
+ }
+
+ // Present Queue
+ VkBool32 supports_present = VK_FALSE;
+ vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &supports_present);
+ if (supports_present) {
+ out_queue_info->present_family_index = i;
+ }
+ }
+
+ INFO(" %d | %d | %d | %d | %s",
+ out_queue_info->graphics_family_index != -1, out_queue_info->present_family_index != -1,
+ out_queue_info->compute_family_index != -1, out_queue_info->transfer_family_index != -1,
+ properties->deviceName);
+ TRACE("Graphics Family queue index: %d", out_queue_info->graphics_family_index);
+ TRACE("Present Family queue index: %d", out_queue_info->present_family_index);
+ TRACE("Compute Family queue index: %d", out_queue_info->compute_family_index);
+ TRACE("Transfer Family queue index: %d", out_queue_info->transfer_family_index);
+
+ if ((!requirements->graphics ||
+ (requirements->graphics && out_queue_info->graphics_family_index != -1))) {
+ INFO("Physical device meets our requirements! Proceed.");
+
+ vulkan_device_query_swapchain_support(
+ device, surface, out_swapchain_support
+
+ // TODO: error handling i.e. format count = 0 or present mode = 0
+
+ );
+ return true;
+ }
+
+ return false;
+} \ No newline at end of file
diff --git a/src/renderer/render.c b/src/renderer/render.c
index 8e67fa6..45762d4 100644
--- a/src/renderer/render.c
+++ b/src/renderer/render.c
@@ -28,11 +28,14 @@ bool renderer_init(renderer* ren) {
// NOTE: all platforms use GLFW at the moment but thats subject to change
glfwInit();
- DEBUG("init graphics api (OpenGL) backend");
+#if defined(CEL_REND_BACKEND_OPENGL)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+#elif defined(CEL_REND_BACKEND_VULKAN)
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+#endif
// glfw window creation
GLFWwindow* window = glfwCreateWindow(ren->config.scr_width, ren->config.scr_height,
@@ -46,6 +49,7 @@ bool renderer_init(renderer* ren) {
glfwMakeContextCurrent(ren->window);
+ DEBUG("init graphics api backend");
if (!gfx_backend_init(ren)) {
FATAL("Couldnt load graphics api backend");
return false;