|
SBgl 0.1.0
A graphics framework in C99
|
This document explains the Vulkan 1.3 implementation used in SBgl. The goal is to use the API directly with separation between the OS and the GPU.
Everything starts with the VkInstance. This is the application's connection to the Vulkan runtime.
VK_KHR_surface and a platform-specific extension are enabled:VK_KHR_wayland_surfaceVK_KHR_xlib_surfaceVK_KHR_win32_surfaceVulkan is platform-agnostic; it does not know what a "window" is. The VkSurfaceKHR acts as the bridge.
sbgl_os_GetNativeWindowHandle).wl_display and wl_surface are passed.The select_physical_device() function identifies the most suitable hardware:
VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU is searched for first.Once a GPU is chosen, a VkDevice is created.
dynamicRendering feature is enabled. This allows drawing directly to an image without the overhead of pre-defined RenderPass objects.To ensure API correctness, SBgl provides configurable Vulkan validation through the enableValidation flag in sbgl_InitConfig:
enableValidation is true, the backend requests the "VK_LAYER_KHRONOS_validation" layer and sets up a debug messenger.VK_EXT_debug_utils extension routes validation messages through SBgl's logging system with source location tracking (file, line, function).stderr with category tagging (e.g., [GFX]) for filtering.enableValidation = false in production to eliminate real-time API checking overhead.The backend tracks Vulkan results for debugging. Use sbgl_GetErrorDetail() to inspect both core error codes and raw VkResult values:
To prevent flickering, drawing is never done directly to the screen. A Swapchain is used—a queue of images (usually 3 or 4).
VkSemaphore (GPU-to-GPU sync) and VkFence (CPU-to-GPU sync) are used to ensure the CPU does not start drawing before the GPU is finished with the previous frame.inFlightFence is waited upon to ensure the previous frame is fully processed by the GPU.VkCommandBuffer is reset and a new list of operations is started.VkImageMemoryBarrier is used to signal that the image is no longer for the OS and pixels will now be written to it.vkCmdBeginRendering is used.clearValue (RGBA) is provided.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR. This signals that drawing is finished and the image can be returned to the OS.vkQueuePresentKHR is called. This is the final step where the pixels are sent to the compositor to be displayed.SBgl strictly avoids standard malloc/free calls and individual vkCreateBuffer operations during its runtime to ensure deterministic performance and prevent memory fragmentation.
The backend implements a Hybrid GPU Memory Manager that handles all buffer allocations through sub-allocation from pre-allocated physical memory pools.
sbgl_gfx_CreateBuffer: Instead of calling vkCreateBuffer for every request, sbgl_gfx_CreateBuffer identifies the target heap (Static, Dynamic, or Managed) and returns a sub-allocated range (offset and size) within a master buffer.For resources with non-linear lifecycles, the Managed Heap utilizes an internal array-based tracker to maintain visibility of active and free ranges.
When a window is resized, the Vulkan swapchain and its associated image views must be destroyed and recreated. To manage this without free():
SblArenaMark (a bookmark of the current arena offset).cleanup_swapchain phase, the backend calls sbl_arena_rewind() to reset the arena to that bookmark.This pattern allows the engine to handle resize events without increasing the memory footprint or leaking memory.
The graphics backend is fully decoupled from global state. All Vulkan function pointers and device state are stored within an opaque sbgl_GfxContext structure.
sbgl_gfx_Init accepts a sbgl_Window* and an SblArena* for all its internal state allocations.Resource limits for buffers, shaders, and pipelines are configurable at context initialization time via sbgl_InitWithConfig():
Alternatively, use sbgl_DefaultInitConfig to start with defaults and override specific fields:
Default Limits:
| Resource | Default | Minimum | Memory Impact |
|---|---|---|---|
| Buffers | 1024 | 64 | ~32KB per 1024 entries |
| Shaders | 256 | 16 | ~4KB per 256 entries |
| Pipelines | 256 | 16 | ~8KB per 256 entries |
Limits below the minimum are automatically clamped. Resource arrays are allocated from the persistent arena during initialization and remain fixed for the context lifetime to ensure O(1) handle lookups.
Before destroying resources during application shutdown or major state transitions, the CPU MUST wait for the GPU to become idle.
Failure to call sbgl_DeviceWaitIdle() before destruction will trigger Vulkan Validation Errors such as:
VUID-vkDestroyPipeline-pipeline-00765: Pipeline currently in use.VUID-vkDestroyBuffer-buffer-00922: Buffer currently in use.SBgl maintains this synchronization as explicit. This prevents unnecessary stalls during execution while ensuring full API correctness during cleanup.
SBgl utilizes the VK_KHR_buffer_device_address extension (core in Vulkan 1.3) to achieve efficient memory access.
To maximize the "Where there is one, there are many" mandate, SBgl employs Indirect Drawing for its automated batching system.
vkCmdDrawIndirect (or vkCmdDrawIndexedIndirect) command.The backend utilizes volk as its Vulkan meta-loader to eliminate static linking and provide device-level function dispatching.
volk automates the retrieval of function pointers, replacing manual vkGetInstanceProcAddr and vkGetDeviceProcAddr calls.VolkDeviceTable to load device-specific function pointers. This enables multiple graphics contexts to operate with minimal overhead by avoiding the Vulkan loader's internal dispatching logic for high-frequency commands.| Feature | Implementation | Benefit |
|---|---|---|
| Vulkan API | 1.3 Core | Access to simplified pipelines. |
| Meta-loader | volk (v1.4.350) | Device-level function dispatching via device tables. |
| Rendering | Dynamic Rendering | No RenderPass/Framebuffer boilerplate. |
| Sync | Fences & Semaphores | Synchronization and stable frame rates. |
| GPU Memory | Hybrid Sub-allocation | Reduced API overhead and fragmentation via heap partitioning. |
| Arch | Context-Based | No global singletons; supports multi-window. |
For detailed technical specifications of the Vulkan API and its implementations, refer to the following official documentation: