|
|
@ -1,27 +1,784 @@ |
|
|
#include "rlvk.h" |
|
|
#include "rlvk.h" |
|
|
#include "utils.h" // For TRACELOG if needed |
|
|
|
|
|
#include <stdio.h> // For printf in stubs |
|
|
|
|
|
#include <stdlib.h> // For RL_MALLOC, RL_FREE if used |
|
|
|
|
|
|
|
|
#include "utils.h" // For TRACELOG |
|
|
|
|
|
#include <stdio.h> // For TRACELOG / printf |
|
|
|
|
|
#include <stdlib.h> // For RL_MALLOC, RL_FREE, NULL |
|
|
|
|
|
#include <string.h> // For strcmp, memset |
|
|
|
|
|
#include <stdbool.h> // For bool type |
|
|
|
|
|
|
|
|
|
|
|
// Core Vulkan Handles |
|
|
|
|
|
static VkInstance vkInstance = VK_NULL_HANDLE; |
|
|
|
|
|
static VkSurfaceKHR vkSurface = VK_NULL_HANDLE; |
|
|
|
|
|
static VkPhysicalDevice vkPhysicalDevice = VK_NULL_HANDLE; |
|
|
|
|
|
static VkDevice vkDevice = VK_NULL_HANDLE; |
|
|
|
|
|
static VkQueue vkGraphicsQueue = VK_NULL_HANDLE; |
|
|
|
|
|
static VkQueue vkPresentQueue = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
|
|
|
|
// Queue Family Indices |
|
|
|
|
|
typedef struct { |
|
|
|
|
|
uint32_t graphicsFamily; |
|
|
|
|
|
uint32_t presentFamily; |
|
|
|
|
|
bool graphicsFamilyHasValue; |
|
|
|
|
|
bool presentFamilyHasValue; |
|
|
|
|
|
} QueueFamilyIndices; |
|
|
|
|
|
static QueueFamilyIndices queueFamilyIndices; |
|
|
|
|
|
|
|
|
|
|
|
// Swapchain related |
|
|
|
|
|
static VkSwapchainKHR vkSwapchain = VK_NULL_HANDLE; |
|
|
|
|
|
static VkFormat vkSwapchainImageFormat; |
|
|
|
|
|
static VkExtent2D vkSwapchainExtent; |
|
|
|
|
|
static VkImage* vkSwapchainImages = NULL; |
|
|
|
|
|
static uint32_t vkSwapchainImageCount = 0; |
|
|
|
|
|
static VkImageView* vkSwapchainImageViews = NULL; |
|
|
|
|
|
|
|
|
|
|
|
// Render Pass and Framebuffers |
|
|
|
|
|
static VkRenderPass vkRenderPass = VK_NULL_HANDLE; |
|
|
|
|
|
static VkFramebuffer* vkFramebuffers = NULL; // One per swapchain image view |
|
|
|
|
|
|
|
|
|
|
|
// Depth Buffer |
|
|
|
|
|
static VkImage vkDepthImage = VK_NULL_HANDLE; |
|
|
|
|
|
static VkDeviceMemory vkDepthImageMemory = VK_NULL_HANDLE; |
|
|
|
|
|
static VkImageView vkDepthImageView = VK_NULL_HANDLE; |
|
|
|
|
|
static VkFormat vkDepthFormat; |
|
|
|
|
|
|
|
|
|
|
|
// Command Pool and Command Buffers |
|
|
|
|
|
static VkCommandPool vkCommandPool = VK_NULL_HANDLE; |
|
|
|
|
|
static VkCommandBuffer* vkCommandBuffers = NULL; // One per framebuffer |
|
|
|
|
|
|
|
|
|
|
|
// Synchronization Primitives |
|
|
|
|
|
static VkSemaphore vkImageAvailableSemaphore = VK_NULL_HANDLE; |
|
|
|
|
|
static VkSemaphore vkRenderFinishedSemaphore = VK_NULL_HANDLE; |
|
|
|
|
|
static VkFence* vkInFlightFences = NULL; // One per frame in flight (usually same as swapchain image count) |
|
|
|
|
|
// static VkFence* imagesInFlight; // Maps swapchain images to fences |
|
|
|
|
|
|
|
|
// Global or static variables for Vulkan state (minimal for stubs) |
|
|
|
|
|
static bool rlvkReady = false; |
|
|
static bool rlvkReady = false; |
|
|
|
|
|
static int screenWidth = 0; |
|
|
|
|
|
static int screenHeight = 0; |
|
|
|
|
|
|
|
|
|
|
|
// Drawing/Frame state |
|
|
|
|
|
static uint32_t currentFrame = 0; |
|
|
|
|
|
#define MAX_FRAMES_IN_FLIGHT 2 // Default to 2, will be set to vkSwapchainImageCount if different |
|
|
|
|
|
// This define might become a static variable if vkSwapchainImageCount can change (e.g. recreation) |
|
|
|
|
|
static uint32_t acquiredImageIndex = 0; // To store the image index from vkAcquireNextImageKHR |
|
|
|
|
|
|
|
|
|
|
|
// Clear values for the render pass |
|
|
|
|
|
static VkClearColorValue currentClearColor = {{0.0f, 0.0f, 0.0f, 1.0f}}; // Default to black |
|
|
|
|
|
static VkClearDepthStencilValue defaultDepthStencilClear = {1.0f, 0}; // Default depth/stencil clear |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Helper function to find suitable queue families |
|
|
|
|
|
static QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface) { |
|
|
|
|
|
QueueFamilyIndices indices = {0, 0, false, false}; |
|
|
|
|
|
uint32_t queueFamilyCount = 0; |
|
|
|
|
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, NULL); |
|
|
|
|
|
|
|
|
|
|
|
VkQueueFamilyProperties* queueFamilies = (VkQueueFamilyProperties*)RL_MALLOC(queueFamilyCount * sizeof(VkQueueFamilyProperties)); |
|
|
|
|
|
if (!queueFamilies) { |
|
|
|
|
|
TRACELOG(LOG_ERROR, "RLVK: Failed to allocate memory for queue families"); |
|
|
|
|
|
return indices; |
|
|
|
|
|
} |
|
|
|
|
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies); |
|
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < queueFamilyCount; i++) { |
|
|
|
|
|
if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { |
|
|
|
|
|
indices.graphicsFamily = i; |
|
|
|
|
|
indices.graphicsFamilyHasValue = true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
VkBool32 presentSupport = false; |
|
|
|
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); |
|
|
|
|
|
if (presentSupport) { |
|
|
|
|
|
indices.presentFamily = i; |
|
|
|
|
|
indices.presentFamilyHasValue = true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (indices.graphicsFamilyHasValue && indices.presentFamilyHasValue) { |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
RL_FREE(queueFamilies); |
|
|
|
|
|
return indices; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Helper function to check device suitability |
|
|
|
|
|
static bool isDeviceSuitable(VkPhysicalDevice device, VkSurfaceKHR surface) { |
|
|
|
|
|
QueueFamilyIndices indices = findQueueFamilies(device, surface); |
|
|
|
|
|
// TODO: Check for required device extensions (e.g. swapchain) |
|
|
|
|
|
// TODO: Query and check surface formats and present modes |
|
|
|
|
|
return indices.graphicsFamilyHasValue && indices.presentFamilyHasValue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void rlvkInit(VkInstance instance, VkSurfaceKHR surface, int width, int height) { |
|
|
void rlvkInit(VkInstance instance, VkSurfaceKHR surface, int width, int height) { |
|
|
printf("rlvkInit called (STUB)\n"); |
|
|
|
|
|
// Minimal check or setup |
|
|
|
|
|
if (instance != VK_NULL_HANDLE && surface != VK_NULL_HANDLE) { |
|
|
|
|
|
rlvkReady = true; |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Vulkan backend initialized (stubbed)."); |
|
|
|
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Initializing Vulkan backend."); |
|
|
|
|
|
if (rlvkReady) { |
|
|
|
|
|
TRACELOG(LOG_WARNING, "RLVK: Vulkan backend already initialized."); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
vkInstance = instance; |
|
|
|
|
|
vkSurface = surface; |
|
|
|
|
|
screenWidth = width; |
|
|
|
|
|
screenHeight = height; |
|
|
|
|
|
|
|
|
|
|
|
if (vkInstance == VK_NULL_HANDLE) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Provided VkInstance is NULL."); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
if (vkSurface == VK_NULL_HANDLE) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Provided VkSurfaceKHR is NULL."); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// --- Physical Device Selection --- |
|
|
|
|
|
uint32_t deviceCount = 0; |
|
|
|
|
|
VkResult result = vkEnumeratePhysicalDevices(vkInstance, &deviceCount, NULL); |
|
|
|
|
|
if (result != VK_SUCCESS || deviceCount == 0) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to find GPUs with Vulkan support!"); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
VkPhysicalDevice* devices = (VkPhysicalDevice*)RL_MALLOC(deviceCount * sizeof(VkPhysicalDevice)); |
|
|
|
|
|
if (!devices) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to allocate memory for physical devices list."); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
result = vkEnumeratePhysicalDevices(vkInstance, &deviceCount, devices); |
|
|
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to enumerate physical devices."); |
|
|
|
|
|
RL_FREE(devices); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Found %d physical device(s).", deviceCount); |
|
|
|
|
|
VkPhysicalDeviceProperties chosenDeviceProperties; // To store properties of the chosen device for logging or other uses |
|
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < deviceCount; i++) { |
|
|
|
|
|
VkPhysicalDeviceProperties deviceProperties; |
|
|
|
|
|
vkGetPhysicalDeviceProperties(devices[i], &deviceProperties); |
|
|
|
|
|
TRACELOG(LOG_DEBUG, "RLVK: Evaluating device: %s (ID: %u, Type: %u)", deviceProperties.deviceName, deviceProperties.deviceID, deviceProperties.deviceType); |
|
|
|
|
|
|
|
|
|
|
|
if (isDeviceSuitable(devices[i], vkSurface)) { |
|
|
|
|
|
// Prefer discrete GPU if available |
|
|
|
|
|
if (vkPhysicalDevice == VK_NULL_HANDLE || deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { |
|
|
|
|
|
vkPhysicalDevice = devices[i]; |
|
|
|
|
|
chosenDeviceProperties = deviceProperties; // Store its properties |
|
|
|
|
|
// If it's a discrete GPU, we might want to break early, or continue to see if there are others. |
|
|
|
|
|
// For now, take the first suitable discrete GPU or the first suitable integrated/other if no discrete. |
|
|
|
|
|
if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Selected discrete GPU: %s", deviceProperties.deviceName); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
RL_FREE(devices); |
|
|
|
|
|
|
|
|
|
|
|
if (vkPhysicalDevice == VK_NULL_HANDLE) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to find a suitable GPU!"); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Selected physical device: %s", chosenDeviceProperties.deviceName); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
queueFamilyIndices = findQueueFamilies(vkPhysicalDevice, vkSurface); |
|
|
|
|
|
if (!queueFamilyIndices.graphicsFamilyHasValue || !queueFamilyIndices.presentFamilyHasValue) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Could not find required queue families on selected physical device."); |
|
|
|
|
|
vkPhysicalDevice = VK_NULL_HANDLE; // Reset since it's not fully suitable |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Graphics Queue Family Index: %u", queueFamilyIndices.graphicsFamily); |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Present Queue Family Index: %u", queueFamilyIndices.presentFamily); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Placeholder for further initialization steps |
|
|
|
|
|
// For now, if we reached here with a physical device, consider it partially ready for this phase. |
|
|
|
|
|
// rlvkReady = true; // This will be set at the very end of the full Init function. |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Physical device selected successfully. Further initialization pending."); |
|
|
|
|
|
|
|
|
|
|
|
// TODO: Implement steps 3-11 as per the plan. |
|
|
|
|
|
// For now, stubbing the rest of the function. |
|
|
|
|
|
// This is just Phase 1. |
|
|
|
|
|
if (vkPhysicalDevice != VK_NULL_HANDLE) { |
|
|
|
|
|
// rlvkReady = true; // This will be at the end of the full function |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Stub: Phase 1 (Device Selection) complete."); |
|
|
|
|
|
} else { |
|
|
|
|
|
TRACELOG(LOG_ERROR, "RLVK: Stub: Phase 1 (Device Selection) failed."); |
|
|
|
|
|
rlvkReady = false; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
// --- Logical Device Creation --- |
|
|
|
|
|
float queuePriority = 1.0f; |
|
|
|
|
|
VkDeviceQueueCreateInfo queueCreateInfos[2]; // Max 2: one for graphics, one for present (if different) |
|
|
|
|
|
uint32_t uniqueQueueFamilyCount = 0; |
|
|
|
|
|
|
|
|
|
|
|
// Graphics Queue |
|
|
|
|
|
queueCreateInfos[uniqueQueueFamilyCount].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
|
|
|
|
|
queueCreateInfos[uniqueQueueFamilyCount].queueFamilyIndex = queueFamilyIndices.graphicsFamily; |
|
|
|
|
|
queueCreateInfos[uniqueQueueFamilyCount].queueCount = 1; |
|
|
|
|
|
queueCreateInfos[uniqueQueueFamilyCount].pQueuePriorities = &queuePriority; |
|
|
|
|
|
queueCreateInfos[uniqueQueueFamilyCount].pNext = NULL; |
|
|
|
|
|
queueCreateInfos[uniqueQueueFamilyCount].flags = 0; |
|
|
|
|
|
uniqueQueueFamilyCount++; |
|
|
|
|
|
|
|
|
|
|
|
// Present Queue (if different from graphics) |
|
|
|
|
|
if (queueFamilyIndices.presentFamily != queueFamilyIndices.graphicsFamily) { |
|
|
|
|
|
queueCreateInfos[uniqueQueueFamilyCount].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
|
|
|
|
|
queueCreateInfos[uniqueQueueFamilyCount].queueFamilyIndex = queueFamilyIndices.presentFamily; |
|
|
|
|
|
queueCreateInfos[uniqueQueueFamilyCount].queueCount = 1; |
|
|
|
|
|
queueCreateInfos[uniqueQueueFamilyCount].pQueuePriorities = &queuePriority; |
|
|
|
|
|
queueCreateInfos[uniqueQueueFamilyCount].pNext = NULL; |
|
|
|
|
|
queueCreateInfos[uniqueQueueFamilyCount].flags = 0; |
|
|
|
|
|
uniqueQueueFamilyCount++; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
VkPhysicalDeviceFeatures deviceFeatures = {0}; // Initialize all features to VK_FALSE |
|
|
|
|
|
// Enable specific features if needed, e.g. deviceFeatures.samplerAnisotropy = VK_TRUE; |
|
|
|
|
|
|
|
|
|
|
|
const char* deviceExtensions[] = { |
|
|
|
|
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
VkDeviceCreateInfo deviceCreateInfo = {0}; |
|
|
|
|
|
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; |
|
|
|
|
|
deviceCreateInfo.queueCreateInfoCount = uniqueQueueFamilyCount; |
|
|
|
|
|
deviceCreateInfo.pQueueCreateInfos = queueCreateInfos; |
|
|
|
|
|
deviceCreateInfo.pEnabledFeatures = &deviceFeatures; |
|
|
|
|
|
deviceCreateInfo.enabledExtensionCount = sizeof(deviceExtensions) / sizeof(deviceExtensions[0]); |
|
|
|
|
|
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions; |
|
|
|
|
|
// deviceCreateInfo.enabledLayerCount is deprecated and ignored for vkCreateDevice. Validation layers are instance-level. |
|
|
|
|
|
|
|
|
|
|
|
result = vkCreateDevice(vkPhysicalDevice, &deviceCreateInfo, NULL, &vkDevice); |
|
|
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to create logical device (Error: %i)", result); |
|
|
|
|
|
// Potentially reset vkPhysicalDevice here if cleanup is needed |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Logical device created successfully."); |
|
|
|
|
|
|
|
|
|
|
|
vkGetDeviceQueue(vkDevice, queueFamilyIndices.graphicsFamily, 0, &vkGraphicsQueue); |
|
|
|
|
|
vkGetDeviceQueue(vkDevice, queueFamilyIndices.presentFamily, 0, &vkPresentQueue); |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Graphics and Present queues obtained."); |
|
|
|
|
|
|
|
|
|
|
|
// --- Swapchain Creation --- |
|
|
|
|
|
VkSurfaceCapabilitiesKHR capabilities; |
|
|
|
|
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vkPhysicalDevice, vkSurface, &capabilities); |
|
|
|
|
|
|
|
|
|
|
|
uint32_t formatCount; |
|
|
|
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(vkPhysicalDevice, vkSurface, &formatCount, NULL); |
|
|
|
|
|
VkSurfaceFormatKHR* formats = NULL; |
|
|
|
|
|
if (formatCount != 0) { |
|
|
|
|
|
formats = (VkSurfaceFormatKHR*)RL_MALLOC(formatCount * sizeof(VkSurfaceFormatKHR)); |
|
|
|
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(vkPhysicalDevice, vkSurface, &formatCount, formats); |
|
|
|
|
|
} else { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: No surface formats found for swapchain creation."); |
|
|
|
|
|
vkDestroyDevice(vkDevice, NULL); vkDevice = VK_NULL_HANDLE; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
uint32_t presentModeCount; |
|
|
|
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(vkPhysicalDevice, vkSurface, &presentModeCount, NULL); |
|
|
|
|
|
VkPresentModeKHR* presentModes = NULL; |
|
|
|
|
|
if (presentModeCount != 0) { |
|
|
|
|
|
presentModes = (VkPresentModeKHR*)RL_MALLOC(presentModeCount * sizeof(VkPresentModeKHR)); |
|
|
|
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(vkPhysicalDevice, vkSurface, &presentModeCount, presentModes); |
|
|
|
|
|
} else { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: No present modes found for swapchain creation."); |
|
|
|
|
|
RL_FREE(formats); |
|
|
|
|
|
vkDestroyDevice(vkDevice, NULL); vkDevice = VK_NULL_HANDLE; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Choose swap surface format |
|
|
|
|
|
VkSurfaceFormatKHR surfaceFormat = formats[0]; // Default to first available |
|
|
|
|
|
for (uint32_t i = 0; i < formatCount; i++) { |
|
|
|
|
|
if (formats[i].format == VK_FORMAT_B8G8R8A8_SRGB && formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { |
|
|
|
|
|
surfaceFormat = formats[i]; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
vkSwapchainImageFormat = surfaceFormat.format; |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Chosen swapchain format: %d, color space: %d", surfaceFormat.format, surfaceFormat.colorSpace); |
|
|
|
|
|
|
|
|
|
|
|
// Choose swap present mode |
|
|
|
|
|
VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; // Guaranteed to be available |
|
|
|
|
|
for (uint32_t i = 0; i < presentModeCount; i++) { |
|
|
|
|
|
if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { |
|
|
|
|
|
presentMode = presentModes[i]; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Chosen present mode: %d", presentMode); |
|
|
|
|
|
|
|
|
|
|
|
RL_FREE(formats); |
|
|
|
|
|
RL_FREE(presentModes); |
|
|
|
|
|
|
|
|
|
|
|
// Choose swap extent |
|
|
|
|
|
if (capabilities.currentExtent.width != UINT32_MAX) { |
|
|
|
|
|
vkSwapchainExtent = capabilities.currentExtent; |
|
|
|
|
|
} else { |
|
|
|
|
|
vkSwapchainExtent.width = (uint32_t)screenWidth; |
|
|
|
|
|
vkSwapchainExtent.height = (uint32_t)screenHeight; |
|
|
|
|
|
vkSwapchainExtent.width = MAX(capabilities.minImageExtent.width, MIN(capabilities.maxImageExtent.width, vkSwapchainExtent.width)); |
|
|
|
|
|
vkSwapchainExtent.height = MAX(capabilities.minImageExtent.height, MIN(capabilities.maxImageExtent.height, vkSwapchainExtent.height)); |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Swapchain extent: %u x %u", vkSwapchainExtent.width, vkSwapchainExtent.height); |
|
|
|
|
|
|
|
|
|
|
|
vkSwapchainImageCount = capabilities.minImageCount + 1; |
|
|
|
|
|
if (capabilities.maxImageCount > 0 && vkSwapchainImageCount > capabilities.maxImageCount) { |
|
|
|
|
|
vkSwapchainImageCount = capabilities.maxImageCount; |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Swapchain image count: %u", vkSwapchainImageCount); |
|
|
|
|
|
|
|
|
|
|
|
VkSwapchainCreateInfoKHR swapchainCreateInfo = {0}; |
|
|
|
|
|
swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; |
|
|
|
|
|
swapchainCreateInfo.surface = vkSurface; |
|
|
|
|
|
swapchainCreateInfo.minImageCount = vkSwapchainImageCount; |
|
|
|
|
|
swapchainCreateInfo.imageFormat = surfaceFormat.format; |
|
|
|
|
|
swapchainCreateInfo.imageColorSpace = surfaceFormat.colorSpace; |
|
|
|
|
|
swapchainCreateInfo.imageExtent = vkSwapchainExtent; |
|
|
|
|
|
swapchainCreateInfo.imageArrayLayers = 1; |
|
|
|
|
|
swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; // For rendering directly to swapchain images |
|
|
|
|
|
|
|
|
|
|
|
uint32_t qFamilyIndices[] = {queueFamilyIndices.graphicsFamily, queueFamilyIndices.presentFamily}; |
|
|
|
|
|
if (queueFamilyIndices.graphicsFamily != queueFamilyIndices.presentFamily) { |
|
|
|
|
|
swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; |
|
|
|
|
|
swapchainCreateInfo.queueFamilyIndexCount = 2; |
|
|
|
|
|
swapchainCreateInfo.pQueueFamilyIndices = qFamilyIndices; |
|
|
} else { |
|
|
} else { |
|
|
TRACELOG(LOG_ERROR, "RLVK: Failed to initialize Vulkan backend due to null instance or surface (stubbed)."); |
|
|
|
|
|
rlvkReady = false; |
|
|
|
|
|
|
|
|
swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; |
|
|
|
|
|
swapchainCreateInfo.queueFamilyIndexCount = 0; // Optional |
|
|
|
|
|
swapchainCreateInfo.pQueueFamilyIndices = NULL; // Optional |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
swapchainCreateInfo.preTransform = capabilities.currentTransform; |
|
|
|
|
|
swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; // No alpha blending with window system |
|
|
|
|
|
swapchainCreateInfo.presentMode = presentMode; |
|
|
|
|
|
swapchainCreateInfo.clipped = VK_TRUE; // Allow clipping if other windows obscure parts of the surface |
|
|
|
|
|
swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE; // For resizing, not used now |
|
|
|
|
|
|
|
|
|
|
|
result = vkCreateSwapchainKHR(vkDevice, &swapchainCreateInfo, NULL, &vkSwapchain); |
|
|
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to create swapchain (Error: %i)", result); |
|
|
|
|
|
vkDestroyDevice(vkDevice, NULL); vkDevice = VK_NULL_HANDLE; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Swapchain created successfully."); |
|
|
|
|
|
|
|
|
|
|
|
// Get swapchain images |
|
|
|
|
|
// vkSwapchainImageCount was requested, now query actual count (can be higher) |
|
|
|
|
|
vkGetSwapchainImagesKHR(vkDevice, vkSwapchain, &vkSwapchainImageCount, NULL); |
|
|
|
|
|
vkSwapchainImages = (VkImage*)RL_MALLOC(vkSwapchainImageCount * sizeof(VkImage)); |
|
|
|
|
|
if (!vkSwapchainImages) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to allocate memory for swapchain images."); |
|
|
|
|
|
vkDestroySwapchainKHR(vkDevice, vkSwapchain, NULL); vkSwapchain = VK_NULL_HANDLE; |
|
|
|
|
|
vkDestroyDevice(vkDevice, NULL); vkDevice = VK_NULL_HANDLE; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
vkGetSwapchainImagesKHR(vkDevice, vkSwapchain, &vkSwapchainImageCount, vkSwapchainImages); |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Retrieved %u swapchain images.", vkSwapchainImageCount); |
|
|
|
|
|
|
|
|
|
|
|
// --- Image View Creation (Step 5) --- |
|
|
|
|
|
vkSwapchainImageViews = (VkImageView*)RL_MALLOC(vkSwapchainImageCount * sizeof(VkImageView)); |
|
|
|
|
|
if (!vkSwapchainImageViews) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to allocate memory for swapchain image views."); |
|
|
|
|
|
// Perform necessary cleanup from previous steps |
|
|
|
|
|
RL_FREE(vkSwapchainImages); vkSwapchainImages = NULL; |
|
|
|
|
|
vkDestroySwapchainKHR(vkDevice, vkSwapchain, NULL); vkSwapchain = VK_NULL_HANDLE; |
|
|
|
|
|
vkDestroyDevice(vkDevice, NULL); vkDevice = VK_NULL_HANDLE; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < vkSwapchainImageCount; i++) { |
|
|
|
|
|
VkImageViewCreateInfo viewInfo = {0}; |
|
|
|
|
|
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; |
|
|
|
|
|
viewInfo.image = vkSwapchainImages[i]; |
|
|
|
|
|
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; |
|
|
|
|
|
viewInfo.format = vkSwapchainImageFormat; |
|
|
|
|
|
viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; |
|
|
|
|
|
viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; |
|
|
|
|
|
viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; |
|
|
|
|
|
viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; |
|
|
|
|
|
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
|
|
|
|
|
viewInfo.subresourceRange.baseMipLevel = 0; |
|
|
|
|
|
viewInfo.subresourceRange.levelCount = 1; |
|
|
|
|
|
viewInfo.subresourceRange.baseArrayLayer = 0; |
|
|
|
|
|
viewInfo.subresourceRange.layerCount = 1; |
|
|
|
|
|
|
|
|
|
|
|
result = vkCreateImageView(vkDevice, &viewInfo, NULL, &vkSwapchainImageViews[i]); |
|
|
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to create image view %u (Error: %i)", i, result); |
|
|
|
|
|
// Perform cleanup |
|
|
|
|
|
for (uint32_t j = 0; j < i; j++) vkDestroyImageView(vkDevice, vkSwapchainImageViews[j], NULL); |
|
|
|
|
|
RL_FREE(vkSwapchainImageViews); vkSwapchainImageViews = NULL; |
|
|
|
|
|
RL_FREE(vkSwapchainImages); vkSwapchainImages = NULL; |
|
|
|
|
|
vkDestroySwapchainKHR(vkDevice, vkSwapchain, NULL); vkSwapchain = VK_NULL_HANDLE; |
|
|
|
|
|
vkDestroyDevice(vkDevice, NULL); vkDevice = VK_NULL_HANDLE; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Swapchain image views created successfully."); |
|
|
|
|
|
|
|
|
|
|
|
// --- Render Pass Creation (Step 6) --- |
|
|
|
|
|
// Find supported depth format (helper function recommended) |
|
|
|
|
|
// VkFormat findSupportedFormat(const VkFormat* candidates, uint32_t candidateCount, VkImageTiling tiling, VkFormatFeatureFlags features); |
|
|
|
|
|
// For now, assume VK_FORMAT_D32_SFLOAT is supported. A real implementation needs findSupportedFormat. |
|
|
|
|
|
vkDepthFormat = VK_FORMAT_D32_SFLOAT; // Placeholder - must be queried |
|
|
|
|
|
|
|
|
|
|
|
VkAttachmentDescription colorAttachment = {0}; |
|
|
|
|
|
colorAttachment.format = vkSwapchainImageFormat; |
|
|
|
|
|
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; |
|
|
|
|
|
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; |
|
|
|
|
|
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
|
|
|
|
|
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
|
|
|
|
|
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
|
|
|
|
|
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
|
|
|
|
|
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; |
|
|
|
|
|
|
|
|
|
|
|
VkAttachmentDescription depthAttachment = {0}; |
|
|
|
|
|
depthAttachment.format = vkDepthFormat; // Must be a supported depth format |
|
|
|
|
|
depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; |
|
|
|
|
|
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; |
|
|
|
|
|
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
|
|
|
|
|
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
|
|
|
|
|
depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
|
|
|
|
|
depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
|
|
|
|
|
depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
|
|
|
|
|
|
|
|
|
|
|
VkAttachmentReference colorAttachmentRef = {0}; |
|
|
|
|
|
colorAttachmentRef.attachment = 0; |
|
|
|
|
|
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
|
|
|
|
|
|
|
|
|
|
|
VkAttachmentReference depthAttachmentRef = {0}; |
|
|
|
|
|
depthAttachmentRef.attachment = 1; |
|
|
|
|
|
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
|
|
|
|
|
|
|
|
|
|
|
VkSubpassDescription subpass = {0}; |
|
|
|
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
|
|
|
|
|
subpass.colorAttachmentCount = 1; |
|
|
|
|
|
subpass.pColorAttachments = &colorAttachmentRef; |
|
|
|
|
|
subpass.pDepthStencilAttachment = &depthAttachmentRef; |
|
|
|
|
|
|
|
|
|
|
|
VkSubpassDependency dependency = {0}; |
|
|
|
|
|
dependency.srcSubpass = VK_SUBPASS_EXTERNAL; |
|
|
|
|
|
dependency.dstSubpass = 0; |
|
|
|
|
|
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; |
|
|
|
|
|
dependency.srcAccessMask = 0; |
|
|
|
|
|
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; |
|
|
|
|
|
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; |
|
|
|
|
|
|
|
|
|
|
|
VkAttachmentDescription attachments[] = {colorAttachment, depthAttachment}; |
|
|
|
|
|
VkRenderPassCreateInfo renderPassInfo = {0}; |
|
|
|
|
|
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; |
|
|
|
|
|
renderPassInfo.attachmentCount = sizeof(attachments) / sizeof(VkAttachmentDescription); |
|
|
|
|
|
renderPassInfo.pAttachments = attachments; |
|
|
|
|
|
renderPassInfo.subpassCount = 1; |
|
|
|
|
|
renderPassInfo.pSubpasses = &subpass; |
|
|
|
|
|
renderPassInfo.dependencyCount = 1; |
|
|
|
|
|
renderPassInfo.pDependencies = &dependency; |
|
|
|
|
|
|
|
|
|
|
|
result = vkCreateRenderPass(vkDevice, &renderPassInfo, NULL, &vkRenderPass); |
|
|
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to create render pass (Error: %i)", result); |
|
|
|
|
|
// Perform cleanup... (image views, swapchain, device etc.) |
|
|
|
|
|
return; // Simplified cleanup for now |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Render pass created successfully."); |
|
|
|
|
|
|
|
|
|
|
|
// --- Depth Resources Creation (Step 7) --- |
|
|
|
|
|
VkImageCreateInfo imageInfo = {0}; |
|
|
|
|
|
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; |
|
|
|
|
|
imageInfo.imageType = VK_IMAGE_TYPE_2D; |
|
|
|
|
|
imageInfo.extent.width = vkSwapchainExtent.width; |
|
|
|
|
|
imageInfo.extent.height = vkSwapchainExtent.height; |
|
|
|
|
|
imageInfo.extent.depth = 1; |
|
|
|
|
|
imageInfo.mipLevels = 1; |
|
|
|
|
|
imageInfo.arrayLayers = 1; |
|
|
|
|
|
imageInfo.format = vkDepthFormat; |
|
|
|
|
|
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // For best performance |
|
|
|
|
|
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
|
|
|
|
|
imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; |
|
|
|
|
|
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; |
|
|
|
|
|
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
|
|
|
|
|
|
|
|
|
|
|
result = vkCreateImage(vkDevice, &imageInfo, NULL, &vkDepthImage); |
|
|
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to create depth image (Error: %i)", result); |
|
|
|
|
|
// Cleanup... |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
VkMemoryRequirements memRequirements; |
|
|
|
|
|
vkGetImageMemoryRequirements(vkDevice, vkDepthImage, &memRequirements); |
|
|
|
|
|
|
|
|
|
|
|
VkMemoryAllocateInfo allocInfo = {0}; |
|
|
|
|
|
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; |
|
|
|
|
|
allocInfo.allocationSize = memRequirements.size; |
|
|
|
|
|
// allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); // Helper needed |
|
|
|
|
|
// For now, assume memoryTypeIndex 0 is valid (highly unlikely in real scenario without querying) |
|
|
|
|
|
// This needs a proper findMemoryType implementation. |
|
|
|
|
|
uint32_t memoryTypeIndex = 0; // Placeholder - THIS IS A BUG without proper findMemoryType |
|
|
|
|
|
VkPhysicalDeviceMemoryProperties memProperties; |
|
|
|
|
|
vkGetPhysicalDeviceMemoryProperties(vkPhysicalDevice, &memProperties); |
|
|
|
|
|
bool memoryTypeFound = false; |
|
|
|
|
|
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { |
|
|
|
|
|
if ((memRequirements.memoryTypeBits & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { |
|
|
|
|
|
memoryTypeIndex = i; |
|
|
|
|
|
memoryTypeFound = true; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (!memoryTypeFound) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to find suitable memory type for depth image!"); |
|
|
|
|
|
// Cleanup... |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
allocInfo.memoryTypeIndex = memoryTypeIndex; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result = vkAllocateMemory(vkDevice, &allocInfo, NULL, &vkDepthImageMemory); |
|
|
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to allocate depth image memory (Error: %i)", result); |
|
|
|
|
|
// Cleanup... |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
vkBindImageMemory(vkDevice, vkDepthImage, vkDepthImageMemory, 0); |
|
|
|
|
|
|
|
|
|
|
|
VkImageViewCreateInfo depthViewInfo = {0}; |
|
|
|
|
|
depthViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; |
|
|
|
|
|
depthViewInfo.image = vkDepthImage; |
|
|
|
|
|
depthViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; |
|
|
|
|
|
depthViewInfo.format = vkDepthFormat; |
|
|
|
|
|
depthViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; |
|
|
|
|
|
depthViewInfo.subresourceRange.baseMipLevel = 0; |
|
|
|
|
|
depthViewInfo.subresourceRange.levelCount = 1; |
|
|
|
|
|
depthViewInfo.subresourceRange.baseArrayLayer = 0; |
|
|
|
|
|
depthViewInfo.subresourceRange.layerCount = 1; |
|
|
|
|
|
|
|
|
|
|
|
result = vkCreateImageView(vkDevice, &depthViewInfo, NULL, &vkDepthImageView); |
|
|
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to create depth image view (Error: %i)", result); |
|
|
|
|
|
// Cleanup... |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Depth resources created successfully."); |
|
|
|
|
|
|
|
|
|
|
|
// --- Framebuffer Creation (Step 8) --- |
|
|
|
|
|
vkFramebuffers = (VkFramebuffer*)RL_MALLOC(vkSwapchainImageCount * sizeof(VkFramebuffer)); |
|
|
|
|
|
if (!vkFramebuffers) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to allocate memory for framebuffers."); |
|
|
|
|
|
// Perform cleanup... (this is getting repetitive, a helper might be good) |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
for (uint32_t i = 0; i < vkSwapchainImageCount; i++) { |
|
|
|
|
|
VkImageView attachments[] = { |
|
|
|
|
|
vkSwapchainImageViews[i], |
|
|
|
|
|
vkDepthImageView |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
VkFramebufferCreateInfo framebufferInfo = {0}; |
|
|
|
|
|
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; |
|
|
|
|
|
framebufferInfo.renderPass = vkRenderPass; |
|
|
|
|
|
framebufferInfo.attachmentCount = sizeof(attachments) / sizeof(VkImageView); |
|
|
|
|
|
framebufferInfo.pAttachments = attachments; |
|
|
|
|
|
framebufferInfo.width = vkSwapchainExtent.width; |
|
|
|
|
|
framebufferInfo.height = vkSwapchainExtent.height; |
|
|
|
|
|
framebufferInfo.layers = 1; |
|
|
|
|
|
|
|
|
|
|
|
result = vkCreateFramebuffer(vkDevice, &framebufferInfo, NULL, &vkFramebuffers[i]); |
|
|
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to create framebuffer %u (Error: %i)", i, result); |
|
|
|
|
|
// Perform cleanup... |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Framebuffers created successfully."); |
|
|
|
|
|
|
|
|
|
|
|
// --- Command Pool and Command Buffers (Step 9) --- |
|
|
|
|
|
VkCommandPoolCreateInfo poolInfo = {0}; |
|
|
|
|
|
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; |
|
|
|
|
|
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; |
|
|
|
|
|
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; |
|
|
|
|
|
|
|
|
|
|
|
result = vkCreateCommandPool(vkDevice, &poolInfo, NULL, &vkCommandPool); |
|
|
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to create command pool (Error: %i)", result); |
|
|
|
|
|
// Perform cleanup... |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Command pool created successfully."); |
|
|
|
|
|
|
|
|
|
|
|
vkCommandBuffers = (VkCommandBuffer*)RL_MALLOC(vkSwapchainImageCount * sizeof(VkCommandBuffer)); |
|
|
|
|
|
if(!vkCommandBuffers) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to allocate memory for command buffers."); |
|
|
|
|
|
// Perform cleanup... |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
VkCommandBufferAllocateInfo cmdAllocInfo = {0}; |
|
|
|
|
|
cmdAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; |
|
|
|
|
|
cmdAllocInfo.commandPool = vkCommandPool; |
|
|
|
|
|
cmdAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
|
|
|
|
|
cmdAllocInfo.commandBufferCount = vkSwapchainImageCount; // Allocate all at once |
|
|
|
|
|
|
|
|
|
|
|
result = vkAllocateCommandBuffers(vkDevice, &cmdAllocInfo, vkCommandBuffers); |
|
|
|
|
|
if (result != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to allocate command buffers (Error: %i)", result); |
|
|
|
|
|
// Perform cleanup... |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Command buffers allocated successfully."); |
|
|
|
|
|
|
|
|
|
|
|
// --- Synchronization Primitives (Step 10) --- |
|
|
|
|
|
VkSemaphoreCreateInfo semaphoreInfo = {0}; |
|
|
|
|
|
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; |
|
|
|
|
|
|
|
|
|
|
|
VkFenceCreateInfo fenceInfo = {0}; |
|
|
|
|
|
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; |
|
|
|
|
|
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; // Create fences in signaled state |
|
|
|
|
|
|
|
|
|
|
|
vkInFlightFences = (VkFence*)RL_MALLOC(vkSwapchainImageCount * sizeof(VkFence)); |
|
|
|
|
|
if (!vkInFlightFences) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to allocate memory for in-flight fences."); |
|
|
|
|
|
// Perform cleanup... |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (vkCreateSemaphore(vkDevice, &semaphoreInfo, NULL, &vkImageAvailableSemaphore) != VK_SUCCESS || |
|
|
|
|
|
vkCreateSemaphore(vkDevice, &semaphoreInfo, NULL, &vkRenderFinishedSemaphore) != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to create semaphores."); |
|
|
|
|
|
// Perform cleanup... |
|
|
|
|
|
if (vkImageAvailableSemaphore != VK_NULL_HANDLE) vkDestroySemaphore(vkDevice, vkImageAvailableSemaphore, NULL); |
|
|
|
|
|
if (vkRenderFinishedSemaphore != VK_NULL_HANDLE) vkDestroySemaphore(vkDevice, vkRenderFinishedSemaphore, NULL); |
|
|
|
|
|
RL_FREE(vkInFlightFences); vkInFlightFences = NULL; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < vkSwapchainImageCount; i++) { |
|
|
|
|
|
if (vkCreateFence(vkDevice, &fenceInfo, NULL, &vkInFlightFences[i]) != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to create fence %u.", i); |
|
|
|
|
|
// Perform cleanup for already created fences and semaphores |
|
|
|
|
|
for(uint32_t j=0; j < i; ++j) vkDestroyFence(vkDevice, vkInFlightFences[j], NULL); |
|
|
|
|
|
RL_FREE(vkInFlightFences); vkInFlightFences = NULL; |
|
|
|
|
|
vkDestroySemaphore(vkDevice, vkImageAvailableSemaphore, NULL); |
|
|
|
|
|
vkDestroySemaphore(vkDevice, vkRenderFinishedSemaphore, NULL); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Synchronization primitives created successfully."); |
|
|
|
|
|
|
|
|
|
|
|
rlvkReady = true; // All initialization steps completed successfully |
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Vulkan backend initialized successfully."); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void rlvkClose(void) { |
|
|
void rlvkClose(void) { |
|
|
printf("rlvkClose called (STUB)\n"); |
|
|
|
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Closing Vulkan backend."); |
|
|
|
|
|
|
|
|
|
|
|
if (vkDevice != VK_NULL_HANDLE) { |
|
|
|
|
|
vkDeviceWaitIdle(vkDevice); // Ensure device is idle before destroying resources |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (vkImageAvailableSemaphore != VK_NULL_HANDLE) { |
|
|
|
|
|
vkDestroySemaphore(vkDevice, vkImageAvailableSemaphore, NULL); |
|
|
|
|
|
vkImageAvailableSemaphore = VK_NULL_HANDLE; |
|
|
|
|
|
} |
|
|
|
|
|
if (vkRenderFinishedSemaphore != VK_NULL_HANDLE) { |
|
|
|
|
|
vkDestroySemaphore(vkDevice, vkRenderFinishedSemaphore, NULL); |
|
|
|
|
|
vkRenderFinishedSemaphore = VK_NULL_HANDLE; |
|
|
|
|
|
} |
|
|
|
|
|
if (vkInFlightFences != NULL) { |
|
|
|
|
|
// Use MAX_FRAMES_IN_FLIGHT or actual count used for fences if different from vkSwapchainImageCount |
|
|
|
|
|
uint32_t fenceCount = vkSwapchainImageCount; // Assuming fences per swapchain image for now |
|
|
|
|
|
for (uint32_t i = 0; i < fenceCount; i++) { |
|
|
|
|
|
if (vkInFlightFences[i] != VK_NULL_HANDLE) { |
|
|
|
|
|
vkDestroyFence(vkDevice, vkInFlightFences[i], NULL); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
RL_FREE(vkInFlightFences); |
|
|
|
|
|
vkInFlightFences = NULL; |
|
|
|
|
|
TRACELOG(LOG_DEBUG, "RLVK: Fences destroyed."); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (vkCommandPool != VK_NULL_HANDLE) { |
|
|
|
|
|
vkDestroyCommandPool(vkDevice, vkCommandPool, NULL); |
|
|
|
|
|
vkCommandPool = VK_NULL_HANDLE; |
|
|
|
|
|
TRACELOG(LOG_DEBUG, "RLVK: Command pool destroyed."); |
|
|
|
|
|
} |
|
|
|
|
|
if (vkCommandBuffers != NULL) { // Command buffers are freed with the pool |
|
|
|
|
|
RL_FREE(vkCommandBuffers); |
|
|
|
|
|
vkCommandBuffers = NULL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (vkFramebuffers != NULL) { |
|
|
|
|
|
for (uint32_t i = 0; i < vkSwapchainImageCount; i++) { |
|
|
|
|
|
if (vkFramebuffers[i] != VK_NULL_HANDLE) { |
|
|
|
|
|
vkDestroyFramebuffer(vkDevice, vkFramebuffers[i], NULL); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
RL_FREE(vkFramebuffers); |
|
|
|
|
|
vkFramebuffers = NULL; |
|
|
|
|
|
TRACELOG(LOG_DEBUG, "RLVK: Framebuffers destroyed."); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (vkDepthImageView != VK_NULL_HANDLE) { |
|
|
|
|
|
vkDestroyImageView(vkDevice, vkDepthImageView, NULL); |
|
|
|
|
|
vkDepthImageView = VK_NULL_HANDLE; |
|
|
|
|
|
} |
|
|
|
|
|
if (vkDepthImage != VK_NULL_HANDLE) { |
|
|
|
|
|
vkDestroyImage(vkDevice, vkDepthImage, NULL); |
|
|
|
|
|
vkDepthImage = VK_NULL_HANDLE; |
|
|
|
|
|
} |
|
|
|
|
|
if (vkDepthImageMemory != VK_NULL_HANDLE) { |
|
|
|
|
|
vkFreeMemory(vkDevice, vkDepthImageMemory, NULL); |
|
|
|
|
|
vkDepthImageMemory = VK_NULL_HANDLE; |
|
|
|
|
|
} |
|
|
|
|
|
TRACELOG(LOG_DEBUG, "RLVK: Depth resources destroyed."); |
|
|
|
|
|
|
|
|
|
|
|
if (vkRenderPass != VK_NULL_HANDLE) { |
|
|
|
|
|
vkDestroyRenderPass(vkDevice, vkRenderPass, NULL); |
|
|
|
|
|
vkRenderPass = VK_NULL_HANDLE; |
|
|
|
|
|
TRACELOG(LOG_DEBUG, "RLVK: Render pass destroyed."); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (vkSwapchainImageViews != NULL) { |
|
|
|
|
|
for (uint32_t i = 0; i < vkSwapchainImageCount; i++) { |
|
|
|
|
|
if (vkSwapchainImageViews[i] != VK_NULL_HANDLE) { |
|
|
|
|
|
vkDestroyImageView(vkDevice, vkSwapchainImageViews[i], NULL); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
RL_FREE(vkSwapchainImageViews); |
|
|
|
|
|
vkSwapchainImageViews = NULL; |
|
|
|
|
|
TRACELOG(LOG_DEBUG, "RLVK: Swapchain image views destroyed."); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (vkSwapchain != VK_NULL_HANDLE) { |
|
|
|
|
|
vkDestroySwapchainKHR(vkDevice, vkSwapchain, NULL); |
|
|
|
|
|
vkSwapchain = VK_NULL_HANDLE; |
|
|
|
|
|
TRACELOG(LOG_DEBUG, "RLVK: Swapchain destroyed."); |
|
|
|
|
|
} |
|
|
|
|
|
if (vkSwapchainImages != NULL) { |
|
|
|
|
|
RL_FREE(vkSwapchainImages); |
|
|
|
|
|
vkSwapchainImages = NULL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
vkGraphicsQueue = VK_NULL_HANDLE; |
|
|
|
|
|
vkPresentQueue = VK_NULL_HANDLE; |
|
|
|
|
|
if (vkDevice != VK_NULL_HANDLE) { |
|
|
|
|
|
vkDestroyDevice(vkDevice, NULL); |
|
|
|
|
|
TRACELOG(LOG_DEBUG,"RLVK: Logical device destroyed."); |
|
|
|
|
|
} |
|
|
|
|
|
vkDevice = VK_NULL_HANDLE; |
|
|
|
|
|
vkPhysicalDevice = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
|
|
|
|
vkSurface = VK_NULL_HANDLE; |
|
|
|
|
|
vkInstance = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
|
|
|
|
memset(&queueFamilyIndices, 0, sizeof(QueueFamilyIndices)); |
|
|
|
|
|
screenWidth = 0; |
|
|
|
|
|
screenHeight = 0; |
|
|
|
|
|
currentFrame = 0; // Reset frame counter |
|
|
|
|
|
|
|
|
rlvkReady = false; |
|
|
rlvkReady = false; |
|
|
TRACELOG(LOG_INFO, "RLVK: Vulkan backend closed (stubbed)."); |
|
|
|
|
|
|
|
|
TRACELOG(LOG_INFO, "RLVK: Vulkan backend resources reset."); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
bool rlvkIsReady(void) { |
|
|
bool rlvkIsReady(void) { |
|
|
@ -29,15 +786,144 @@ bool rlvkIsReady(void) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void rlvkBeginDrawing(void) { |
|
|
void rlvkBeginDrawing(void) { |
|
|
// printf("rlvkBeginDrawing called (STUB)\n"); |
|
|
|
|
|
|
|
|
if (!rlvkReady) return; |
|
|
|
|
|
|
|
|
|
|
|
// Wait for the fence of the current frame to ensure the command buffer is free to be reused |
|
|
|
|
|
// MAX_FRAMES_IN_FLIGHT should be used here instead of vkSwapchainImageCount if they can differ. |
|
|
|
|
|
// For this implementation, we assume they are the same (fences per swapchain image). |
|
|
|
|
|
VkResult fenceResult = vkWaitForFences(vkDevice, 1, &vkInFlightFences[currentFrame], VK_TRUE, UINT64_MAX); |
|
|
|
|
|
if (fenceResult != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_ERROR, "RLVK: Failed to wait for fence (Error: %i)", fenceResult); |
|
|
|
|
|
// Handle error, possibly by trying to recreate resources or exiting |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Acquire next image from swapchain |
|
|
|
|
|
VkResult acquireResult = vkAcquireNextImageKHR(vkDevice, vkSwapchain, UINT64_MAX, vkImageAvailableSemaphore, VK_NULL_HANDLE, &acquiredImageIndex); |
|
|
|
|
|
|
|
|
|
|
|
if (acquireResult == VK_ERROR_OUT_OF_DATE_KHR) { |
|
|
|
|
|
TRACELOG(LOG_WARNING, "RLVK: Swapchain out of date during vkAcquireNextImageKHR. TODO: Recreate swapchain."); |
|
|
|
|
|
// rlRecreateSwapchain(); // Placeholder for swapchain recreation logic |
|
|
|
|
|
return; // Skip rendering this frame |
|
|
|
|
|
} else if (acquireResult != VK_SUCCESS && acquireResult != VK_SUBOPTIMAL_KHR) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to acquire swapchain image (Error: %i)", acquireResult); |
|
|
|
|
|
return; // Skip rendering this frame |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Only reset the fence if we are sure we will submit work with it. |
|
|
|
|
|
// This happens after successfully acquiring an image. |
|
|
|
|
|
vkResetFences(vkDevice, 1, &vkInFlightFences[currentFrame]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Begin Command Buffer |
|
|
|
|
|
VkCommandBuffer currentCmdBuffer = vkCommandBuffers[acquiredImageIndex]; // Use command buffer corresponding to acquired image |
|
|
|
|
|
// Or use vkCommandBuffers[currentFrame] if MAX_FRAMES_IN_FLIGHT is less than swapchain image count. |
|
|
|
|
|
// For now, assume one command buffer per swapchain image. |
|
|
|
|
|
|
|
|
|
|
|
// vkResetCommandBuffer(currentCmdBuffer, 0); // Not needed if VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT is set on pool |
|
|
|
|
|
|
|
|
|
|
|
VkCommandBufferBeginInfo beginInfo = {0}; |
|
|
|
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; |
|
|
|
|
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; |
|
|
|
|
|
|
|
|
|
|
|
if (vkBeginCommandBuffer(currentCmdBuffer, &beginInfo) != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to begin command buffer."); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Begin Render Pass |
|
|
|
|
|
VkClearValue clearValues[2]; |
|
|
|
|
|
clearValues[0].color = currentClearColor; // Use the color set by rlvkClearBackground |
|
|
|
|
|
clearValues[1].depthStencil = defaultDepthStencilClear; |
|
|
|
|
|
|
|
|
|
|
|
VkRenderPassBeginInfo renderPassInfo = {0}; |
|
|
|
|
|
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; |
|
|
|
|
|
renderPassInfo.renderPass = vkRenderPass; |
|
|
|
|
|
renderPassInfo.framebuffer = vkFramebuffers[acquiredImageIndex]; |
|
|
|
|
|
renderPassInfo.renderArea.offset = (VkOffset2D){0, 0}; |
|
|
|
|
|
renderPassInfo.renderArea.extent = vkSwapchainExtent; |
|
|
|
|
|
renderPassInfo.clearValueCount = sizeof(clearValues) / sizeof(VkClearValue); |
|
|
|
|
|
renderPassInfo.pClearValues = clearValues; |
|
|
|
|
|
|
|
|
|
|
|
vkCmdBeginRenderPass(currentCmdBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); |
|
|
|
|
|
|
|
|
|
|
|
// Set Dynamic Viewport and Scissor |
|
|
|
|
|
VkViewport viewport = {0}; |
|
|
|
|
|
viewport.x = 0.0f; |
|
|
|
|
|
viewport.y = 0.0f; // Or (float)vkSwapchainExtent.height and negative height if flipping |
|
|
|
|
|
viewport.width = (float)vkSwapchainExtent.width; |
|
|
|
|
|
viewport.height = (float)vkSwapchainExtent.height; // Or -(float)vkSwapchainExtent.height if flipping |
|
|
|
|
|
viewport.minDepth = 0.0f; |
|
|
|
|
|
viewport.maxDepth = 1.0f; |
|
|
|
|
|
vkCmdSetViewport(currentCmdBuffer, 0, 1, &viewport); |
|
|
|
|
|
|
|
|
|
|
|
VkRect2D scissor = {0}; |
|
|
|
|
|
scissor.offset = (VkOffset2D){0, 0}; |
|
|
|
|
|
scissor.extent = vkSwapchainExtent; |
|
|
|
|
|
vkCmdSetScissor(currentCmdBuffer, 0, 1, &scissor); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void rlvkEndDrawing(void) { |
|
|
void rlvkEndDrawing(void) { |
|
|
// printf("rlvkEndDrawing called (STUB)\n"); |
|
|
|
|
|
|
|
|
if (!rlvkReady) return; |
|
|
|
|
|
|
|
|
|
|
|
VkCommandBuffer currentCmdBuffer = vkCommandBuffers[acquiredImageIndex]; // Or vkCommandBuffers[currentFrame] |
|
|
|
|
|
|
|
|
|
|
|
vkCmdEndRenderPass(currentCmdBuffer); |
|
|
|
|
|
if (vkEndCommandBuffer(currentCmdBuffer) != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to end command buffer."); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Submit Command Buffer |
|
|
|
|
|
VkSubmitInfo submitInfo = {0}; |
|
|
|
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
|
|
|
|
|
|
|
|
|
|
|
VkSemaphore waitSemaphores[] = {vkImageAvailableSemaphore}; |
|
|
|
|
|
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; |
|
|
|
|
|
submitInfo.waitSemaphoreCount = 1; |
|
|
|
|
|
submitInfo.pWaitSemaphores = waitSemaphores; |
|
|
|
|
|
submitInfo.pWaitDstStageMask = waitStages; |
|
|
|
|
|
submitInfo.commandBufferCount = 1; |
|
|
|
|
|
submitInfo.pCommandBuffers = ¤tCmdBuffer; |
|
|
|
|
|
VkSemaphore signalSemaphores[] = {vkRenderFinishedSemaphore}; |
|
|
|
|
|
submitInfo.signalSemaphoreCount = 1; |
|
|
|
|
|
submitInfo.pSignalSemaphores = signalSemaphores; |
|
|
|
|
|
|
|
|
|
|
|
VkResult submitResult = vkQueueSubmit(vkGraphicsQueue, 1, &submitInfo, vkInFlightFences[currentFrame]); |
|
|
|
|
|
if (submitResult != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to submit draw command buffer (Error: %i)", submitResult); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Present Image |
|
|
|
|
|
VkPresentInfoKHR presentInfo = {0}; |
|
|
|
|
|
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; |
|
|
|
|
|
presentInfo.waitSemaphoreCount = 1; |
|
|
|
|
|
presentInfo.pWaitSemaphores = signalSemaphores; |
|
|
|
|
|
VkSwapchainKHR swapchains[] = {vkSwapchain}; |
|
|
|
|
|
presentInfo.swapchainCount = 1; |
|
|
|
|
|
presentInfo.pSwapchains = swapchains; |
|
|
|
|
|
presentInfo.pImageIndices = &acquiredImageIndex; |
|
|
|
|
|
|
|
|
|
|
|
VkResult presentResult = vkQueuePresentKHR(vkPresentQueue, &presentInfo); |
|
|
|
|
|
|
|
|
|
|
|
if (presentResult == VK_ERROR_OUT_OF_DATE_KHR || presentResult == VK_SUBOPTIMAL_KHR) { |
|
|
|
|
|
TRACELOG(LOG_WARNING, "RLVK: Swapchain out of date or suboptimal during vkQueuePresentKHR. TODO: Recreate swapchain."); |
|
|
|
|
|
// rlRecreateSwapchain(); |
|
|
|
|
|
} else if (presentResult != VK_SUCCESS) { |
|
|
|
|
|
TRACELOG(LOG_FATAL, "RLVK: Failed to present swapchain image (Error: %i)", presentResult); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// MAX_FRAMES_IN_FLIGHT needs to be well defined. Assuming it's vkSwapchainImageCount for now for simplicity of fence management. |
|
|
|
|
|
// If MAX_FRAMES_IN_FLIGHT is less than vkSwapchainImageCount, the logic for fences and command buffers needs adjustment. |
|
|
|
|
|
currentFrame = (currentFrame + 1) % vkSwapchainImageCount; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void rlvkClearBackground(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { |
|
|
void rlvkClearBackground(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { |
|
|
// printf("rlvkClearBackground called (STUB) with color: %u, %u, %u, %u\n", r, g, b, a); |
|
|
|
|
|
|
|
|
currentClearColor.float32[0] = (float)r / 255.0f; |
|
|
|
|
|
currentClearColor.float32[1] = (float)g / 255.0f; |
|
|
|
|
|
currentClearColor.float32[2] = (float)b / 255.0f; |
|
|
|
|
|
currentClearColor.float32[3] = (float)a / 255.0f; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
unsigned int rlvkLoadTexture(const void *data, int width, int height, int format, int mipmaps) { |
|
|
unsigned int rlvkLoadTexture(const void *data, int width, int height, int format, int mipmaps) { |
|
|
|