Merge pull request #3 from kd7tck/feat/vulkan-backend-framework

feat: Add initial Vulkan backend framework and compile flag
This commit is contained in:
Joshua Reisenauer 2025-05-31 19:23:50 -07:00 committed by GitHub
commit 84c57c890d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 625 additions and 115 deletions

View file

@ -27,8 +27,18 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(CompilerFlags) include(CompilerFlags)
# Registers build options that are exposed to cmake # Registers build options that are exposed to cmake
option(SUPPORT_VULKAN "Enable Vulkan graphics backend" OFF)
include(CMakeOptions.txt) include(CMakeOptions.txt)
if (SUPPORT_VULKAN)
find_package(Vulkan REQUIRED)
if (NOT Vulkan_FOUND)
message(FATAL_ERROR "Vulkan SDK not found, required for SUPPORT_VULKAN option.")
else()
message(STATUS "Vulkan SDK found: Headers at ${Vulkan_INCLUDE_DIRS}, Libraries at ${Vulkan_LIBRARIES}")
endif()
endif()
if (UNIX AND NOT APPLE AND NOT "${PLATFORM}" MATCHES "DRM") if (UNIX AND NOT APPLE AND NOT "${PLATFORM}" MATCHES "DRM")
if (NOT GLFW_BUILD_WAYLAND AND NOT GLFW_BUILD_X11) if (NOT GLFW_BUILD_WAYLAND AND NOT GLFW_BUILD_X11)
MESSAGE(FATAL_ERROR "Cannot disable both Wayland and X11") MESSAGE(FATAL_ERROR "Cannot disable both Wayland and X11")

View file

@ -1,7 +1,14 @@
target_compile_definitions("raylib" PUBLIC "CF_VULKAN_=0")
# Adding compile definitions # Adding compile definitions
target_compile_definitions("raylib" PUBLIC "${PLATFORM_CPP}") target_compile_definitions("raylib" PUBLIC "${PLATFORM_CPP}")
target_compile_definitions("raylib" PUBLIC "${GRAPHICS}") target_compile_definitions("raylib" PUBLIC "${GRAPHICS}")
if (SUPPORT_VULKAN AND Vulkan_FOUND)
target_compile_definitions("raylib" PUBLIC "CF_VULKAN_=1")
target_compile_definitions("raylib" PUBLIC "GRAPHICS_API_VULKAN")
message(STATUS "Vulkan backend enabled via CF_VULKAN_ and GRAPHICS_API_VULKAN")
endif()
function(define_if target variable) function(define_if target variable)
if(${${variable}}) if(${${variable}})
message(STATUS "${variable}=${${variable}}") message(STATUS "${variable}=${${variable}}")

View file

@ -7,6 +7,13 @@ if(POLICY CMP0072)
cmake_policy(SET CMP0072 NEW) cmake_policy(SET CMP0072 NEW)
endif() endif()
if (SUPPORT_VULKAN AND Vulkan_FOUND)
set(GRAPHICS "GRAPHICS_API_VULKAN")
message(STATUS "Vulkan graphics API selected. GRAPHICS set to GRAPHICS_API_VULKAN.")
# Any Vulkan-specific LIBS_PRIVATE additions can be handled here or in src/CMakeLists.txt
# For now, assuming Vulkan::Vulkan and Vulkan_LIBRARIES cover necessary linking.
else()
# ORIGINAL OPENGL-SPECIFIC LOGIC
if (${PLATFORM} MATCHES "Desktop") if (${PLATFORM} MATCHES "Desktop")
set(PLATFORM_CPP "PLATFORM_DESKTOP") set(PLATFORM_CPP "PLATFORM_DESKTOP")
@ -104,7 +111,11 @@ elseif ("${PLATFORM}" MATCHES "SDL")
find_package(SDL2 REQUIRED) find_package(SDL2 REQUIRED)
set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL") set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL")
set(LIBS_PRIVATE SDL2::SDL2) set(LIBS_PRIVATE SDL2::SDL2)
# For SDL, if it's used with OpenGL, ensure GRAPHICS is set appropriately if not already.
# This might need adjustment if SDL can also host Vulkan. For now, assuming SDL is for OpenGL.
if(NOT GRAPHICS)
set(GRAPHICS "GRAPHICS_API_OPENGL_33") # Default for SDL on desktop, or could be ES for other platforms
endif()
endif () endif ()
if (NOT ${OPENGL_VERSION} MATCHES "OFF") if (NOT ${OPENGL_VERSION} MATCHES "OFF")
@ -129,11 +140,16 @@ if (NOT ${OPENGL_VERSION} MATCHES "OFF")
endif () endif ()
if (NOT GRAPHICS) if (NOT GRAPHICS)
set(GRAPHICS "GRAPHICS_API_OPENGL_33") set(GRAPHICS "GRAPHICS_API_OPENGL_33") # Default OpenGL version if nothing else set it
endif () endif ()
endif() # End of SUPPORT_VULKAN AND Vulkan_FOUND conditional
# Universal appends to LIBS_PRIVATE
set(LIBS_PRIVATE ${LIBS_PRIVATE} ${OPENAL_LIBRARY}) set(LIBS_PRIVATE ${LIBS_PRIVATE} ${OPENAL_LIBRARY})
if (${PLATFORM} MATCHES "Desktop") if (${PLATFORM} MATCHES "Desktop")
# This implies glfw is used for both Vulkan and OpenGL on Desktop.
# If GLFW is only for OpenGL, this should be in the else block.
# Given rcore_vulkan_glfw.c, it's likely needed for Vulkan too.
set(LIBS_PRIVATE ${LIBS_PRIVATE} glfw) set(LIBS_PRIVATE ${LIBS_PRIVATE} glfw)
endif () endif ()

View file

@ -39,6 +39,11 @@ set(raylib_sources
utils.c utils.c
) )
if (SUPPORT_VULKAN AND Vulkan_FOUND)
list(APPEND raylib_sources rlvk.c)
message(STATUS "Vulkan source (rlvk.c) added to compilation.")
endif()
# <root>/cmake/GlfwImport.cmake handles the details around the inclusion of glfw # <root>/cmake/GlfwImport.cmake handles the details around the inclusion of glfw
if (NOT ${PLATFORM} MATCHES "Web") if (NOT ${PLATFORM} MATCHES "Web")
include(GlfwImport) include(GlfwImport)
@ -49,6 +54,24 @@ endif ()
# Produces a variable LIBS_PRIVATE that will be used later # Produces a variable LIBS_PRIVATE that will be used later
include(LibraryConfigurations) include(LibraryConfigurations)
if (SUPPORT_VULKAN AND Vulkan_FOUND)
if(TARGET Vulkan::Vulkan)
target_link_libraries(raylib PUBLIC Vulkan::Vulkan)
message(STATUS "Linking raylib with Vulkan::Vulkan target.")
elseif(Vulkan_LIBRARIES)
# For older CMake or non-imported target setups for Vulkan
target_link_libraries(raylib PUBLIC ${Vulkan_LIBRARIES})
message(STATUS "Linking raylib with Vulkan libraries: ${Vulkan_LIBRARIES}")
else()
message(WARNING "Vulkan support is enabled, but no Vulkan link target or libraries (Vulkan::Vulkan or Vulkan_LIBRARIES) were found/specified by find_package(Vulkan).")
endif()
# Ensure Vulkan include directories are available to the raylib target
if (Vulkan_INCLUDE_DIRS)
target_include_directories(raylib PUBLIC $<BUILD_INTERFACE:${Vulkan_INCLUDE_DIRS}>)
message(STATUS "Adding Vulkan include directories: ${Vulkan_INCLUDE_DIRS}")
endif()
endif()
if (SUPPORT_MODULE_RAUDIO) if (SUPPORT_MODULE_RAUDIO)
MESSAGE(STATUS "Audio Backend: miniaudio") MESSAGE(STATUS "Audio Backend: miniaudio")
else () else ()

View file

@ -54,6 +54,25 @@
#include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management #include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management
// NOTE: GLFW3 already includes gl.h (OpenGL) headers // NOTE: GLFW3 already includes gl.h (OpenGL) headers
#if defined(GRAPHICS_API_VULKAN)
#if defined(__APPLE__)
// MoltenVK requires a specific include order and defines
#define VK_USE_PLATFORM_MACOS_MVK
#define VK_USE_PLATFORM_METAL_EXT // If using newer MoltenVK/GLFW with direct Metal layer access
#elif defined(_WIN32)
#define VK_USE_PLATFORM_WIN32_KHR
#else // Assuming X11 or Wayland for other Linux/Unix via GLFW
// GLFW handles these includes mostly, but define for clarity if needed by MoltenVK headers
#if defined(GLFW_EXPOSE_NATIVE_X11) // This define might be set by glfw3native.h later
#define VK_USE_PLATFORM_XLIB_KHR
#endif
#if defined(GLFW_EXPOSE_NATIVE_WAYLAND) // This define might be set by glfw3native.h later
#define VK_USE_PLATFORM_WAYLAND_KHR
#endif
#endif
#include <vulkan/vulkan.h>
#endif
// Support retrieving native window handlers // Support retrieving native window handlers
#if defined(_WIN32) #if defined(_WIN32)
typedef void *PVOID; typedef void *PVOID;
@ -108,6 +127,12 @@ static PlatformData platform = { 0 }; // Platform specific data
int InitPlatform(void); // Initialize platform (graphics, inputs and more) int InitPlatform(void); // Initialize platform (graphics, inputs and more)
void ClosePlatform(void); // Close platform void ClosePlatform(void); // Close platform
#if defined(GRAPHICS_API_VULKAN)
// Vulkan specific platform functions
static int InitPlatformVulkan(void);
static void ClosePlatformVulkan(void);
#endif
// Error callback event // Error callback event
static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error
@ -1317,6 +1342,181 @@ static void DeallocateWrapper(void* block, void* user)
} }
// Initialize platform: graphics, inputs and more // Initialize platform: graphics, inputs and more
#if defined(GRAPHICS_API_VULKAN)
// Initialize platform for Vulkan (GLFW STUB)
static int InitPlatformVulkan(void) {
TRACELOG(LOG_INFO, "PLATFORM: Initializing platform for Vulkan (GLFW STUB)");
glfwSetErrorCallback(ErrorCallback);
const GLFWallocator allocator = {
.allocate = AllocateWrapper,
.deallocate = DeallocateWrapper,
.reallocate = ReallocateWrapper,
.user = NULL,
};
glfwInitAllocator(&allocator);
#if defined(__APPLE__)
glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE);
#endif
if (!glfwInit()) {
TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize GLFW for Vulkan");
return RL_FALSE;
}
if (!glfwVulkanSupported()) {
TRACELOG(LOG_FATAL, "PLATFORM: Vulkan not supported by GLFW or system drivers");
glfwTerminate();
return RL_FALSE;
}
TRACELOG(LOG_INFO, "PLATFORM: GLFW Vulkan support detected.");
glfwDefaultWindowHints();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // Critical for Vulkan
// Common window hints from InitPlatform()
if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);
if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
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_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);
else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE);
glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); // Keep old behavior for now
if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) {
glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
#if defined(__APPLE__)
glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE);
#endif
} else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
// MSAA hint is not relevant for Vulkan in this context
// Determine monitor for fullscreen or initial placement
GLFWmonitor *monitor = NULL;
if (CORE.Window.fullscreen) {
monitor = glfwGetPrimaryMonitor();
if (!monitor) {
TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor for Vulkan fullscreen");
glfwTerminate();
return RL_FALSE;
}
SetDimensionsFromMonitor(monitor);
} else {
// For windowed mode, get current monitor to center on later
// This part is tricky because GetCurrentMonitor() needs a window handle.
// We'll get primary and center on that if no better option before window creation.
monitor = glfwGetPrimaryMonitor(); // Default to primary for initial setup
if (monitor) SetDimensionsFromMonitor(monitor);
else { TRACELOG(LOG_WARNING, "GLFW: Could not get primary monitor for initial Vulkan window setup."); }
}
// Create window
int creationWidth = (CORE.Window.screen.width > 0) ? CORE.Window.screen.width : 640;
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) {
TRACELOG(LOG_FATAL, "PLATFORM: Failed to create GLFW window for Vulkan");
glfwTerminate();
return RL_FALSE;
}
TRACELOG(LOG_INFO, "PLATFORM: GLFW window created for Vulkan");
vkInstanceHandle = (VkInstance)0x1;
vkSurfaceHandle = (VkSurfaceKHR)0x1;
TRACELOG(LOG_INFO, "PLATFORM: VkInstance and VkSurfaceKHR STUBBED as non-null for rlvkInit testing.");
// Setup GLFW callbacks
glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback);
glfwSetWindowPosCallback(platform.handle, WindowPosCallback);
glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback);
glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback);
glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
glfwSetDropCallback(platform.handle, WindowDropCallback);
if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) {
glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
}
glfwSetKeyCallback(platform.handle, KeyCallback);
glfwSetCharCallback(platform.handle, CharCallback);
glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback);
glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback);
glfwSetScrollCallback(platform.handle, MouseScrollCallback);
glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback);
glfwSetJoystickCallback(JoystickCallback);
glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE);
int fbWidth = CORE.Window.screen.width; // Fallback to screen width
int fbHeight = CORE.Window.screen.height; // Fallback to screen height
// For Vulkan with GLFW_NO_API, glfwGetFramebufferSize might still be relevant for HighDPI scenarios
// where window coords and pixel coords differ.
glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight);
CORE.Window.render.width = fbWidth;
CORE.Window.render.height = fbHeight;
CORE.Window.currentFbo.width = fbWidth;
CORE.Window.currentFbo.height = fbHeight;
if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) {
#if !defined(__APPLE__)
if (CORE.Window.screen.width > 0 && CORE.Window.screen.height > 0) { // Avoid division by zero
CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f);
SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight);
}
#endif
}
if (!CORE.Window.fullscreen && monitor) { // monitor might be null if primary couldn't be fetched
int monitorX = 0, monitorY = 0;
glfwGetMonitorPos(monitor, &monitorX, &monitorY);
int areaWidth = 0, areaHeight = 0;
glfwGetMonitorWorkarea(monitor, NULL, NULL, &areaWidth, &areaHeight);
int posX = monitorX + (areaWidth - CORE.Window.render.width)/2;
int posY = monitorY + (areaHeight - CORE.Window.render.height)/2;
if (posX < monitorX) posX = monitorX;
if (posY < monitorY) posY = monitorY;
SetWindowPosition(posX, posY);
}
CORE.Window.ready = RL_TRUE;
TRACELOG(LOG_INFO, "PLATFORM: Vulkan platform initialized successfully (GLFW STUBBED VkInstance/Surface)");
InitTimer();
CORE.Storage.basePath = GetWorkingDirectory();
return RL_TRUE;
}
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
if (CORE.Window.handle != NULL) {
glfwDestroyWindow(CORE.Window.handle);
CORE.Window.handle = NULL;
platform.handle = NULL;
}
glfwTerminate();
TRACELOG(LOG_INFO, "PLATFORM: Vulkan platform resources closed (GLFW STUBBED VkInstance/Surface)");
}
#endif // GRAPHICS_API_VULKAN
int InitPlatform(void) int InitPlatform(void)
{ {
glfwSetErrorCallback(ErrorCallback); glfwSetErrorCallback(ErrorCallback);

View file

@ -123,6 +123,16 @@
#define RAYMATH_IMPLEMENTATION #define RAYMATH_IMPLEMENTATION
#include "raymath.h" // Vector2, Vector3, Quaternion and Matrix functionality #include "raymath.h" // Vector2, Vector3, Quaternion and Matrix functionality
#if defined(GRAPHICS_API_VULKAN)
#include "rlvk.h" // If rlvk functions are called directly from rcore
// It's also possible that rlgl.h handles the dispatch via its own functions.
// The plan suggests InitPlatformVulkan will provide instance/surface to rlvkInit.
// Global/static VkInstance and VkSurfaceKHR to be set by platform layer for rlvkInit.
// This is a temporary approach for the subtask.
VkInstance vkInstanceHandle = VK_NULL_HANDLE;
VkSurfaceKHR vkSurfaceHandle = VK_NULL_HANDLE;
#endif
#if defined(SUPPORT_GESTURES_SYSTEM) #if defined(SUPPORT_GESTURES_SYSTEM)
#define RGESTURES_IMPLEMENTATION #define RGESTURES_IMPLEMENTATION
#include "rgestures.h" // Gestures detection functionality #include "rgestures.h" // Gestures detection functionality
@ -681,7 +691,13 @@ void InitWindow(int width, int height, const char *title)
// Initialize platform // Initialize platform
//-------------------------------------------------------------- //--------------------------------------------------------------
//int result = InitPlatform();
#if defined(GRAPHICS_API_VULKAN)
// InitPlatformVulkan() will be created in step 6 and should set vkInstanceHandle and vkSurfaceHandle
int result = InitPlatformVulkan();
#else
int result = InitPlatform(); int result = InitPlatform();
#endif
if (result != 0) if (result != 0)
{ {
@ -690,10 +706,39 @@ void InitWindow(int width, int height, const char *title)
} }
//-------------------------------------------------------------- //--------------------------------------------------------------
#if defined(GRAPHICS_API_VULKAN)
if (CORE.Window.ready) { // Assuming InitPlatformVulkan sets CORE.Window.ready on success
rlvkInit(vkInstanceHandle, vkSurfaceHandle, CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
if (rlvkIsReady()) {
isGpuReady = true;
TRACELOG(LOG_INFO, "RCORE: Vulkan backend initialized successfully via rlvkInit.");
} else {
TRACELOG(LOG_ERROR, "RCORE: Failed to initialize Vulkan backend via rlvkInit.");
CORE.Window.ready = false; // Ensure window is not marked as ready
// Potentially call ClosePlatformVulkan here if InitPlatformVulkan succeeded but rlvkInit failed
ClosePlatformVulkan();
return;
}
}
#endif
// Initialize rlgl default data (buffers and shaders) // Initialize rlgl default data (buffers and shaders)
// NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
isGpuReady = true; // Flag to note GPU has been initialized successfully
#if !defined(GRAPHICS_API_VULKAN)
// If not using Vulkan (i.e., using OpenGL), and rlglInit() for OpenGL has completed,
// then the GPU is considered ready.
// This line was originally after rlglInit() before Vulkan changes.
isGpuReady = true;
#endif
// If GRAPHICS_API_VULKAN is defined, isGpuReady is set (or not) earlier,
// based on the success of rlvkInit().
// We can add a final check here if really needed, but the previous logic should suffice:
// else if (!isGpuReady && defined(GRAPHICS_API_VULKAN)) {
// TRACELOG(LOG_ERROR, "RCORE: GPU not ready after Vulkan initialization sequence (final check).");
// }
// Setup default viewport // Setup default viewport
SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
@ -753,11 +798,21 @@ void CloseWindow(void)
UnloadFontDefault(); // WARNING: Module required: rtext UnloadFontDefault(); // WARNING: Module required: rtext
#endif #endif
#if defined(GRAPHICS_API_VULKAN)
if (rlvkIsReady()) { // Check if it was successfully initialized
rlvkClose();
}
#endif
rlglClose(); // De-init rlgl rlglClose(); // De-init rlgl
// De-initialize platform // De-initialize platform
//-------------------------------------------------------------- //--------------------------------------------------------------
//ClosePlatform();
#if defined(GRAPHICS_API_VULKAN)
ClosePlatformVulkan();
#else
ClosePlatform(); ClosePlatform();
#endif
//-------------------------------------------------------------- //--------------------------------------------------------------
CORE.Window.ready = false; CORE.Window.ready = false;

View file

@ -826,6 +826,10 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
#if defined(RLGL_IMPLEMENTATION) #if defined(RLGL_IMPLEMENTATION)
#if defined(GRAPHICS_API_VULKAN)
#include "rlvk.h" // Path relative to rcore.c or where rlgl.h implementation is
#endif
// Expose OpenGL functions from glad in raylib // Expose OpenGL functions from glad in raylib
#if defined(BUILD_LIBTYPE_SHARED) #if defined(BUILD_LIBTYPE_SHARED)
#define GLAD_API_CALL_EXPORT #define GLAD_API_CALL_EXPORT
@ -1455,6 +1459,14 @@ void rlColor4f(float x, float y, float z, float w) { glColor4f(x, y, z, w); }
// Initialize drawing mode (how to organize vertex) // Initialize drawing mode (how to organize vertex)
void rlBegin(int mode) void rlBegin(int mode)
{ {
#if defined(GRAPHICS_API_VULKAN)
// In Vulkan, rlBegin equivalent would be part of starting a command buffer or render pass.
// For immediate mode emulation, this might involve setting up for a new batch.
// rlvkBeginDrawing() is the target. The 'mode' parameter might be used by rlvk to set pipeline state.
rlvkBeginDrawing();
// The original logic for rlBegin in OpenGL was to manage batching and draw call generation
// based on mode changes. Vulkan path might do similar for its own batching or just rely on rlvk.
#else
// Draw mode can be RL_LINES, RL_TRIANGLES and RL_QUADS // Draw mode can be RL_LINES, RL_TRIANGLES and RL_QUADS
// NOTE: In all three cases, vertex are accumulated over default internal vertex buffer // NOTE: In all three cases, vertex are accumulated over default internal vertex buffer
if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode != mode) if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode != mode)
@ -1483,15 +1495,22 @@ void rlBegin(int mode)
RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount = 0; RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount = 0;
RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = RLGL.State.defaultTextureId; RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = RLGL.State.defaultTextureId;
} }
#endif // GRAPHICS_API_VULKAN
} }
// Finish vertex providing // Finish vertex providing
void rlEnd(void) void rlEnd(void)
{ {
#if defined(GRAPHICS_API_VULKAN)
// In Vulkan, rlEnd equivalent would be part of ending a command buffer or render pass.
// rlvkEndDrawing() is the target.
rlvkEndDrawing();
#else
// NOTE: Depth increment is dependant on rlOrtho(): z-near and z-far values, // NOTE: Depth increment is dependant on rlOrtho(): z-near and z-far values,
// as well as depth buffer bit-depth (16bit or 24bit or 32bit) // as well as depth buffer bit-depth (16bit or 24bit or 32bit)
// Correct increment formula would be: depthInc = (zfar - znear)/pow(2, bits) // Correct increment formula would be: depthInc = (zfar - znear)/pow(2, bits)
RLGL.currentBatch->currentDepth += (1.0f/20000.0f); RLGL.currentBatch->currentDepth += (1.0f/20000.0f);
#endif // GRAPHICS_API_VULKAN
} }
// Define one vertex (position) // Define one vertex (position)
@ -2050,6 +2069,9 @@ bool rlIsStereoRenderEnabled(void)
// Clear color buffer with color // Clear color buffer with color
void rlClearColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a) void rlClearColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{ {
#if defined(GRAPHICS_API_VULKAN)
rlvkClearBackground(r, g, b, a); // Call the Vulkan equivalent
#else
// Color values clamp to 0.0f(0) and 1.0f(255) // Color values clamp to 0.0f(0) and 1.0f(255)
float cr = (float)r/255; float cr = (float)r/255;
float cg = (float)g/255; float cg = (float)g/255;
@ -2057,6 +2079,7 @@ void rlClearColor(unsigned char r, unsigned char g, unsigned char b, unsigned ch
float ca = (float)a/255; float ca = (float)a/255;
glClearColor(cr, cg, cb, ca); glClearColor(cr, cg, cb, ca);
#endif
} }
// Clear used screen buffers (color and depth) // Clear used screen buffers (color and depth)
@ -2233,6 +2256,43 @@ static void GLAPIENTRY rlDebugMessageCallback(GLenum source, GLenum type, GLuint
// Initialize rlgl: OpenGL extensions, default buffers/shaders/textures, OpenGL states // Initialize rlgl: OpenGL extensions, default buffers/shaders/textures, OpenGL states
void rlglInit(int width, int height) void rlglInit(int width, int height)
{ {
#if defined(GRAPHICS_API_VULKAN)
// Most Vulkan initialization is platform and rlvk specific.
// rlgl itself might initialize shared data structures here if any.
// For now, rlvkInit() is expected to be called from rcore.c's InitWindow.
// Load default batch for Vulkan if its structure is generic enough,
// or rlvk will handle its own batching system.
// The plan says: "Initialize rlgl default data (buffers and shaders)"
// This might mean setting up RLGL.State for Vulkan mode.
// Init default white texture (Vulkan specific)
// unsigned char pixels[4] = { 255, 255, 255, 255 };
// RLGL.State.defaultTextureId = rlvkLoadTexture(pixels, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
// if (RLGL.State.defaultTextureId != 0) TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Default texture loaded successfully (Vulkan)", RLGL.State.defaultTextureId);
// else TRACELOG(LOG_WARNING, "TEXTURE: Failed to load default texture (Vulkan)");
// Load default shader (Vulkan specific)
// rlLoadShaderDefault(); // This needs to be Vulkan aware
// RLGL.State.currentShaderId = RLGL.State.defaultShaderId;
// RLGL.State.currentShaderLocs = RLGL.State.defaultShaderLocs;
// For now, assume rlvkInit handles this, and rcore.c calls it.
// rlglInit under Vulkan might just set some state flags.
TRACELOG(LOG_INFO, "RLGL: rlglInit called for Vulkan. Expecting rlvkInit to be called from rcore.");
RLGL.State.currentMatrixMode = RL_MODELVIEW;
RLGL.State.currentMatrix = &RLGL.State.modelview;
RLGL.State.modelview = rlMatrixIdentity();
RLGL.State.projection = rlMatrixIdentity();
RLGL.State.transform = rlMatrixIdentity();
// Other RLGL.State initializations as needed.
// Store screen size into global variables
RLGL.State.framebufferWidth = width;
RLGL.State.framebufferHeight = height;
TRACELOG(RL_LOG_INFO, "RLGL: Default Vulkan state initialized successfully"); // This is a bit optimistic, more setup needed in rlvk
#else // This means !defined(GRAPHICS_API_VULKAN)
// Enable OpenGL debug context if required // Enable OpenGL debug context if required
#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43) #if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43)
if ((glDebugMessageCallback != NULL) && (glDebugMessageControl != NULL)) if ((glDebugMessageCallback != NULL) && (glDebugMessageControl != NULL))
@ -2319,11 +2379,18 @@ void rlglInit(int width, int height)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color (black) glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color (black)
glClearDepth(1.0f); // Set clear depth value (default) glClearDepth(1.0f); // Set clear depth value (default)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers (depth buffer required for 3D) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers (depth buffer required for 3D)
#endif // GRAPHICS_API_VULKAN defined check
} }
// Vertex Buffer Object deinitialization (memory free) // Vertex Buffer Object deinitialization (memory free)
void rlglClose(void) void rlglClose(void)
{ {
#if defined(GRAPHICS_API_VULKAN)
// rlvkClose(); // This will be called from rcore.c's CloseWindow
TRACELOG(LOG_INFO, "RLGL: rlglClose called for Vulkan. Expecting rlvkClose to be called from rcore.");
// Unload default batch if managed by rlgl for vulkan
// rlUnloadRenderBatch(RLGL.defaultBatch); // This needs to be Vulkan aware or not done here if rlvk handles it
#else // !defined(GRAPHICS_API_VULKAN)
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
rlUnloadRenderBatch(RLGL.defaultBatch); rlUnloadRenderBatch(RLGL.defaultBatch);
@ -2332,6 +2399,7 @@ void rlglClose(void)
glDeleteTextures(1, &RLGL.State.defaultTextureId); // Unload default texture glDeleteTextures(1, &RLGL.State.defaultTextureId); // Unload default texture
TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture unloaded successfully", RLGL.State.defaultTextureId); TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture unloaded successfully", RLGL.State.defaultTextureId);
#endif #endif
#endif // GRAPHICS_API_VULKAN defined check
} }
// Load OpenGL extensions // Load OpenGL extensions
@ -3173,6 +3241,11 @@ bool rlCheckRenderBatchLimit(int vCount)
// Convert image data to OpenGL texture (returns OpenGL valid Id) // Convert image data to OpenGL texture (returns OpenGL valid Id)
unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount) unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount)
{ {
#if defined(GRAPHICS_API_VULKAN)
// return rlvkLoadTexture(data, width, height, format, mipmapCount); // Actual call to Vulkan backend
TRACELOG(LOG_DEBUG, "RLGL: rlLoadTexture called (Vulkan path - using stub)");
return 0; // Placeholder for Vulkan
#else
unsigned int id = 0; unsigned int id = 0;
glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding
@ -3322,6 +3395,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format,
else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to load texture"); else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to load texture");
return id; return id;
#endif // GRAPHICS_API_VULKAN
} }
// Load depth texture/renderbuffer (to be attached to fbo) // Load depth texture/renderbuffer (to be attached to fbo)
@ -4064,6 +4138,11 @@ void rlUnloadVertexBuffer(unsigned int vboId)
// NOTE: If shader string is NULL, using default vertex/fragment shaders // NOTE: If shader string is NULL, using default vertex/fragment shaders
unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode) unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode)
{ {
#if defined(GRAPHICS_API_VULKAN)
// return rlvkLoadShaderCode(vsCode, fsCode); // Actual call to Vulkan backend
TRACELOG(LOG_DEBUG, "RLGL: rlLoadShaderCode called (Vulkan path - using stub)");
return 0; // Placeholder for Vulkan
#else
unsigned int id = 0; unsigned int id = 0;
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
@ -4136,6 +4215,7 @@ unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode)
#endif #endif
return id; return id;
#endif // GRAPHICS_API_VULKAN
} }
// Compile custom shader and return shader id // Compile custom shader and return shader id
@ -4306,6 +4386,11 @@ int rlGetLocationAttrib(unsigned int shaderId, const char *attribName)
// Set shader value uniform // Set shader value uniform
void rlSetUniform(int locIndex, const void *value, int uniformType, int count) void rlSetUniform(int locIndex, const void *value, int uniformType, int count)
{ {
#if defined(GRAPHICS_API_VULKAN)
// rlvkSetUniform(locIndex, value, uniformType, count); // Actual call to Vulkan backend
TRACELOG(LOG_DEBUG, "RLGL: rlSetUniform called (Vulkan path - using stub for locIndex %d)", locIndex);
// No return value, placeholder action is just logging.
#else
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
switch (uniformType) switch (uniformType)
{ {
@ -4329,6 +4414,7 @@ void rlSetUniform(int locIndex, const void *value, int uniformType, int count)
// TODO: Support glUniform1uiv(), glUniform2uiv(), glUniform3uiv(), glUniform4uiv() // TODO: Support glUniform1uiv(), glUniform2uiv(), glUniform3uiv(), glUniform4uiv()
} }
#endif #endif
#endif // GRAPHICS_API_VULKAN
} }
// Set shader value attribute // Set shader value attribute

77
src/rlvk.c Normal file
View file

@ -0,0 +1,77 @@
#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
// Global or static variables for Vulkan state (minimal for stubs)
static bool rlvkReady = false;
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).");
} else {
TRACELOG(LOG_ERROR, "RLVK: Failed to initialize Vulkan backend due to null instance or surface (stubbed).");
rlvkReady = false;
}
}
void rlvkClose(void) {
printf("rlvkClose called (STUB)\n");
rlvkReady = false;
TRACELOG(LOG_INFO, "RLVK: Vulkan backend closed (stubbed).");
}
bool rlvkIsReady(void) {
return rlvkReady;
}
void rlvkBeginDrawing(void) {
// printf("rlvkBeginDrawing called (STUB)\n");
}
void rlvkEndDrawing(void) {
// printf("rlvkEndDrawing called (STUB)\n");
}
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);
}
unsigned int rlvkLoadTexture(const void *data, int width, int height, int format, int mipmaps) {
printf("rlvkLoadTexture called (STUB)\n");
return 0; // Return a dummy ID
}
void rlvkUnloadTexture(unsigned int id) {
printf("rlvkUnloadTexture called (STUB) for ID: %u\n", id);
}
unsigned int rlvkLoadShaderCode(const char *vsCode, const char *fsCode) {
printf("rlvkLoadShaderCode called (STUB)\n");
return 0; // Return a dummy ID
}
void rlvkUnloadShaderProgram(unsigned int id) {
printf("rlvkUnloadShaderProgram called (STUB) for ID: %u\n", id);
}
int rlvkGetLocationUniform(unsigned int shaderId, const char *uniformName) {
// printf("rlvkGetLocationUniform called (STUB) for shader ID: %u, uniform: %s\n", shaderId, uniformName);
return -1;
}
int rlvkGetLocationAttrib(unsigned int shaderId, const char *attribName) {
// printf("rlvkGetLocationAttrib called (STUB) for shader ID: %u, attrib: %s\n", shaderId, attribName);
return -1;
}
void rlvkSetUniform(int locIndex, const void *value, int uniformType, int count) {
// printf("rlvkSetUniform called (STUB) for locIndex: %d\n", locIndex);
}
// ... other rlgl equivalent function stub implementations ...
// TRACELOG can be used for more detailed stub logging if utils.h is appropriately included and linked.
// For now, simple printf might be fine for basic stub verification.

36
src/rlvk.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef RLVK_H
#define RLVK_H
#include <vulkan/vulkan.h>
#ifdef __cplusplus
extern "C" {
#endif
// Initialization and Configuration
void rlvkInit(VkInstance instance, VkSurfaceKHR surface, int width, int height); // Simplified, may need more params
void rlvkClose(void);
bool rlvkIsReady(void);
// Drawing
void rlvkBeginDrawing(void);
void rlvkEndDrawing(void);
void rlvkClearBackground(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
// Basic Texture Management (Stubs)
unsigned int rlvkLoadTexture(const void *data, int width, int height, int format, int mipmaps);
void rlvkUnloadTexture(unsigned int id);
// Basic Shader Management (Stubs)
unsigned int rlvkLoadShaderCode(const char *vsCode, const char *fsCode);
void rlvkUnloadShaderProgram(unsigned int id);
int rlvkGetLocationUniform(unsigned int shaderId, const char *uniformName);
int rlvkGetLocationAttrib(unsigned int shaderId, const char *attribName);
void rlvkSetUniform(int locIndex, const void *value, int uniformType, int count);
// ... other rlgl equivalent function declarations as stubs ...
#ifdef __cplusplus
}
#endif
#endif // RLVK_H