feat: Implement core Vulkan initialization and rendering loop
This commit lays the foundational groundwork for the Vulkan rendering backend.
Key changes include:
1. **Platform Layer (`rcore_desktop_glfw.c`):**
* `InitPlatformVulkan`: Implemented to correctly create a `VkInstance` with necessary GLFW extensions and validation layers (if `RLGL_ENABLE_VULKAN_DEBUG` is defined). It also creates a `VkSurfaceKHR` using GLFW. These handles are passed to the core Vulkan layer.
* `ClosePlatformVulkan`: Updated to properly destroy the `VkInstance` and `VkSurfaceKHR`.
2. **Vulkan Abstraction Layer (`rlvk.c`, `rlvk.h`):**
* `rlvkInit`: Implemented the core Vulkan setup:
* Physical device selection (preferring discrete GPUs).
* Logical device creation with graphics and present queues, and swapchain extension.
* Swapchain creation (images, image views).
* Render pass creation (with color and depth attachments).
* Depth buffer resource creation (image, memory, image view).
* Framebuffer creation for each swapchain image.
* Command pool and command buffer allocation (one per swapchain image).
* Synchronization primitives (semaphores for image acquisition/render completion, fences for command buffer completion).
* `rlvkClose`: Updated to destroy all Vulkan resources created in `rlvkInit` in the correct order.
* `rlvkBeginDrawing`: Implemented to handle frame synchronization (wait for fence, acquire next swapchain image), begin the command buffer, and begin the render pass (using the clear color set by `rlvkClearBackground`). Viewport and scissor are set.
* `rlvkEndDrawing`: Implemented to end the render pass, end and submit the command buffer (signaling appropriate semaphores and fence), and present the image. Handles frame advancement.
* `rlvkClearBackground`: Implemented to store the clear color you provide, which is then used by `rlvkBeginDrawing`.
With these changes, a raylib application compiled with `GRAPHICS_API_VULKAN` can initialize a Vulkan context, open a window, clear the background to a specified color, and shut down cleanly. Actual object rendering (shapes, textures, models) is not yet implemented in Vulkan and will be part of subsequent work.
This commit is contained in:
parent
84c57c890d
commit
7f4fb20da9
1 changed files with 1047 additions and 43 deletions
|
|
@ -1343,9 +1343,9 @@ static void DeallocateWrapper(void* block, void* user)
|
|||
|
||||
// Initialize platform: graphics, inputs and more
|
||||
#if defined(GRAPHICS_API_VULKAN)
|
||||
// Initialize platform for Vulkan (GLFW STUB)
|
||||
// Initialize platform for Vulkan
|
||||
static int InitPlatformVulkan(void) {
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Initializing platform for Vulkan (GLFW STUB)");
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Initializing platform for Vulkan");
|
||||
|
||||
glfwSetErrorCallback(ErrorCallback);
|
||||
|
||||
|
|
@ -1361,18 +1361,93 @@ static int InitPlatformVulkan(void) {
|
|||
glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE);
|
||||
#endif
|
||||
|
||||
if (!glfwInit()) {
|
||||
if (glfwInit() == GLFW_FALSE) {
|
||||
TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize GLFW for Vulkan");
|
||||
return RL_FALSE;
|
||||
}
|
||||
|
||||
if (!glfwVulkanSupported()) {
|
||||
if (glfwVulkanSupported() == GLFW_FALSE) {
|
||||
TRACELOG(LOG_FATAL, "PLATFORM: Vulkan not supported by GLFW or system drivers");
|
||||
glfwTerminate();
|
||||
return RL_FALSE;
|
||||
}
|
||||
TRACELOG(LOG_INFO, "PLATFORM: GLFW Vulkan support detected.");
|
||||
|
||||
// Get required instance extensions
|
||||
uint32_t requiredExtensionsCount = 0;
|
||||
const char **requiredExtensions = glfwGetRequiredInstanceExtensions(&requiredExtensionsCount);
|
||||
if (requiredExtensions == NULL) {
|
||||
TRACELOG(LOG_FATAL, "PLATFORM: Could not get required Vulkan instance extensions");
|
||||
glfwTerminate();
|
||||
return RL_FALSE;
|
||||
}
|
||||
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Required Vulkan instance extensions (%u):", requiredExtensionsCount);
|
||||
for (uint32_t i = 0; i < requiredExtensionsCount; i++) TRACELOG(LOG_INFO, " %s", requiredExtensions[i]);
|
||||
|
||||
// Define desired validation layers
|
||||
const char *validationLayers[] = { "VK_LAYER_KHRONOS_validation" };
|
||||
uint32_t enabledLayerCount = 0;
|
||||
const char *enabledLayers[1]; // Max 1 layer for now
|
||||
|
||||
#if defined(RLGL_ENABLE_VULKAN_DEBUG) // Or a custom define for enabling validation layers
|
||||
uint32_t layerCount = 0;
|
||||
vkEnumerateInstanceLayerProperties(&layerCount, NULL);
|
||||
if (layerCount > 0) {
|
||||
VkLayerProperties *availableLayers = (VkLayerProperties *)RL_MALLOC(layerCount * sizeof(VkLayerProperties));
|
||||
if (availableLayers == NULL) {
|
||||
TRACELOG(LOG_WARNING, "PLATFORM: Failed to allocate memory for Vulkan layer properties");
|
||||
} else {
|
||||
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers);
|
||||
bool layerFound = false;
|
||||
for (uint32_t i = 0; i < layerCount; i++) {
|
||||
if (strcmp(validationLayers[0], availableLayers[i].layerName) == 0) {
|
||||
layerFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
RL_FREE(availableLayers);
|
||||
|
||||
if (layerFound) {
|
||||
enabledLayers[0] = validationLayers[0];
|
||||
enabledLayerCount = 1;
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Enabled validation layer: %s", enabledLayers[0]);
|
||||
} else {
|
||||
TRACELOG(LOG_WARNING, "PLATFORM: Validation layer VK_LAYER_KHRONOS_validation not available");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TRACELOG(LOG_INFO, "PLATFORM: No Vulkan instance layers found.");
|
||||
}
|
||||
#endif
|
||||
|
||||
VkApplicationInfo appInfo = {0};
|
||||
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
appInfo.pApplicationName = (CORE.Window.title != NULL) ? CORE.Window.title : "raylib Vulkan App";
|
||||
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||
appInfo.pEngineName = "raylib";
|
||||
appInfo.engineVersion = VK_MAKE_VERSION(RAYLIB_VERSION_MAJOR, RAYLIB_VERSION_MINOR, RAYLIB_VERSION_PATCH);
|
||||
appInfo.apiVersion = VK_API_VERSION_1_1;
|
||||
|
||||
VkInstanceCreateInfo createInfo = {0};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
createInfo.pApplicationInfo = &appInfo;
|
||||
createInfo.enabledExtensionCount = requiredExtensionsCount;
|
||||
createInfo.ppEnabledExtensionNames = requiredExtensions;
|
||||
createInfo.enabledLayerCount = enabledLayerCount;
|
||||
createInfo.ppEnabledLayerNames = (enabledLayerCount > 0) ? enabledLayers : NULL;
|
||||
|
||||
// Initialize global vkInstanceHandle
|
||||
vkInstanceHandle = VK_NULL_HANDLE; // Ensure it's NULL before creation
|
||||
VkResult result = vkCreateInstance(&createInfo, NULL, &vkInstanceHandle);
|
||||
if (result != VK_SUCCESS) {
|
||||
TRACELOG(LOG_FATAL, "PLATFORM: Failed to create Vulkan instance (Error: %i)", result);
|
||||
glfwTerminate();
|
||||
return RL_FALSE;
|
||||
}
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Vulkan instance created successfully");
|
||||
|
||||
// Window hints should be set *before* window creation
|
||||
glfwDefaultWindowHints();
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // Critical for Vulkan
|
||||
|
||||
|
|
@ -1384,8 +1459,8 @@ static int InitPlatformVulkan(void) {
|
|||
else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE);
|
||||
if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||
else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||
if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // Cannot be set on creation
|
||||
if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // Cannot be set on creation
|
||||
if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // Cannot be set on creation, will be handled later if set
|
||||
if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // Cannot be set on creation, will be handled later if set
|
||||
if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
|
||||
else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
|
||||
if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
|
||||
|
|
@ -1396,7 +1471,7 @@ static int InitPlatformVulkan(void) {
|
|||
if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) {
|
||||
glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
|
||||
#if defined(__APPLE__)
|
||||
glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE);
|
||||
// glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE); // This hint might be managed differently or implicitly with Vulkan/MoltenVK
|
||||
#endif
|
||||
} else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
|
||||
if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
|
||||
|
|
@ -1427,17 +1502,30 @@ static int InitPlatformVulkan(void) {
|
|||
int creationHeight = (CORE.Window.screen.height > 0) ? CORE.Window.screen.height : 480;
|
||||
|
||||
platform.handle = glfwCreateWindow(creationWidth, creationHeight, (CORE.Window.title != 0)? CORE.Window.title : " ", CORE.Window.fullscreen ? monitor : NULL, NULL);
|
||||
CORE.Window.handle = platform.handle;
|
||||
if (!CORE.Window.handle) {
|
||||
if (!platform.handle) {
|
||||
TRACELOG(LOG_FATAL, "PLATFORM: Failed to create GLFW window for Vulkan");
|
||||
vkDestroyInstance(vkInstanceHandle, NULL); // Clean up created instance
|
||||
vkInstanceHandle = VK_NULL_HANDLE;
|
||||
glfwTerminate();
|
||||
return RL_FALSE;
|
||||
}
|
||||
TRACELOG(LOG_INFO, "PLATFORM: GLFW window created for Vulkan");
|
||||
CORE.Window.handle = platform.handle; // Assign global window handle after successful creation
|
||||
|
||||
vkInstanceHandle = (VkInstance)0x1;
|
||||
vkSurfaceHandle = (VkSurfaceKHR)0x1;
|
||||
TRACELOG(LOG_INFO, "PLATFORM: VkInstance and VkSurfaceKHR STUBBED as non-null for rlvkInit testing.");
|
||||
// Create Vulkan surface
|
||||
vkSurfaceHandle = VK_NULL_HANDLE; // Ensure it's NULL before creation
|
||||
VkResult surfaceResult = glfwCreateWindowSurface(vkInstanceHandle, platform.handle, NULL, &vkSurfaceHandle);
|
||||
if (surfaceResult != VK_SUCCESS) {
|
||||
TRACELOG(LOG_FATAL, "PLATFORM: Failed to create Vulkan window surface (Error: %i)", surfaceResult);
|
||||
vkDestroyInstance(vkInstanceHandle, NULL);
|
||||
vkInstanceHandle = VK_NULL_HANDLE;
|
||||
glfwDestroyWindow(platform.handle);
|
||||
platform.handle = NULL;
|
||||
CORE.Window.handle = NULL;
|
||||
glfwTerminate();
|
||||
return RL_FALSE;
|
||||
}
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Vulkan window surface created successfully");
|
||||
|
||||
// Setup GLFW callbacks
|
||||
glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback);
|
||||
|
|
@ -1491,7 +1579,7 @@ static int InitPlatformVulkan(void) {
|
|||
}
|
||||
|
||||
CORE.Window.ready = RL_TRUE;
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Vulkan platform initialized successfully (GLFW STUBBED VkInstance/Surface)");
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Vulkan platform initialized successfully");
|
||||
|
||||
InitTimer();
|
||||
CORE.Storage.basePath = GetWorkingDirectory();
|
||||
|
|
@ -1500,20 +1588,50 @@ static int InitPlatformVulkan(void) {
|
|||
}
|
||||
|
||||
static void ClosePlatformVulkan(void) {
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Closing Vulkan platform (GLFW STUB)");
|
||||
#if defined(GRAPHICS_API_VULKAN)
|
||||
vkSurfaceHandle = VK_NULL_HANDLE;
|
||||
vkInstanceHandle = VK_NULL_HANDLE;
|
||||
TRACELOG(LOG_INFO, "PLATFORM: VkInstance and VkSurfaceKHR STUBBED as NULL.");
|
||||
#endif
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Closing Vulkan platform");
|
||||
|
||||
if (CORE.Window.handle != NULL) {
|
||||
glfwDestroyWindow(CORE.Window.handle);
|
||||
CORE.Window.handle = NULL;
|
||||
platform.handle = NULL;
|
||||
// Destroy Vulkan surface
|
||||
// Note: vkGetInstanceProcAddr is not strictly needed for vkDestroySurfaceKHR if Vulkan headers are recent enough
|
||||
// and vkDestroySurfaceKHR was linked directly or via a loader. However, explicitly getting the function pointer
|
||||
// is safer if there's any doubt about the linking process or for older setups.
|
||||
// For simplicity and modern Vulkan loaders (like the one GLFW might use internally or if linked with Vulkan SDK),
|
||||
// direct call is often fine. If issues arise, use vkGetInstanceProcAddr.
|
||||
if (vkSurfaceHandle != VK_NULL_HANDLE && vkInstanceHandle != VK_NULL_HANDLE) {
|
||||
// PFN_vkDestroySurfaceKHR pfnDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)vkGetInstanceProcAddr(vkInstanceHandle, "vkDestroySurfaceKHR");
|
||||
// if (pfnDestroySurfaceKHR) pfnDestroySurfaceKHR(vkInstanceHandle, vkSurfaceHandle, NULL);
|
||||
// else TRACELOG(LOG_WARNING, "PLATFORM: Failed to get vkDestroySurfaceKHR proc address");
|
||||
// Assuming vkDestroySurfaceKHR is available directly through linking/loader:
|
||||
vkDestroySurfaceKHR(vkInstanceHandle, vkSurfaceHandle, NULL);
|
||||
vkSurfaceHandle = VK_NULL_HANDLE; // Set to NULL after destruction
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Vulkan surface destroyed");
|
||||
} else if (vkInstanceHandle == VK_NULL_HANDLE && vkSurfaceHandle != VK_NULL_HANDLE) {
|
||||
TRACELOG(LOG_WARNING, "PLATFORM: vkInstanceHandle is NULL, cannot destroy vkSurfaceHandle. Surface might be leaked if instance was lost prematurely.");
|
||||
vkSurfaceHandle = VK_NULL_HANDLE; // Still nullify to prevent reuse attempts
|
||||
}
|
||||
|
||||
|
||||
// Destroy Vulkan instance
|
||||
if (vkInstanceHandle != VK_NULL_HANDLE) {
|
||||
vkDestroyInstance(vkInstanceHandle, NULL);
|
||||
vkInstanceHandle = VK_NULL_HANDLE; // Set to NULL after destruction
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Vulkan instance destroyed");
|
||||
}
|
||||
|
||||
// Destroy GLFW window
|
||||
// Use platform.handle as it's the direct reference to the created window.
|
||||
// CORE.Window.handle should mirror platform.handle but platform.handle is the source of truth here.
|
||||
if (platform.handle != NULL) {
|
||||
glfwDestroyWindow(platform.handle);
|
||||
platform.handle = NULL; // Set to NULL after destruction
|
||||
CORE.Window.handle = NULL; // Ensure CORE's copy is also NULL
|
||||
TRACELOG(LOG_INFO, "PLATFORM: GLFW window destroyed");
|
||||
}
|
||||
|
||||
// Terminate GLFW
|
||||
glfwTerminate();
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Vulkan platform resources closed (GLFW STUBBED VkInstance/Surface)");
|
||||
TRACELOG(LOG_INFO, "PLATFORM: GLFW terminated");
|
||||
|
||||
TRACELOG(LOG_INFO, "PLATFORM: Vulkan platform resources closed successfully");
|
||||
}
|
||||
#endif // GRAPHICS_API_VULKAN
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue