Merge pull request #1 from kd7tck/vulkan-upgrade-plan-update
Vulkan upgrade plan update
This commit is contained in:
commit
b1c19935c3
1 changed files with 436 additions and 0 deletions
436
vulkan-upgrade-plan
Normal file
436
vulkan-upgrade-plan
Normal file
|
@ -0,0 +1,436 @@
|
|||
# Raylib Vulkan Upgrade Plan
|
||||
|
||||
This document outlines the strategy for integrating Vulkan as a compile-time graphics backend option for raylib. The goal is to maintain the existing raylib API and ease of use while providing an alternative, modern graphics API.
|
||||
|
||||
## 1. Define Vulkan API Abstraction Layer (`rlvk`)
|
||||
|
||||
A new abstraction layer, similar to `rlgl` for OpenGL, will be created for Vulkan. This layer, tentatively named `rlvk`, will reside in `src/rlvk.h` and `src/rlvk.c`.
|
||||
|
||||
**Key Responsibilities of `rlvk`:**
|
||||
|
||||
* **Initialization & Deinitialization:**
|
||||
* `rlvkInit()`:
|
||||
* Initialize Vulkan loader.
|
||||
* Create Vulkan instance (`vkCreateInstance`).
|
||||
* Setup debug messenger (if `RLGL_ENABLE_OPENGL_DEBUG_CONTEXT` or a new Vulkan equivalent is defined).
|
||||
* Select a suitable physical device (`vkEnumeratePhysicalDevices`, `vkGetPhysicalDeviceProperties`, `vkGetPhysicalDeviceFeatures`, `vkGetPhysicalDeviceQueueFamilyProperties`).
|
||||
* Create a logical device (`vkCreateDevice`) with necessary queues (graphics, present).
|
||||
* Create a Vulkan surface using the platform layer (e.g., via GLFW's `glfwCreateWindowSurface`).
|
||||
* Create the swapchain (`vkCreateSwapchainKHR`).
|
||||
* Create image views for swapchain images (`vkCreateImageView`).
|
||||
* Create a default render pass (`vkCreateRenderPass`).
|
||||
* Create framebuffers for each swapchain image view, associating them with the render pass (`vkCreateFramebuffer`).
|
||||
* Create a command pool (`vkCreateCommandPool`).
|
||||
* Allocate primary command buffers (`vkAllocateCommandBuffers`).
|
||||
* Create synchronization primitives (semaphores for image available/render finished, fences for command buffer execution) (`vkCreateSemaphore`, `vkCreateFence`).
|
||||
* Initialize default resources (e.g., default white texture, default shaders in SPIR-V).
|
||||
* Setup default pipeline state objects (PSO) for common rendering tasks (e.g., 2D textured quads, 3D models).
|
||||
* `rlvkClose()`:
|
||||
* Wait for device to be idle (`vkDeviceWaitIdle`).
|
||||
* Destroy all Vulkan resources in reverse order of creation (synchronization primitives, command buffers, command pool, framebuffers, render pass, image views, swapchain, logical device, surface, debug messenger, instance).
|
||||
|
||||
* **Core Rendering Loop Functions:**
|
||||
* `rlvkBeginDrawing()`:
|
||||
* Acquire the next available swapchain image (`vkAcquireNextImageKHR`).
|
||||
* Begin the primary command buffer (`vkBeginCommandBuffer`).
|
||||
* Begin the default render pass (`vkCmdBeginRenderPass`).
|
||||
* Set default viewport and scissor.
|
||||
* `rlvkEndDrawing()`:
|
||||
* End the render pass (`vkCmdEndRenderPass`).
|
||||
* End the command buffer (`vkEndCommandBuffer`).
|
||||
* Submit the command buffer to the graphics queue (`vkQueueSubmit`), waiting on the image available semaphore and signaling the render finished semaphore.
|
||||
* Present the rendered image to the swapchain (`vkQueuePresentKHR`), waiting on the render finished semaphore.
|
||||
|
||||
* **Matrix Operations:**
|
||||
* Maintain projection and modelview matrix stacks similar to `rlgl`.
|
||||
* `rlvkMatrixMode()`, `rlvkPushMatrix()`, `rlvkPopMatrix()`, `rlvkLoadIdentity()`, `rlvkTranslatef()`, `rlvkRotatef()`, `rlvkScalef()`, `rlvkMultMatrixf()`.
|
||||
* These will update internal matrix state, which will then be passed to shaders via UBOs/push constants.
|
||||
|
||||
* **Viewport and Clipping:**
|
||||
* `rlvkViewport()`: Set dynamic viewport state (`vkCmdSetViewport`).
|
||||
* `rlvkSetClipPlanes()`: Potentially manage via shader logic or dynamic rasterizer state if available/performant. Vulkan does not have direct equivalents to `gl_ClipDistance` in the same way as OpenGL for user-defined clip planes easily manipulated by `rlFrustum`.
|
||||
|
||||
* **Vertex Buffer Management (Batching System):**
|
||||
* Adapt or reimplement raylib's batching system (`rlRenderBatch`) for Vulkan.
|
||||
* `rlvkLoadRenderBatch()`: Create Vulkan buffers (vertex, index, staging) for batch data.
|
||||
* `rlvkUnloadRenderBatch()`: Destroy Vulkan buffers.
|
||||
* `rlvkDrawRenderBatch()`:
|
||||
* If batch data has changed, update staging buffers and record commands to copy to device-local buffers (`vkCmdCopyBuffer`).
|
||||
* Bind appropriate pipeline (PSO).
|
||||
* Bind descriptor sets (for UBOs, textures).
|
||||
* Bind vertex and index buffers (`vkCmdBindVertexBuffers`, `vkCmdBindIndexBuffer`).
|
||||
* Issue draw calls (`vkCmdDrawIndexed` or `vkCmdDraw`).
|
||||
* `rlvkSetTexture()`: Manage texture binding for the current batch, potentially requiring different descriptor sets or dynamic descriptor updates.
|
||||
|
||||
* **Immediate Mode Emulation (`rlBegin`, `rlEnd`, `rlVertex3f`, etc.):**
|
||||
* These functions will populate the CPU-side vertex buffers of the active `rlRenderBatch`.
|
||||
* `rlBegin()` will set the primitive topology for the current batch draw call.
|
||||
|
||||
* **Texture Management:**
|
||||
* `rlvkLoadTexture()`:
|
||||
* Create Vulkan image (`vkCreateImage`) with appropriate format, extent, mip levels, usage flags.
|
||||
* Allocate device memory (`vkAllocateMemory`, `vkBindImageMemory`).
|
||||
* Create image view (`vkCreateImageView`).
|
||||
* Create a default sampler (`vkCreateSampler`).
|
||||
* Transition image layout to `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL`.
|
||||
* Upload pixel data using a staging buffer and command buffer operations (`vkCmdCopyBufferToImage`).
|
||||
* `rlvkLoadTextureDepth()`: Create depth/stencil attachment.
|
||||
* `rlvkLoadTextureCubemap()`: Similar to `rlvkLoadTexture` but for cubemaps.
|
||||
* `rlvkUpdateTexture()`: Update a region of an existing texture using staging buffers.
|
||||
* `rlvkGenTextureMipmaps()`: Generate mipmaps using `vkCmdBlitImage` if supported, or require pre-generated mipmaps.
|
||||
* `rlvkUnloadTexture()`: Destroy image, image view, sampler, and free device memory.
|
||||
|
||||
* **Shader Management:**
|
||||
* `rlvkLoadShaderCode()`: Will expect SPIR-V bytecode.
|
||||
* `rlvkLoadShaderProgram()`: Create `VkShaderModule` from SPIR-V, define pipeline layouts, and potentially create initial `VkPipeline` objects.
|
||||
* `rlvkGetLocationUniform()`, `rlvkGetLocationAttrib()`: Manage mapping of uniform/attribute names to SPIR-V binding points/locations.
|
||||
* `rlvkSetUniform()`: Update UBOs or push constants.
|
||||
* `rlvkSetShader()`: Select the active pipeline and descriptor sets.
|
||||
|
||||
* **Framebuffer Management:**
|
||||
* `rlvkLoadFramebuffer()`: Create `VkFramebuffer` (distinct from swapchain framebuffers, for render textures).
|
||||
* `rlvkFramebufferAttach()`: Attach textures to custom framebuffers.
|
||||
* `rlvkFramebufferComplete()`: Check framebuffer completeness.
|
||||
|
||||
* **Render State Management:**
|
||||
* `rlvkEnableColorBlend()`, `rlvkDisableColorBlend()`, `rlvkSetBlendMode()`, `rlvkSetBlendFactors()`: Configure `VkPipelineColorBlendStateCreateInfo`.
|
||||
* `rlvkEnableDepthTest()`, `rlvkDisableDepthTest()`: Configure `VkPipelineDepthStencilStateCreateInfo`.
|
||||
* `rlvkEnableDepthMask()`, `rlvkDisableDepthMask()`: Part of `VkPipelineDepthStencilStateCreateInfo`.
|
||||
* `rlvkEnableBackfaceCulling()`, `rlvkDisableBackfaceCulling()`, `rlvkSetCullFace()`: Configure `VkPipelineRasterizationStateCreateInfo`.
|
||||
* `rlvkEnableScissorTest()`, `rlvkDisableScissorTest()`, `rlvkScissor()`: Set dynamic scissor state (`vkCmdSetScissor`).
|
||||
* `rlvkEnableWireMode()`, `rlvkDisableWireMode()`: Set `polygonMode` in `VkPipelineRasterizationStateCreateInfo`.
|
||||
|
||||
## 2. Core Library Integration (`rcore.c`)
|
||||
|
||||
* **Conditional Compilation:**
|
||||
* Use `#if defined(GRAPHICS_API_VULKAN)` to conditionally include `rlvk.h` and call `rlvk` functions.
|
||||
* Ensure only one graphics API is active (e.g., `#elif defined(GRAPHICS_API_OPENGL_XX)`).
|
||||
* **`InitWindow()` Modifications:**
|
||||
* If `GRAPHICS_API_VULKAN` is defined:
|
||||
* Call `InitPlatform()` (platform layer, e.g., GLFW, will need Vulkan-specific setup).
|
||||
* Call `rlvkInit()` instead of `rlglInit()`.
|
||||
* Set `isGpuReady = true` upon successful Vulkan initialization.
|
||||
* **`CloseWindow()` Modifications:**
|
||||
* If `GRAPHICS_API_VULKAN` is defined:
|
||||
* Call `rlvkClose()` before `ClosePlatform()`.
|
||||
* **Drawing Function Adaption:**
|
||||
* Functions like `ClearBackground()`, `BeginDrawing()`, `EndDrawing()`, `BeginMode2D()`, `EndMode2D()`, `BeginMode3D()`, `EndMode3D()`, `BeginTextureMode()`, `EndTextureMode()`, `BeginShaderMode()`, `EndShaderMode()`, etc., will need to call their `rlvk` counterparts when `GRAPHICS_API_VULKAN` is active.
|
||||
|
||||
## 3. Platform Layer Integration (Example: `rcore_desktop_glfw.c`)
|
||||
|
||||
* **Window Creation:**
|
||||
* When `GRAPHICS_API_VULKAN` is defined:
|
||||
* Call `glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);` before `glfwCreateWindow()`.
|
||||
* **Surface Creation:**
|
||||
* After window creation, if Vulkan is active, call `glfwCreateWindowSurface(vulkanInstance, windowHandle, NULL, &vulkanSurface)` to create the Vulkan rendering surface. This surface handle will be needed by `rlvkInit()`.
|
||||
* **Instance Extensions:**
|
||||
* Query required Vulkan instance extensions from GLFW using `glfwGetRequiredInstanceExtensions()` and pass them to `vkCreateInstance` in `rlvkInit()`.
|
||||
* **Input and Other Callbacks:**
|
||||
* Most input handling and window event callbacks should remain largely unchanged.
|
||||
|
||||
## 4. Build System Changes (CMake)
|
||||
|
||||
* **Add CMake Option:**
|
||||
* In the main `CMakeLists.txt` or a dedicated options file (e.g., `cmake/CMakeOptions.txt` or a new `cmake/GraphicsAPI.cmake`), add an option to select Vulkan. This could be a boolean `SUPPORT_VULKAN` or an enum-style option like `GRAPHICS_BACKEND` with values "OpenGL" and "Vulkan".
|
||||
* Example: `option(SUPPORT_VULKAN "Enable Vulkan graphics backend" OFF)`
|
||||
* **Conditional Compilation Define:**
|
||||
* Based on the CMake option, add a compile definition for `GRAPHICS_API_VULKAN` to `src/config.h` or directly to the target compile definitions.
|
||||
* Ensure mutual exclusivity with OpenGL defines (e.g., `GRAPHICS_API_OPENGL_33`).
|
||||
* Example in `src/config.h` (controlled by CMake):
|
||||
```c
|
||||
// Select desired graphics API (OpenGL or Vulkan)
|
||||
// #define GRAPHICS_API_OPENGL_11
|
||||
// #define GRAPHICS_API_OPENGL_21
|
||||
// #define GRAPHICS_API_OPENGL_33
|
||||
// #define GRAPHICS_API_OPENGL_43
|
||||
// #define GRAPHICS_API_OPENGL_ES2
|
||||
// #define GRAPHICS_API_OPENGL_ES3
|
||||
// #define GRAPHICS_API_VULKAN // This would be enabled by CMake
|
||||
```
|
||||
* **Link Vulkan Libraries:**
|
||||
* When Vulkan is enabled, use `find_package(Vulkan REQUIRED)` to locate the Vulkan SDK.
|
||||
* Link the raylib library against `Vulkan::Vulkan` (for the loader) and potentially `Vulkan::glslang` or `Vulkan::SPIRV-Tools` if shader compilation is handled by CMake.
|
||||
* **Source Files:**
|
||||
* Conditionally compile `src/rlvk.c` when Vulkan is enabled.
|
||||
* **Shader Compilation (SPIR-V):**
|
||||
* If GLSL shaders are to be compiled to SPIR-V at build time:
|
||||
* Integrate a tool like `glslangValidator` as a custom build step.
|
||||
* Define rules to find GLSL shaders (e.g., in `src/shaders/glsl/` or a new `src/shaders/vk/`) and compile them to SPIR-V, placing the output in the build directory for embedding or loading.
|
||||
|
||||
## 5. Shader Management for Vulkan
|
||||
|
||||
* **SPIR-V Requirement:** Vulkan shaders must be in SPIR-V binary format.
|
||||
* **Compilation Strategy:**
|
||||
* **Option A (Build-time compilation):** Use `glslangValidator` (or similar) integrated into the CMake build process to compile GLSL shaders to SPIR-V. These SPIR-V files can then be embedded into the executable or loaded at runtime.
|
||||
* **Option B (Pre-compiled SPIR-V):** Require users or developers to provide pre-compiled SPIR-V shaders.
|
||||
* Raylib's default shaders will need to be converted to GLSL suitable for Vulkan (e.g., using explicit binding locations) and then compiled to SPIR-V.
|
||||
* **`rlvkLoadShaderCode()`:** This function in `rlvk.c` will expect paths to SPIR-V files or raw SPIR-V bytecode if embedded.
|
||||
* **Descriptor Sets and Pipeline Layouts:**
|
||||
* `rlvk` will need to manage `VkDescriptorSetLayout`, `VkPipelineLayout`, `VkDescriptorPool`, and `VkDescriptorSet` for binding UBOs and textures to shaders. This is significantly different from OpenGL's `glGetUniformLocation`.
|
||||
|
||||
## 6. Potential Challenges and Considerations
|
||||
|
||||
* **API Mapping:** Translating raylib's simple, immediate-mode style OpenGL calls to Vulkan's more complex, explicit API requires careful design of the `rlvk` layer to hide Vulkan's verbosity.
|
||||
* **Performance:** While Vulkan offers potential performance benefits, a naive translation might not achieve them. Efficient batching, command buffer usage, and resource management will be crucial.
|
||||
* **Error Handling:** Vulkan's error reporting is robust but requires more explicit checking.
|
||||
* **Driver Differences:** Vulkan driver quality and feature support can vary.
|
||||
* **Development Complexity:** Implementing a Vulkan backend is a substantial undertaking.
|
||||
* **Shader Management:** Handling SPIR-V and descriptor sets is more involved than GLSL uniform locations.
|
||||
* **Maintaining Simplicity:** A core tenet of raylib is its ease of use. The Vulkan backend should ideally not expose Vulkan's complexity to the end-user.
|
||||
|
||||
This plan provides a high-level roadmap. Each step, especially the design and implementation of `rlvk`, involves many detailed sub-tasks.
|
||||
|
||||
## Vulkan Upgrade Plan for raylib
|
||||
|
||||
This document outlines the necessary modifications to integrate Vulkan support into the raylib library, managed by a compile-time flag `GRAPHICS_API_VULKAN`.
|
||||
|
||||
### 1. Build System (CMake)
|
||||
|
||||
**File: `CMakeLists.txt` (Root)**
|
||||
* **Add CMake Option:**
|
||||
* Introduce an option `SUPPORT_VULKAN` (default OFF) to enable Vulkan backend.
|
||||
```cmake
|
||||
option(SUPPORT_VULKAN "Enable Vulkan graphics backend" OFF)
|
||||
```
|
||||
* **Find Vulkan SDK:**
|
||||
* If `SUPPORT_VULKAN` is ON, use `find_package` to locate the Vulkan SDK.
|
||||
```cmake
|
||||
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()
|
||||
```
|
||||
|
||||
**File: `src/CMakeLists.txt`**
|
||||
* **Set Preprocessor Define:**
|
||||
* In `CompileDefinitions.cmake` (or directly in `src/CMakeLists.txt` if more appropriate), add `GRAPHICS_API_VULKAN` if `SUPPORT_VULKAN` is ON and Vulkan is found.
|
||||
```cmake
|
||||
# Inside CompileDefinitions.cmake or src/CMakeLists.txt
|
||||
if (SUPPORT_VULKAN AND Vulkan_FOUND)
|
||||
target_compile_definitions(raylib PUBLIC GRAPHICS_API_VULKAN)
|
||||
# Potentially link Vulkan libraries
|
||||
# target_link_libraries(raylib PUBLIC Vulkan::Vulkan) # Modern CMake
|
||||
# or target_link_libraries(raylib PUBLIC ${Vulkan_LIBRARIES}) # Older CMake
|
||||
endif()
|
||||
```
|
||||
* The exact linking method (`Vulkan::Vulkan` vs `Vulkan_LIBRARIES`) depends on how `FindVulkan.cmake` exports its targets. `Vulkan::Vulkan` is preferred if available.
|
||||
* **Conditionally Compile Vulkan Source Files:**
|
||||
* Add new source files for the Vulkan backend (e.g., `rlgl_vulkan.c`, `rcore_vulkan_glfw.c`) to the `raylib_sources` list conditionally.
|
||||
```cmake
|
||||
if (SUPPORT_VULKAN AND Vulkan_FOUND)
|
||||
list(APPEND raylib_sources
|
||||
platforms/rcore_vulkan_glfw.c # Or other platform specific Vulkan file
|
||||
rlgl_vulkan.c # Or however rlgl's Vulkan part is named
|
||||
)
|
||||
# Also ensure Vulkan headers are available
|
||||
target_include_directories(raylib PUBLIC $<BUILD_INTERFACE:${Vulkan_INCLUDE_DIRS}>)
|
||||
endif()
|
||||
```
|
||||
* **Link Vulkan Libraries:**
|
||||
* Ensure Vulkan libraries are linked. This might be handled by `LibraryConfigurations.cmake` or directly here. The `target_link_libraries` call mentioned above for preprocessor definitions might already cover this if `Vulkan::Vulkan` is an INTERFACE library that carries its link dependencies. Otherwise, add explicitly:
|
||||
```cmake
|
||||
if (SUPPORT_VULKAN AND Vulkan_FOUND)
|
||||
# If not handled by an INTERFACE library target like Vulkan::Vulkan
|
||||
target_link_libraries(raylib PUBLIC ${Vulkan_LIBRARIES})
|
||||
endif()
|
||||
```
|
||||
|
||||
**File: `cmake/LibraryConfigurations.cmake`**
|
||||
* This file likely contains logic for selecting graphics APIs (OpenGL versions). It will need to be extended:
|
||||
* Add checks for `GRAPHICS_API_VULKAN`.
|
||||
* If defined, ensure it sets up any necessary Vulkan-specific library paths or flags, and potentially skips/overrides some OpenGL configurations.
|
||||
|
||||
### 2. Configuration Header
|
||||
|
||||
**File: `src/config.h`**
|
||||
* This file might not need explicit changes if `GRAPHICS_API_VULKAN` is purely controlled by CMake.
|
||||
* However, for consistency or manual override capabilities, a section could be added:
|
||||
```c
|
||||
// Module: rlgl - Configuration Flags
|
||||
// ... existing flags ...
|
||||
// #define GRAPHICS_API_VULKAN 1
|
||||
```
|
||||
* Code within raylib would then check `#if defined(GRAPHICS_API_VULKAN)`
|
||||
|
||||
### 3. Graphics Abstraction Layer (`rlgl`)
|
||||
|
||||
**File: `src/rlgl.h`**
|
||||
* **Include Vulkan Headers:**
|
||||
```c
|
||||
#if defined(GRAPHICS_API_VULKAN)
|
||||
#include <vulkan/vulkan.h>
|
||||
// May need platform-specific Vulkan extensions for surfaces (e.g., vulkan_win32.h)
|
||||
// These would typically be included in the platform-specific rcore_vulkan_*.c file
|
||||
#endif
|
||||
```
|
||||
* **Conditional Function Declarations / Implementations:**
|
||||
* The existing `rlgl` functions (`rlLoadTexture`, `rlLoadShaderCode`, `rlBegin`, `rlEnd`, `rlDrawRenderBatch`, etc.) will need to either:
|
||||
1. Have internal conditional logic:
|
||||
```c
|
||||
RLAPI void rlBegin(int mode) {
|
||||
#if defined(GRAPHICS_API_VULKAN)
|
||||
// Vulkan implementation for rlBegin
|
||||
#elif defined(GRAPHICS_API_OPENGL_33)
|
||||
// OpenGL 3.3 implementation
|
||||
#else // GRAPHICS_API_OPENGL_11
|
||||
// OpenGL 1.1 implementation
|
||||
#endif
|
||||
}
|
||||
```
|
||||
2. Or, more cleanly, `rlgl.h` could define function pointers that are assigned during `rlglInit` to point to either OpenGL or Vulkan implementations. This is a more significant refactor but more extensible.
|
||||
```c
|
||||
// In rlgl.h
|
||||
// extern void (*rlBeginImpl)(int mode);
|
||||
// #define rlBegin rlBeginImpl
|
||||
|
||||
// In rlglInit() or a new rlglInitVulkan()
|
||||
// #if defined(GRAPHICS_API_VULKAN)
|
||||
// rlBeginImpl = rlBegin_Vulkan;
|
||||
// #else
|
||||
// rlBeginImpl = rlBegin_OpenGL;
|
||||
// #endif
|
||||
```
|
||||
* Given raylib's style, the first approach (internal conditional logic with `#if defined()`) is more likely.
|
||||
|
||||
**New File: `src/rlgl_vulkan.c` (or integrated into `rlgl.h`)**
|
||||
* This file will contain the Vulkan implementations of `rlgl` functions.
|
||||
* **`rlglInit_Vulkan(VkInstance instance, VkSurfaceKHR surface)` (or similar signature):**
|
||||
* **Physical Device Selection:** Enumerate physical devices, select a suitable one (e.g., discrete GPU with necessary queue families).
|
||||
* **Logical Device Creation:** Create `VkDevice` with required queues (graphics, present).
|
||||
* **Swapchain Setup:**
|
||||
* Query surface capabilities, formats, and present modes.
|
||||
* Choose swapchain format, extent, and present mode.
|
||||
* Create `VkSwapchainKHR`.
|
||||
* Get swapchain images and create `VkImageViews`.
|
||||
* **Command Pools & Buffers:** Create command pools and pre-allocate command buffers.
|
||||
* **Render Passes:** Create a default `VkRenderPass` compatible with the swapchain image format (for clearing and basic drawing).
|
||||
* **Framebuffers:** Create framebuffers for each swapchain image view, associating them with the render pass.
|
||||
* **Pipelines:**
|
||||
* Create a default graphics pipeline (vertex input, shaders, viewport, rasterizer, MSAA, depth/stencil, color blending). This will require a default SPIR-V vertex and fragment shader.
|
||||
* Pipeline layout.
|
||||
* **Synchronization Primitives:** Create semaphores and fences for frame rendering synchronization.
|
||||
* **Default Resources:**
|
||||
* Create a default 1x1 white texture (`VkImage`, `VkDeviceMemory`, `VkImageView`, `VkSampler`).
|
||||
* Load/compile default SPIR-V shaders for basic textured/colored drawing. Store their `VkShaderModule` handles.
|
||||
* Initialize Vulkan-specific parts of `RLGL.State` or a new `RLGL_VK_State` struct.
|
||||
* **`rlglClose_Vulkan()`:**
|
||||
* Call `vkDeviceWaitIdle()`.
|
||||
* Destroy all Vulkan resources in reverse order of creation (pipelines, framebuffers, render passes, swapchain, device, command pools, default textures/shaders, instance, etc.).
|
||||
* **Drawing Functions (`rlBegin`, `rlEnd`, `rlVertex3f`, `rlSetTexture`, etc.):**
|
||||
* These will need to manage a Vulkan-specific render batch system.
|
||||
* `rlBegin` might start a command buffer recording or select a pipeline.
|
||||
* `rlVertex3f` and related functions will populate CPU-side vertex/index buffers.
|
||||
* `rlEnd` or `rlDrawRenderBatch` will:
|
||||
* Copy CPU vertex/index data to Vulkan staging buffers, then to `VkBuffer` (vertex/index buffers).
|
||||
* Acquire next swapchain image.
|
||||
* Begin command buffer recording.
|
||||
* Begin render pass.
|
||||
* Bind pipeline, descriptor sets (for textures, uniforms).
|
||||
* Bind vertex/index buffers.
|
||||
* Set viewport/scissor.
|
||||
* Issue `vkCmdDraw` or `vkCmdDrawIndexed`.
|
||||
* End render pass.
|
||||
* End command buffer.
|
||||
* Submit command buffer to graphics queue.
|
||||
* Present swapchain image.
|
||||
* **Texture Functions (`rlLoadTexture`, `rlUpdateTexture`, `rlUnloadTexture`):**
|
||||
* `rlLoadTexture`: Create `VkImage`, allocate `VkDeviceMemory`, create `VkImageView`, `VkSampler`. Handle data upload via staging buffer.
|
||||
* `rlUpdateTexture`: Update `VkImage` data, potentially via staging buffer.
|
||||
* `rlUnloadTexture`: Destroy Vulkan image resources.
|
||||
* **Shader Functions (`rlLoadShaderFromMemory`, `rlLoadShader`):**
|
||||
* Modify to accept SPIR-V bytecode.
|
||||
* Create `VkShaderModule` from SPIR-V.
|
||||
* Store `VkShaderModule` handles. Pipeline creation will use these.
|
||||
* The existing `Shader` struct in raylib might need to be extended or have a Vulkan-specific counterpart to store `VkPipelineLayout`, `VkPipeline`, and descriptor set layouts/pools if shaders manage their own pipelines.
|
||||
|
||||
### 4. Core Layer (`rcore`)
|
||||
|
||||
**File: `src/rcore.c`**
|
||||
* **`InitWindow()`:**
|
||||
* Modify to conditionally call `InitPlatformVulkan()` if `GRAPHICS_API_VULKAN` is defined.
|
||||
```c
|
||||
#if defined(GRAPHICS_API_VULKAN)
|
||||
result = InitPlatformVulkan(); // New function in platform layer
|
||||
#else
|
||||
result = InitPlatform(); // Existing function
|
||||
#endif
|
||||
```
|
||||
* After platform initialization (which creates Vulkan instance & surface), call `rlglInit_Vulkan(instance, surface)` instead of `rlglInit()`.
|
||||
* **`CloseWindow()`:**
|
||||
* Modify to conditionally call `ClosePlatformVulkan()`.
|
||||
```c
|
||||
#if defined(GRAPHICS_API_VULKAN)
|
||||
ClosePlatformVulkan();
|
||||
#else
|
||||
ClosePlatform();
|
||||
#endif
|
||||
```
|
||||
* Call `rlglClose_Vulkan()` instead of `rlglClose()`.
|
||||
|
||||
### 5. Platform Layer (`src/platforms/`)
|
||||
|
||||
**New File: `src/platforms/rcore_vulkan_glfw.c` (example for GLFW)**
|
||||
* This file will handle Vulkan-specific initialization for the GLFW platform.
|
||||
* **`InitPlatformVulkan()`:**
|
||||
* Call `glfwInit()`.
|
||||
* Check for Vulkan support: `glfwVulkanSupported()`.
|
||||
* Get required instance extensions: `glfwGetRequiredInstanceExtensions()`.
|
||||
* Create `VkInstance` using these extensions and potentially validation layers.
|
||||
* Window hints for Vulkan: `glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API)`.
|
||||
* Create GLFW window: `glfwCreateWindow()`. Store window handle in `platform.handle`.
|
||||
* Create Vulkan surface: `glfwCreateWindowSurface(vkInstance, platform.handle, NULL, &vkSurface)`.
|
||||
* Store `vkInstance` and `vkSurface` (perhaps in `PlatformData` struct or pass to `rlglInit_Vulkan`).
|
||||
* Call `rlglInit_Vulkan(vkInstance, vkSurface)`.
|
||||
* Setup GLFW input callbacks (can be reused from `rcore_desktop_glfw.c`).
|
||||
* **`ClosePlatformVulkan()`:**
|
||||
* Call `rlglClose_Vulkan()`.
|
||||
* Destroy `VkSurfaceKHR` (`vkDestroySurfaceKHR(vkInstance, vkSurface, NULL)`).
|
||||
* Destroy `VkInstance` (`vkDestroyInstance(vkInstance, NULL)`).
|
||||
* Call `glfwDestroyWindow(platform.handle)`.
|
||||
* Call `glfwTerminate()`.
|
||||
* **PlatformData Struct:**
|
||||
* The existing `PlatformData` struct (if any, or a new one) might need to store `VkInstance` and `VkSurfaceKHR`.
|
||||
```c
|
||||
// In rcore_vulkan_glfw.c
|
||||
typedef struct PlatformDataVulkan {
|
||||
GLFWwindow *handle;
|
||||
VkInstance instance;
|
||||
VkSurfaceKHR surface;
|
||||
// Other Vulkan specific platform data if needed
|
||||
} PlatformDataVulkan;
|
||||
```
|
||||
|
||||
### 6. Shader System and Examples
|
||||
|
||||
* **SPIR-V Compilation:**
|
||||
* A build step needs to be added to compile GLSL shaders (from `examples/shaders/resources/shaders/glslXXX/`) to SPIR-V.
|
||||
* Tools like `glslangValidator` (from Vulkan SDK) can be used for this.
|
||||
* Example CMake integration:
|
||||
```cmake
|
||||
# In examples/CMakeLists.txt or similar
|
||||
if (SUPPORT_VULKAN AND Vulkan_FOUND AND GLSLANG_VALIDATOR_EXECUTABLE)
|
||||
# Loop through example shaders and compile them
|
||||
# add_custom_command(...)
|
||||
endif()
|
||||
```
|
||||
* **Shader Loading in Examples:**
|
||||
* Examples that load shaders will need to be updated to load the compiled SPIR-V versions when the Vulkan backend is active.
|
||||
* This might involve path changes or conditional loading based on `GRAPHICS_API_VULKAN`.
|
||||
* **Default Shaders in `rlgl`:**
|
||||
* The default shaders used internally by `rlgl` (e.g., for `DrawRectangle`, `DrawTexture`) must also be provided in SPIR-V format and loaded by `rlglInit_Vulkan`.
|
||||
|
||||
### 7. Memory Management
|
||||
|
||||
* Vulkan requires explicit memory management. `rlgl_vulkan.c` will need to handle `VkDeviceMemory` allocations and deallocations for buffers and images.
|
||||
* Consider using a Vulkan Memory Allocator library (e.g., VMA from AMD) for more robust and efficient memory management, though for a first pass, direct Vulkan calls can be used.
|
||||
|
||||
### 8. Error Handling
|
||||
|
||||
* Vulkan API calls return `VkResult`. These should be checked consistently, and errors should be logged using raylib's `TRACELOG`.
|
||||
|
||||
This plan provides a high-level overview. Each point, especially within `rlgl_vulkan.c`, involves significant implementation detail.
|
||||
The core principle is to abstract Vulkan complexities within `rlgl` and the platform layer, keeping the raylib API consistent for the end-user.
|
Loading…
Add table
Add a link
Reference in a new issue