SBgl 0.1.0
A graphics framework in C99
Loading...
Searching...
No Matches
sbgl_core.c File Reference
#include "sbgl.h"
#include "sbgl_types.h"
#include "core/sbl_arena.h"
#include "backend/sbgl_graphics_hal.h"
#include "core/sbgl_batcher.h"
#include "core/sbgl_context_internal.h"
#include "core/sbgl_internal_log.h"
#include "core/sbgl_platform.h"
#include "core/sbgl_sort.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
Include dependency graph for sbgl_core.c:

Go to the source code of this file.

Data Structures

struct  sbgl_RenderQueue
 Internal storage for draw packets awaiting submission. More...
 
struct  sbgl_InternalContext
 Internal state for the engine context. More...
 

Macros

#define SBL_ARENA_IMPLEMENTATION
 

Functions

SblArenasbgl_GetContextArena (sbgl_Context *ctx)
 Retrieves the persistent arena associated with a context.
 
sbgl_Result sbgl_GetResult (sbgl_Context *ctx)
 Retrieves the last result code from the context.
 
sbgl_ErrorDetail sbgl_GetErrorDetail (sbgl_Context *ctx)
 Retrieves detailed error information including backend-specific codes.
 
void sbgl_ClearResult (sbgl_Context *ctx)
 Clears the result code to SBGL_SUCCESS.
 
sbgl_InitResult sbgl_InitWithConfig (const sbgl_InitConfig *config)
 Initializes the engine and opens a window with explicit configuration.
 
sbgl_InitResult sbgl_Init (int w, int h, const char *title)
 Initializes the engine and opens a window.
 
void sbgl_Shutdown (sbgl_Context *ctx)
 Gracefully shuts down the engine and releases all resources.
 
bool sbgl_WindowShouldClose (sbgl_Context *ctx)
 Checks if the user or OS has requested to close the window.
 
double sbgl_GetTime (sbgl_Context *ctx)
 Retrieves the current monotonic system time in seconds.
 
void sbgl_GetWindowSize (sbgl_Context *ctx, int *w, int *h)
 Retrieves the current window dimensions.
 
void sbgl_BeginDrawing (sbgl_Context *ctx)
 Prepares the engine for a new frame of drawing.
 
void sbgl_EndDrawing (sbgl_Context *ctx)
 Finalizes the current frame and presents it to the screen.
 
void sbgl_BeginCompute (sbgl_Context *ctx)
 Prepares the engine for compute operations before the main drawing pass.
 
void sbgl_EndCompute (sbgl_Context *ctx)
 Finalizes the compute phase.
 
void sbgl_DeviceWaitIdle (sbgl_Context *ctx)
 Synchronizes the CPU with the GPU, waiting for all commands to complete.
 
void sbgl_SetClearColor (sbgl_Context *ctx, float r, float g, float b, float a)
 Sets the clear color for the next frame.
 
const sbgl_InputStatesbgl_GetInputState (sbgl_Context *ctx)
 Retrieves the input state for the current frame.
 
void sbgl_SetMouseMode (sbgl_Context *ctx, sbgl_MouseMode mode)
 Sets the cursor behavior and visibility.
 
sbgl_Telemetry sbgl_GetTelemetry (sbgl_Context *ctx)
 Retrieves the performance telemetry data for the previous frame.
 
sbgl_Buffer sbgl_CreateBuffer (sbgl_Context *ctx, sbgl_BufferUsage usage, size_t size, const void *data)
 Creates a GPU buffer.
 
void sbgl_DestroyBuffer (sbgl_Context *ctx, sbgl_Buffer buffer)
 Destroys a GPU buffer.
 
void sbgl_FillBuffer (sbgl_Context *ctx, sbgl_Buffer buffer, size_t offset, size_t size, uint32_t value)
 Fills a region of a GPU buffer with a fixed 32-bit value.
 
uint32_t sbgl_GetFrameIndex (sbgl_Context *ctx)
 Retrieves the current frame index for double/triple buffering.
 
void * sbgl_MapBuffer (sbgl_Context *ctx, sbgl_Buffer buffer)
 Maps a GPU buffer into the CPU's address space.
 
void sbgl_UnmapBuffer (sbgl_Context *ctx, sbgl_Buffer buffer)
 Unmaps a previously mapped GPU buffer.
 
sbgl_Shader sbgl_LoadShader (sbgl_Context *ctx, sbgl_ShaderStage stage, const uint32_t *bytecode, size_t size)
 Loads a shader from SPIR-V bytecode.
 
sbgl_Shader sbgl_LoadShaderFromFile (sbgl_Context *ctx, sbgl_ShaderStage stage, const char *filename)
 Helper function to load a shader directly from a SPIR-V file.
 
void sbgl_DestroyShader (sbgl_Context *ctx, sbgl_Shader shader)
 Destroys a shader module.
 
sbgl_Pipeline sbgl_CreatePipeline (sbgl_Context *ctx, const sbgl_PipelineConfig *config)
 Creates a graphics pipeline.
 
void sbgl_DestroyPipeline (sbgl_Context *ctx, sbgl_Pipeline pipeline)
 Destroys a graphics pipeline.
 
sbgl_ComputePipeline sbgl_CreateComputePipeline (sbgl_Context *ctx, sbgl_Shader shader)
 Creates a compute pipeline.
 
void sbgl_DestroyComputePipeline (sbgl_Context *ctx, sbgl_ComputePipeline pipeline)
 Destroys a compute pipeline.
 
void sbgl_BindComputePipeline (sbgl_Context *ctx, sbgl_ComputePipeline pipeline)
 Binds a compute pipeline for subsequent dispatch calls.
 
void sbgl_DispatchCompute (sbgl_Context *ctx, uint32_t x, uint32_t y, uint32_t z)
 Dispatches a compute workload.
 
void sbgl_MemoryBarrier (sbgl_Context *ctx, sbgl_BarrierType type)
 Injects a memory barrier to synchronize compute and graphics operations.
 
void sbgl_BindPipeline (sbgl_Context *ctx, sbgl_Pipeline pipeline)
 Binds a graphics pipeline for subsequent draw calls.
 
void sbgl_BindBuffer (sbgl_Context *ctx, sbgl_Buffer buffer, sbgl_BufferUsage usage)
 Binds a buffer to the pipeline.
 
uint64_t sbgl_GetBufferDeviceAddress (sbgl_Context *ctx, sbgl_Buffer buffer)
 Retrieves the 64-bit GPU virtual address for a buffer.
 
void sbgl_Draw (sbgl_Context *ctx, uint32_t vertexCount, uint32_t firstVertex, uint32_t instanceCount)
 Submits a non-indexed draw command.
 
void sbgl_DrawIndexed (sbgl_Context *ctx, uint32_t indexCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t instanceCount)
 Submits an indexed draw command.
 
void sbgl_DrawIndirect (sbgl_Context *ctx, sbgl_Buffer buffer, size_t offset, uint32_t drawCount)
 Submits a batch of draw calls stored in a GPU buffer.
 
void sbgl_PushConstants (sbgl_Context *ctx, size_t size, const void *data)
 Updates push constants for the currently bound pipeline.
 
sbgl_RenderQueuesbgl_CreateRenderQueue (sbgl_Context *ctx, SblArena *arena)
 Creates a thread-local render queue for collecting draw commands.
 
void sbgl_SubmitDraw (sbgl_RenderQueue *queue, uint32_t mesh, uint32_t material, uint32_t blendMode, uint32_t sidedness, uint32_t tags, sbgl_SortKey key, const sbgl_InstanceData *data)
 Appends a draw command to the render queue.
 
void sbgl_RenderQueues (sbgl_Context *ctx, sbgl_RenderQueue **queues, uint32_t queueCount, const sbgl_Mat4 *viewProj)
 Merges, sorts, and submits pending draw commands to the GPU.
 
void sbgl_RenderQueuesEx (sbgl_Context *ctx, sbgl_RenderQueue **queues, uint32_t queueCount, const sbgl_Mat4 *viewProj, uint64_t userAddress)
 Extended version of sbgl_RenderQueues with user metadata.
 

Macro Definition Documentation

◆ SBL_ARENA_IMPLEMENTATION

#define SBL_ARENA_IMPLEMENTATION

Definition at line 4 of file sbgl_core.c.

Function Documentation

◆ sbgl_BeginCompute()

void sbgl_BeginCompute ( sbgl_Context * ctx)

Prepares the engine for compute operations before the main drawing pass.

If a frame has not yet been started, this function initiates the frame lifecycle, acquiring a swapchain image and starting the command buffer. It must be called outside of a BeginDrawing/EndDrawing block.

Parameters
ctxThe engine context.

Definition at line 344 of file sbgl_core.c.

344 {
345 if (!ctx || !ctx->inner)
346 return;
348
349 // Reset graphics handle to prevent cross-phase push constant delivery
351
352 /* If a frame has not yet been initiated, the system handles event polling,
353 focus management, and starts the GPU command stream. */
354 if (!inner->state.hasStartedFrame) {
355 sbgl_os_PollEvents(inner->window);
356
357 bool currentlyFocused = sbgl_os_IsWindowFocused(inner->window);
358 if (currentlyFocused && !inner->state.wasFocused) {
359 if (inner->mouseMode == SBGL_MOUSE_MODE_CAPTURED) {
360 sbgl_os_SetCursorVisible(inner->window, false);
361 sbgl_os_SetCursorLocked(inner->window, true);
362 }
363 }
364 inner->state.wasFocused = currentlyFocused;
365
366 inner->frameStartTicks = sbgl_os_GetPerfCount(inner->window);
367
368 inner->currentFrame.draw_calls = 0;
369 inner->currentFrame.instance_count = 0;
370 inner->currentFrame.cpu_sort_time = 0.0f;
371
372 if (sbgl_gfx_BeginFrame(inner->gfx)) {
373 inner->state.hasStartedFrame = 1;
374 if (inner->frameCount > 2) {
375 inner->currentFrame.gpu_render_time = sbgl_gfx_GetGpuTime(inner->gfx);
376 }
377 } else {
379 return;
380 }
381 }
382 ctx->result = SBGL_SUCCESS;
383}
@ SBGL_MOUSE_MODE_CAPTURED
Definition sbgl_input.h:156
void sbgl_gfx_BindPipeline(sbgl_GfxContext *ctx, sbgl_Pipeline handle)
bool sbgl_gfx_BeginFrame(sbgl_GfxContext *ctx)
Starts a new frame, acquiring an image and starting the command buffer.
float sbgl_gfx_GetGpuTime(sbgl_GfxContext *ctx)
Retrieves the elapsed GPU time for the previous frame in milliseconds.
uint64_t sbgl_os_GetPerfCount(sbgl_Window *window)
Gets the high-resolution performance counter.
void sbgl_os_PollEvents(sbgl_Window *window)
Dispatches OS events (messages/protocol requests).
void sbgl_os_SetCursorLocked(sbgl_Window *window, bool locked)
Locks or unlocks the cursor within the window bounds.
void sbgl_os_SetCursorVisible(sbgl_Window *window, bool visible)
Sets the visibility of the OS cursor for the given window.
bool sbgl_os_IsWindowFocused(sbgl_Window *window)
Checks if the window currently has input focus.
@ SBGL_SUCCESS
Definition sbgl_types.h:215
@ SBGL_ERROR_GRAPHICS_FAILED
Definition sbgl_types.h:220
#define SBGL_INVALID_HANDLE
Definition sbgl_types.h:8
void * inner
Opaque pointer to the internal engine state.
Definition sbgl_types.h:278
sbgl_Result result
Status of the last major operation.
Definition sbgl_types.h:286
Internal state for the engine context.
Definition sbgl_core.c:40

◆ sbgl_BeginDrawing()

void sbgl_BeginDrawing ( sbgl_Context * ctx)

Prepares the engine for a new frame of drawing.

This function handles event polling and Vulkan swapchain image acquisition. It is called before clearing or drawing commands.

Parameters
ctxThe engine context.

Definition at line 262 of file sbgl_core.c.

262 {
263 if (!ctx || !ctx->inner) return;
265
266 // Reset compute handle to prevent cross-phase push constant delivery
268
269 if (!inner->state.hasStartedFrame) {
270 sbgl_os_PollEvents(inner->window);
271
272 // Detect focus transitions to re-apply mouse locking if necessary.
273 bool currentlyFocused = sbgl_os_IsWindowFocused(inner->window);
274 if (currentlyFocused && !inner->state.wasFocused) {
275 if (inner->mouseMode == SBGL_MOUSE_MODE_CAPTURED) {
276 sbgl_os_SetCursorVisible(inner->window, false);
277 sbgl_os_SetCursorLocked(inner->window, true);
278 }
279 }
280 inner->state.wasFocused = currentlyFocused;
281
282 inner->frameStartTicks = sbgl_os_GetPerfCount(inner->window);
283
284 // Performance counters are reset at the start of every frame
285 inner->currentFrame.draw_calls = 0;
286 inner->currentFrame.instance_count = 0;
287 inner->currentFrame.cpu_sort_time = 0.0f;
288
289 if (sbgl_gfx_BeginFrame(inner->gfx)) {
290 inner->state.hasStartedFrame = 1;
291
292 /* GPU performance data for the previous frame in this slot is now guaranteed
293 to be ready. Telemetry is skipped for the very first frames to allow
294 the pipeline to prime and avoid uninitialized query errors. */
295 if (inner->frameCount > 2) {
296 inner->currentFrame.gpu_render_time = sbgl_gfx_GetGpuTime(inner->gfx);
297 }
298 } else {
300 return;
301 }
302 }
303
305 inner->gfx,
306 inner->clearColor[0],
307 inner->clearColor[1],
308 inner->clearColor[2],
309 inner->clearColor[3]
310 );
311 inner->state.isDrawing = 1;
312 ctx->result = SBGL_SUCCESS;
313}
void sbgl_gfx_BindComputePipeline(sbgl_GfxContext *ctx, sbgl_ComputePipeline handle)
void sbgl_gfx_BeginRenderPass(sbgl_GfxContext *ctx, float r, float g, float b, float a)
Starts a graphics rendering pass.

◆ sbgl_BindBuffer()

void sbgl_BindBuffer ( sbgl_Context * ctx,
sbgl_Buffer buffer,
sbgl_BufferUsage usage )

Binds a buffer to the pipeline.

Parameters
ctxThe engine context.
bufferThe buffer handle.
usageThe usage to bind it as (Vertex/Index).

Definition at line 681 of file sbgl_core.c.

681 {
682 if (!ctx || !ctx->inner)
683 return;
685 sbgl_gfx_BindBuffer(inner->gfx, buffer, usage);
686 ctx->result = SBGL_SUCCESS;
687}
void sbgl_gfx_BindBuffer(sbgl_GfxContext *ctx, sbgl_Buffer handle, sbgl_BufferUsage usage)

◆ sbgl_BindComputePipeline()

void sbgl_BindComputePipeline ( sbgl_Context * ctx,
sbgl_ComputePipeline pipeline )

Binds a compute pipeline for subsequent dispatch calls.

Parameters
ctxThe engine context.
pipelineThe compute pipeline handle.

Definition at line 649 of file sbgl_core.c.

649 {
650 if (!ctx || !ctx->inner)
651 return;
653 sbgl_gfx_BindComputePipeline(inner->gfx, pipeline);
654 ctx->result = SBGL_SUCCESS;
655}

◆ sbgl_BindPipeline()

void sbgl_BindPipeline ( sbgl_Context * ctx,
sbgl_Pipeline pipeline )

Binds a graphics pipeline for subsequent draw calls.

Parameters
ctxThe engine context.
pipelineThe pipeline handle.

Definition at line 673 of file sbgl_core.c.

673 {
674 if (!ctx || !ctx->inner)
675 return;
677 sbgl_gfx_BindPipeline(inner->gfx, pipeline);
678 ctx->result = SBGL_SUCCESS;
679}

◆ sbgl_ClearResult()

void sbgl_ClearResult ( sbgl_Context * ctx)

Clears the result code to SBGL_SUCCESS.

Parameters
ctxThe engine context (may be NULL).

Definition at line 103 of file sbgl_core.c.

103 {
104 if (!ctx) {
105 return;
106 }
107 ctx->result = SBGL_SUCCESS;
108}

◆ sbgl_CreateBuffer()

sbgl_Buffer sbgl_CreateBuffer ( sbgl_Context * ctx,
sbgl_BufferUsage usage,
size_t size,
const void * data )

Creates a GPU buffer.

Parameters
ctxThe engine context.
usageIntended usage of the buffer (Vertex/Index).
sizeSize in bytes.
dataOptional initial data. If NULL, the buffer is created empty.
Returns
A handle to the created buffer.

Definition at line 445 of file sbgl_core.c.

445 {
446 if (!ctx || !ctx->inner)
447 return SBGL_INVALID_HANDLE;
448
450 sbgl_Buffer res = sbgl_gfx_CreateBuffer(inner->gfx, usage, size, data);
451
452 ctx->result =
454 return res;
455}
sbgl_Buffer sbgl_gfx_CreateBuffer(sbgl_GfxContext *ctx, sbgl_BufferUsage usage, size_t size, const void *data)
uint32_t sbgl_Buffer
Handle for a GPU-side buffer.
Definition sbgl_types.h:37

◆ sbgl_CreateComputePipeline()

sbgl_ComputePipeline sbgl_CreateComputePipeline ( sbgl_Context * ctx,
sbgl_Shader shader )

Creates a compute pipeline.

Parameters
ctxThe engine context.
shaderThe compute shader handle.
Returns
A handle to the created compute pipeline.

Definition at line 619 of file sbgl_core.c.

619 {
620 if (!ctx || !ctx->inner)
621 return SBGL_INVALID_HANDLE;
624 ctx->result =
626 return res;
627}
sbgl_ComputePipeline sbgl_gfx_CreateComputePipeline(sbgl_GfxContext *ctx, sbgl_Shader handle)
uint32_t sbgl_ComputePipeline
Handle for a compute pipeline.
Definition sbgl_types.h:52

◆ sbgl_CreatePipeline()

sbgl_Pipeline sbgl_CreatePipeline ( sbgl_Context * ctx,
const sbgl_PipelineConfig * config )

Creates a graphics pipeline.

Parameters
ctxThe engine context.
configConfiguration for the pipeline.
Returns
A handle to the created pipeline.

Definition at line 587 of file sbgl_core.c.

587 {
588 if (!ctx || !ctx->inner)
589 return SBGL_INVALID_HANDLE;
591 sbgl_Pipeline res = sbgl_gfx_CreatePipeline(inner->gfx, config);
592 ctx->result =
594 return res;
595}
sbgl_Pipeline sbgl_gfx_CreatePipeline(sbgl_GfxContext *ctx, const sbgl_PipelineConfig *config)
uint32_t sbgl_Pipeline
Handle for a graphics pipeline.
Definition sbgl_types.h:47

◆ sbgl_CreateRenderQueue()

sbgl_RenderQueue * sbgl_CreateRenderQueue ( sbgl_Context * ctx,
struct SblArena * arena )

Creates a thread-local render queue for collecting draw commands.

Draw packets are stored in this queue and submitted to the backend for rendering. Multiple queues enable parallel command recording.

Parameters
ctxThe engine context.
arenaThe arena to use for queue allocations.
Returns
A pointer to the newly created render queue.

Definition at line 743 of file sbgl_core.c.

743 {
744 if (!ctx || !arena) {
745 return NULL;
746 }
747
748 // Allocate the queue structure from the provided arena
750 if (!queue) {
752 return NULL;
753 }
754
755 // Initialize the queue with a default capacity of 16,384 packets
756 /* Initialize the queue with a high capacity to support massive batching (e.g., voxels). */
757 queue->capacity = 131072;
758 queue->count = 0;
759 queue->arena = arena;
762
763 if (!queue->packets || !queue->instances) {
765 return NULL;
766 }
767
768 ctx->result = SBGL_SUCCESS;
769 return queue;
770}
@ SBGL_ERROR_OUT_OF_MEMORY
Definition sbgl_types.h:221
#define SBL_ARENA_PUSH_ARRAY_ZERO(arena, type, count)
Definition sbl_arena.h:23
#define SBL_ARENA_PUSH_STRUCT_ZERO(arena, type)
Definition sbl_arena.h:20
Encapsulates all data required to submit a single draw call. Optimized for cache density (16 bytes).
Definition sbgl_types.h:97
Per-instance data for automated batching.
Definition sbgl_types.h:19
Internal storage for draw packets awaiting submission.
Definition sbgl_core.c:25
uint32_t capacity
Definition sbgl_core.c:29
uint32_t count
Definition sbgl_core.c:28
sbgl_InstanceData * instances
Definition sbgl_core.c:27
struct SblArena * arena
Definition sbgl_core.c:30
sbgl_DrawPacket * packets
Definition sbgl_core.c:26

◆ sbgl_DestroyBuffer()

void sbgl_DestroyBuffer ( sbgl_Context * ctx,
sbgl_Buffer buffer )

Destroys a GPU buffer.

All submitted GPU commands referencing this buffer must have completed before calling this. Use sbgl_DeviceWaitIdle() to synchronize.

Parameters
ctxThe engine context.
bufferThe buffer handle.

Definition at line 457 of file sbgl_core.c.

457 {
458 if (!ctx || !ctx->inner)
459 return;
460
462
463 if (!inner->state.isIdle) {
467 "Buffer destroyed while GPU is busy! Missing sbgl_DeviceWaitIdle."
468 );
469
471
472 // Force a wait to safely destroy it anyway and prevent driver crash
474 }
475
476 sbgl_gfx_DestroyBuffer(inner->gfx, buffer);
477 ctx->result = SBGL_SUCCESS;
478}
void sbgl_gfx_DestroyBuffer(sbgl_GfxContext *ctx, sbgl_Buffer handle)
void sbgl_DeviceWaitIdle(sbgl_Context *ctx)
Synchronizes the CPU with the GPU, waiting for all commands to complete.
Definition sbgl_core.c:391
@ SBGL_LOG_CAT_GFX
@ SBGL_LOG_WARN
#define sbgl_log_impl(level, category, msg)
@ SBGL_ERROR_DEVICE_BUSY
Definition sbgl_types.h:222

◆ sbgl_DestroyComputePipeline()

void sbgl_DestroyComputePipeline ( sbgl_Context * ctx,
sbgl_ComputePipeline pipeline )

Destroys a compute pipeline.

Parameters
ctxThe engine context.
pipelineThe compute pipeline handle.

Definition at line 629 of file sbgl_core.c.

629 {
630 if (!ctx || !ctx->inner)
631 return;
633
634 if (!inner->state.isIdle) {
638 "Destroy Compute Pipeline called while GPU is busy! Missing sbgl_DeviceWaitIdle."
639 );
640
643 }
644
645 sbgl_gfx_DestroyComputePipeline(inner->gfx, pipeline);
646 ctx->result = SBGL_SUCCESS;
647}
void sbgl_gfx_DestroyComputePipeline(sbgl_GfxContext *ctx, sbgl_ComputePipeline handle)
@ SBGL_LOG_ERROR

◆ sbgl_DestroyPipeline()

void sbgl_DestroyPipeline ( sbgl_Context * ctx,
sbgl_Pipeline pipeline )

Destroys a graphics pipeline.

The pipeline must not be in use by any submitted command buffers. Use sbgl_DeviceWaitIdle() to ensure safe destruction.

Parameters
ctxThe engine context.
pipelineThe pipeline handle.

Definition at line 597 of file sbgl_core.c.

597 {
598 if (!ctx || !ctx->inner)
599 return;
601
602 if (!inner->state.isIdle) {
606 "Destroy Pipeline called while GPU is busy! Missing sbgl_DeviceWaitIdle."
607 );
608
610
611 // Force a wait to safely destroy it anyway and prevent driver crash
613 }
614
615 sbgl_gfx_DestroyPipeline(inner->gfx, pipeline);
616 ctx->result = SBGL_SUCCESS;
617}
void sbgl_gfx_DestroyPipeline(sbgl_GfxContext *ctx, sbgl_Pipeline handle)

◆ sbgl_DestroyShader()

void sbgl_DestroyShader ( sbgl_Context * ctx,
sbgl_Shader shader )

Destroys a shader module.

Shaders must not be in use by the GPU. Use sbgl_DeviceWaitIdle() before destruction during shutdown.

Parameters
ctxThe engine context.
shaderThe shader handle.

Definition at line 565 of file sbgl_core.c.

565 {
566 if (!ctx || !ctx->inner)
567 return;
569
570 if (!inner->state.isIdle) {
574 "Destroy Shader called while GPU is busy! Missing sbgl_DeviceWaitIdle."
575 );
576
578
579 // Force a wait to safely destroy it anyway and prevent driver crash
581 }
582
583 sbgl_gfx_DestroyShader(inner->gfx, shader);
584 ctx->result = SBGL_SUCCESS;
585}
void sbgl_gfx_DestroyShader(sbgl_GfxContext *ctx, sbgl_Shader handle)

◆ sbgl_DeviceWaitIdle()

void sbgl_DeviceWaitIdle ( sbgl_Context * ctx)

Synchronizes the CPU with the GPU, waiting for all commands to complete.

This is called before destroying resources (Pipelines, Buffers, Shaders) to ensure they are no longer in use by the GPU. Failure to do so results in Vulkan validation errors and instability.

Parameters
ctxThe engine context.

Definition at line 391 of file sbgl_core.c.

391 {
392 if (!ctx || !ctx->inner)
393 return;
394
396 sbgl_gfx_DeviceWaitIdle(inner->gfx);
397 inner->state.isIdle = 1;
398 ctx->result = SBGL_SUCCESS;
399}
void sbgl_gfx_DeviceWaitIdle(sbgl_GfxContext *ctx)

◆ sbgl_DispatchCompute()

void sbgl_DispatchCompute ( sbgl_Context * ctx,
uint32_t groupCountX,
uint32_t groupCountY,
uint32_t groupCountZ )

Dispatches a compute workload.

Parameters
ctxThe engine context.
groupCountXNumber of workgroups in X dimension.
groupCountYNumber of workgroups in Y dimension.
groupCountZNumber of workgroups in Z dimension.

Definition at line 657 of file sbgl_core.c.

657 {
658 if (!ctx || !ctx->inner)
659 return;
661 sbgl_gfx_DispatchCompute(inner->gfx, x, y, z);
662 ctx->result = SBGL_SUCCESS;
663}
void sbgl_gfx_DispatchCompute(sbgl_GfxContext *ctx, uint32_t x, uint32_t y, uint32_t z)

◆ sbgl_Draw()

void sbgl_Draw ( sbgl_Context * ctx,
uint32_t vertexCount,
uint32_t firstVertex,
uint32_t instanceCount )

Submits a non-indexed draw command.

Parameters
ctxThe engine context.
vertexCountNumber of vertices to draw.
firstVertexOffset to the first vertex.
instanceCountNumber of instances to draw.

Definition at line 698 of file sbgl_core.c.

698 {
699 if (!ctx || !ctx->inner)
700 return;
702 sbgl_gfx_Draw(inner->gfx, vertexCount, firstVertex, instanceCount);
703 ctx->result = SBGL_SUCCESS;
704}
void sbgl_gfx_Draw(sbgl_GfxContext *ctx, uint32_t vertexCount, uint32_t firstVertex, uint32_t instanceCount)

◆ sbgl_DrawIndexed()

void sbgl_DrawIndexed ( sbgl_Context * ctx,
uint32_t indexCount,
uint32_t firstIndex,
int32_t vertexOffset,
uint32_t instanceCount )

Submits an indexed draw command.

Parameters
ctxThe engine context.
indexCountNumber of indices to draw.
firstIndexOffset to the first index.
vertexOffsetValue added to each index before indexing into vertex buffers.
instanceCountNumber of instances to draw.

Definition at line 706 of file sbgl_core.c.

712 {
713 if (!ctx || !ctx->inner)
714 return;
716 sbgl_gfx_DrawIndexed(inner->gfx, indexCount, firstIndex, vertexOffset, instanceCount);
717 ctx->result = SBGL_SUCCESS;
718}
void sbgl_gfx_DrawIndexed(sbgl_GfxContext *ctx, uint32_t indexCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t instanceCount)

◆ sbgl_DrawIndirect()

void sbgl_DrawIndirect ( sbgl_Context * ctx,
sbgl_Buffer buffer,
size_t offset,
uint32_t drawCount )

Submits a batch of draw calls stored in a GPU buffer.

Parameters
ctxThe engine context.
bufferHandle to the buffer containing an array of sbgl_IndirectCommand.
offsetThe byte offset into the buffer where the commands begin.
drawCountThe number of commands to execute from the buffer.

Definition at line 720 of file sbgl_core.c.

725 {
726 if (!ctx || !ctx->inner)
727 return;
729 sbgl_gfx_DrawIndirect(inner->gfx, buffer, offset, drawCount);
730 ctx->result = SBGL_SUCCESS;
731}
void sbgl_gfx_DrawIndirect(sbgl_GfxContext *ctx, sbgl_Buffer handle, size_t offset, uint32_t drawCount)
Submits a batch of draw calls stored in a GPU buffer.

◆ sbgl_EndCompute()

void sbgl_EndCompute ( sbgl_Context * ctx)

Finalizes the compute phase.

This function is semantically symmetrical to sbgl_BeginCompute. It does not submit the frame; that is still handled by sbgl_EndDrawing.

Parameters
ctxThe engine context.

Definition at line 385 of file sbgl_core.c.

385 {
386 /* The compute phase finalize is currently a no-op at the core level,
387 as command buffer submission is deferred until the main frame end. */
388 (void)ctx;
389}

◆ sbgl_EndDrawing()

void sbgl_EndDrawing ( sbgl_Context * ctx)

Finalizes the current frame and presents it to the screen.

This function submits the recorded GPU commands and swaps the buffer.

Parameters
ctxThe engine context.

Definition at line 315 of file sbgl_core.c.

315 {
316 if (!ctx || !ctx->inner)
317 return;
319
320 if (inner->state.isDrawing) {
321 sbgl_gfx_EndRenderPass(inner->gfx);
322 inner->state.isDrawing = 0;
323 }
324
325 if (inner->state.hasStartedFrame) {
326 sbgl_gfx_EndFrame(inner->gfx);
327 inner->state.hasStartedFrame = 0;
328 inner->state.isIdle = 0;
329 inner->frameCount++;
330
331 uint64_t frameEndTicks = sbgl_os_GetPerfCount(inner->window);
332 inner->currentFrame.cpu_frame_time =
333 (float)((double)(frameEndTicks - inner->frameStartTicks) * 1000.0 / (double)sbgl_os_GetPerfFreq(inner->window));
334
335 // The completed telemetry data is archived for user retrieval
336 inner->lastFrame = inner->currentFrame;
337 }
338
339 // Reset pressed states for next frame
340 memset(inner->input.keysPressed, 0, sizeof(inner->input.keysPressed));
341 ctx->result = SBGL_SUCCESS;
342}
void sbgl_gfx_EndRenderPass(sbgl_GfxContext *ctx)
Ends the current graphics rendering pass.
void sbgl_gfx_EndFrame(sbgl_GfxContext *ctx)
Submits the current frame's commands and presents the image.
uint64_t sbgl_os_GetPerfFreq(sbgl_Window *window)
Gets the performance counter frequency.

◆ sbgl_FillBuffer()

void sbgl_FillBuffer ( sbgl_Context * ctx,
sbgl_Buffer buffer,
size_t offset,
size_t size,
uint32_t value )

Fills a region of a GPU buffer with a fixed 32-bit value.

This operation is performed on the GPU and is highly efficient for clearing counters or initializing large memory regions.

Parameters
ctxThe engine context.
bufferThe buffer handle.
offsetByte offset into the buffer.
sizeNumber of bytes to fill. Must be a multiple of 4.
valueThe 32-bit value to fill with.

Definition at line 480 of file sbgl_core.c.

480 {
481 if (!ctx || !ctx->inner)
482 return;
483
485 sbgl_gfx_FillBuffer(inner->gfx, buffer, offset, size, value);
486 ctx->result = SBGL_SUCCESS;
487}
void sbgl_gfx_FillBuffer(sbgl_GfxContext *ctx, sbgl_Buffer handle, size_t offset, size_t size, uint32_t value)
Performs a hardware-accelerated buffer fill.

◆ sbgl_GetBufferDeviceAddress()

uint64_t sbgl_GetBufferDeviceAddress ( sbgl_Context * ctx,
sbgl_Buffer buffer )

Retrieves the 64-bit GPU virtual address for a buffer.

This address is used for Buffer Device Address (BDA) lookups in shaders.

Parameters
ctxThe engine context.
bufferThe buffer handle.
Returns
The 64-bit GPU address, or 0 on failure.

Definition at line 689 of file sbgl_core.c.

689 {
690 if (!ctx || !ctx->inner)
691 return 0;
693 uint64_t addr = sbgl_gfx_GetBufferDeviceAddress(inner->gfx, buffer);
694 ctx->result = (addr != 0) ? SBGL_SUCCESS : SBGL_ERROR_GRAPHICS_FAILED;
695 return addr;
696}
uint64_t sbgl_gfx_GetBufferDeviceAddress(sbgl_GfxContext *ctx, sbgl_Buffer handle)
Retrieves the 64-bit GPU virtual address for a buffer.

◆ sbgl_GetContextArena()

SblArena * sbgl_GetContextArena ( sbgl_Context * ctx)

Retrieves the persistent arena associated with a context.

Subsystems that need a lifetime-bound allocation region (e.g., the voxel engine) use this arena instead of malloc to remain consistent with the library's memory model.

Parameters
ctxThe engine context.
Returns
Pointer to the context's persistent arena, or NULL if the context is invalid.

Definition at line 62 of file sbgl_core.c.

62 {
63 if (!ctx || !ctx->inner)
64 return NULL;
66 return &inner->arena;
67}

◆ sbgl_GetErrorDetail()

sbgl_ErrorDetail sbgl_GetErrorDetail ( sbgl_Context * ctx)

Retrieves detailed error information including backend-specific codes.

Parameters
ctxThe engine context (may be NULL).
Returns
Detailed error structure.

Definition at line 78 of file sbgl_core.c.

78 {
79 sbgl_ErrorDetail detail = {
80 .type = SBGL_BACKEND_VULKAN,
81 .coreResult = SBGL_SUCCESS,
82 .vkResult = 0,
83 .extension = 0,
84 };
85
86 if (!ctx) {
88 return detail;
89 }
90
91 detail.coreResult = ctx->result;
92
93 if (ctx->inner) {
95 if (inner->gfx) {
96 detail.vkResult = sbgl_gfx_GetLastVkResult(inner->gfx);
97 }
98 }
99
100 return detail;
101}
int32_t sbgl_gfx_GetLastVkResult(sbgl_GfxContext *ctx)
Retrieves the last VkResult from the backend for error inspection.
@ SBGL_ERROR_NULL_CONTEXT
Definition sbgl_types.h:216
@ SBGL_BACKEND_VULKAN
Definition sbgl_types.h:244
Detailed error information for debugging.
Definition sbgl_types.h:253
sbgl_Result coreResult
Definition sbgl_types.h:255

◆ sbgl_GetFrameIndex()

uint32_t sbgl_GetFrameIndex ( sbgl_Context * ctx)

Retrieves the current frame index for double/triple buffering.

Parameters
ctxThe engine context.
Returns
The current frame index (typically 0 or 1).

Definition at line 489 of file sbgl_core.c.

489 {
490 if (!ctx || !ctx->inner)
491 return 0;
492
494 return sbgl_gfx_GetFrameIndex(inner->gfx);
495}
uint32_t sbgl_gfx_GetFrameIndex(sbgl_GfxContext *ctx)
Retrieves the current backend frame index.

◆ sbgl_GetInputState()

const sbgl_InputState * sbgl_GetInputState ( sbgl_Context * ctx)

Retrieves the input state for the current frame.

Adheres to Data-Oriented Design by providing a read-only view of input arrays (keys, mouse) for batch processing.

Parameters
ctxThe engine context.
Returns
A read-only pointer to the current input state. Never returns NULL.

Definition at line 413 of file sbgl_core.c.

413 {
414 static const sbgl_InputState dummy = { 0 };
415 if (!ctx || !ctx->inner)
416 return &dummy;
417
419 return &inner->input;
420}
Represents the real-time state of physical inputs.
Definition sbgl_input.h:165

◆ sbgl_GetResult()

sbgl_Result sbgl_GetResult ( sbgl_Context * ctx)

Retrieves the last result code from the context.

Parameters
ctxThe engine context (may be NULL).
Returns
The result code, or SBGL_ERROR_NULL_CONTEXT if ctx is NULL.

Definition at line 71 of file sbgl_core.c.

71 {
72 if (!ctx) {
74 }
75 return ctx->result;
76}

◆ sbgl_GetTelemetry()

sbgl_Telemetry sbgl_GetTelemetry ( sbgl_Context * ctx)

Retrieves the performance telemetry data for the previous frame.

Parameters
ctxThe active engine context.
Returns
A structure containing CPU and GPU timing data.

Definition at line 436 of file sbgl_core.c.

436 {
437 if (!ctx || !ctx->inner) {
438 return (sbgl_Telemetry){ 0 };
439 }
441 return inner->lastFrame;
442}
Performance telemetry data for a single frame.
Definition sbgl_types.h:175

◆ sbgl_GetTime()

double sbgl_GetTime ( sbgl_Context * ctx)

Retrieves the current monotonic system time in seconds.

This timer is high-resolution and suitable for frame-time calculation.

Parameters
ctxThe active engine context.
Returns
Monotonic time in seconds.

Definition at line 245 of file sbgl_core.c.

245 {
246 if (!ctx || !ctx->inner)
247 return 0.0;
249 return (double)sbgl_os_GetPerfCount(inner->window) / (double)sbgl_os_GetPerfFreq(inner->window);
250}

◆ sbgl_GetWindowSize()

void sbgl_GetWindowSize ( sbgl_Context * ctx,
int * w,
int * h )

Retrieves the current window dimensions.

Parameters
ctxThe active engine context.
wPointer to store the width.
hPointer to store the height.

Definition at line 252 of file sbgl_core.c.

252 {
253 if (w)
254 *w = 0;
255 if (h)
256 *h = 0;
257 if (!ctx || !ctx->inner)
258 return;
260 sbgl_os_GetWindowSize(inner->window, w, h);
261}
void sbgl_os_GetWindowSize(sbgl_Window *window, int *w, int *h)
Retrieves the current client area size.

◆ sbgl_Init()

sbgl_InitResult sbgl_Init ( int w,
int h,
const char * title )

Initializes the engine and opens a window.

This function sets up the internal HALs and the Vulkan 1.3 backend. It is a convenience wrapper around sbgl_InitWithConfig() that uses default resource limits and enables validation in debug builds.

Parameters
wDesired width of the window.
hDesired height of the window.
titleTitle displayed in the OS window manager.
Returns
An initialization result containing the context and any error code.

Definition at line 193 of file sbgl_core.c.

193 {
194 sbgl_InitConfig config = {
195 .windowWidth = w,
196 .windowHeight = h,
197 .windowTitle = title,
198 .limits = { .maxBuffers = 1024, .maxShaders = 256, .maxPipelines = 256 },
199 .enableValidation = true
200 };
201 return sbgl_InitWithConfig(&config);
202}
sbgl_InitResult sbgl_InitWithConfig(const sbgl_InitConfig *config)
Initializes the engine and opens a window with explicit configuration.
Definition sbgl_core.c:112
Configuration for engine initialization.
Definition sbgl_types.h:308

◆ sbgl_InitWithConfig()

sbgl_InitResult sbgl_InitWithConfig ( const sbgl_InitConfig * config)

Initializes the engine and opens a window with explicit configuration.

This function sets up the internal HALs and the Vulkan 1.3 backend using the provided configuration. This allows fine-grained control over resource limits and validation settings.

Parameters
configPointer to the initialization configuration.
Returns
An initialization result containing the context and any error code.

Definition at line 112 of file sbgl_core.c.

112 {
113 sbgl_InitResult res = { .ctx = NULL, .error = SBGL_SUCCESS };
114
115 if (!config) {
118 "sbgl_InitWithConfig: NULL config passed");
119 return res;
120 }
121
122 SblArena main_arena;
123 if (!sbl_arena_init(&main_arena, 4 * 1024 * 1024)) {
126 "sbgl_InitWithConfig: arena initialization failed");
127 return res;
128 }
129
131 if (!ctx) {
132 sbl_arena_free(&main_arena);
135 "sbgl_InitWithConfig: context allocation failed");
136 return res;
137 }
138
140 if (!inner) {
141 sbl_arena_free(&main_arena);
144 "sbgl_InitWithConfig: inner context allocation failed");
145 return res;
146 }
147
148 inner->arena = main_arena;
149 sbl_arena_init(&inner->transientArena, 16 * 1024 * 1024);
150 inner->clearColor[0] = 0.0f;
151 inner->clearColor[1] = 0.0f;
152 inner->clearColor[2] = 0.0f;
153 inner->clearColor[3] = 0.0f;
154 inner->state.isIdle = 1;
156 inner->state.wasFocused = 0;
157
158 ctx->inner = inner;
159 ctx->result = SBGL_SUCCESS;
160
161 const char* title = config->windowTitle ? config->windowTitle : "SBgl";
163 &inner->arena, &inner->input,
164 config->windowWidth, config->windowHeight, title,
165 &inner->window
166 );
167
168 if (platformRes != SBGL_PLATFORM_SUCCESS) {
171 "sbgl_InitWithConfig: window creation failed");
172 sbl_arena_free(&inner->arena);
173 return res;
174 }
175
176 inner->gfx = sbgl_gfx_Init(inner->window, &inner->arena, &config->limits, config->enableValidation);
177 if (!inner->gfx) {
180 "sbgl_InitWithConfig: graphics initialization failed");
182 sbl_arena_free(&inner->arena);
183 return res;
184 }
185
187 "sbgl_InitWithConfig: initialization successful");
188
189 res.ctx = ctx;
190 return res;
191}
@ SBGL_MOUSE_MODE_NORMAL
Definition sbgl_input.h:154
sbgl_GfxContext * sbgl_gfx_Init(sbgl_Window *window, struct SblArena *arena, const sbgl_ResourceLimits *limits, bool enableValidation)
Initializes the graphics backend with configurable resource limits.
@ SBGL_LOG_CAT_PLATFORM
@ SBGL_LOG_CAT_CORE
@ SBGL_LOG_INFO
sbgl_platform_Result sbgl_os_CreateWindow(struct SblArena *arena, sbgl_InputState *input, int width, int height, const char *title, sbgl_Window **outWindow)
Creates a native OS window.
void sbgl_os_DestroyWindow(sbgl_Window *window)
Destroys a native window.
sbgl_platform_Result
Result codes for platform layer operations.
Definition sbgl_types.h:233
@ SBGL_PLATFORM_SUCCESS
Definition sbgl_types.h:234
@ SBGL_ERROR_WINDOW_CREATION_FAILED
Definition sbgl_types.h:219
@ SBGL_ERROR_INVALID_ARGUMENT
Definition sbgl_types.h:217
SBL_ARENA_DEF void sbl_arena_free(SblArena *arena)
SBL_ARENA_DEF bool sbl_arena_init(SblArena *arena, uint64_t initial_size)
Arena allocator.
Definition sbl_arena.h:47
Engine context.
Definition sbgl_types.h:268
uint32_t windowWidth
Definition sbgl_types.h:309
sbgl_ResourceLimits limits
Definition sbgl_types.h:312
const char * windowTitle
Definition sbgl_types.h:311
uint32_t windowHeight
Definition sbgl_types.h:310
Result structure for initialization.
Definition sbgl_types.h:339
sbgl_Context * ctx
Definition sbgl_types.h:340
sbgl_Result error
Definition sbgl_types.h:341
sbgl_GfxContext * gfx
Definition sbgl_core.c:44
sbgl_Window * window
Definition sbgl_core.c:43
struct sbgl_InternalContext::@8 state
sbgl_MouseMode mouseMode
Definition sbgl_core.c:54
SblArena transientArena
Definition sbgl_core.c:42
sbgl_InputState input
Definition sbgl_core.c:53

◆ sbgl_LoadShader()

sbgl_Shader sbgl_LoadShader ( sbgl_Context * ctx,
sbgl_ShaderStage stage,
const uint32_t * bytecode,
size_t size )

Loads a shader from SPIR-V bytecode.

Parameters
ctxThe engine context.
stageThe shader stage (Vertex/Fragment).
bytecodePointer to the SPIR-V bytecode.
sizeSize of the bytecode in bytes.
Returns
A handle to the loaded shader.

Definition at line 523 of file sbgl_core.c.

523 {
524 if (!ctx || !ctx->inner)
525 return SBGL_INVALID_HANDLE;
527 sbgl_Shader res = sbgl_gfx_LoadShader(inner->gfx, stage, bytecode, size);
528 ctx->result =
530 return res;
531}
sbgl_Shader sbgl_gfx_LoadShader(sbgl_GfxContext *ctx, sbgl_ShaderStage stage, const uint32_t *bytecode, size_t size)
uint32_t sbgl_Shader
Handle for a shader module.
Definition sbgl_types.h:42

◆ sbgl_LoadShaderFromFile()

sbgl_Shader sbgl_LoadShaderFromFile ( sbgl_Context * ctx,
sbgl_ShaderStage stage,
const char * filename )

Helper function to load a shader directly from a SPIR-V file.

Parameters
ctxThe engine context.
stageThe shader stage (Vertex/Fragment).
filenamePath to the SPIR-V file.
Returns
A handle to the loaded shader, or SBGL_INVALID_HANDLE on failure.

Definition at line 534 of file sbgl_core.c.

534 {
535 FILE* file = fopen(filename, "rb");
536 if (!file) {
537 sbgl_log_impl(SBGL_LOG_ERROR, SBGL_LOG_CAT_GFX, "Failed to open shader file.");
538 return SBGL_INVALID_HANDLE;
539 }
540
541 fseek(file, 0, SEEK_END);
542 size_t size = ftell(file);
543 fseek(file, 0, SEEK_SET);
544
545 uint32_t* buffer = malloc(size);
546 if (!buffer) {
547 fclose(file);
548 return SBGL_INVALID_HANDLE;
549 }
550
551 size_t read = fread(buffer, 1, size, file);
552 fclose(file);
553
554 if (read != size) {
555 free(buffer);
556 return SBGL_INVALID_HANDLE;
557 }
558
559 sbgl_Shader shader = sbgl_LoadShader(ctx, stage, buffer, size);
560 free(buffer);
561
562 return shader;
563}
sbgl_Shader sbgl_LoadShader(sbgl_Context *ctx, sbgl_ShaderStage stage, const uint32_t *bytecode, size_t size)
Loads a shader from SPIR-V bytecode.
Definition sbgl_core.c:523

◆ sbgl_MapBuffer()

void * sbgl_MapBuffer ( sbgl_Context * ctx,
sbgl_Buffer buffer )

Maps a GPU buffer into the CPU's address space.

This allows for direct reading from and writing to GPU memory. SBgl buffers are created as host-visible and coherent by default.

Parameters
ctxThe engine context.
bufferThe buffer handle.
Returns
A pointer to the mapped memory, or NULL on failure.

Definition at line 497 of file sbgl_core.c.

497 {
498 /* The system maps the specified GPU buffer into the CPU's address space,
499 allowing for direct memory access. This is essential for reading back
500 results from compute shaders or updating dynamic vertex data. */
501 if (!ctx || !ctx->inner)
502 return NULL;
503
505 void* ptr = sbgl_gfx_MapBuffer(inner->gfx, buffer);
506
507 ctx->result = (ptr != NULL) ? SBGL_SUCCESS : SBGL_ERROR_GRAPHICS_FAILED;
508 return ptr;
509}
void * sbgl_gfx_MapBuffer(sbgl_GfxContext *ctx, sbgl_Buffer handle)

◆ sbgl_MemoryBarrier()

void sbgl_MemoryBarrier ( sbgl_Context * ctx,
sbgl_BarrierType type )

Injects a memory barrier to synchronize compute and graphics operations.

Parameters
ctxThe engine context.
typeThe type of barrier to inject.

Definition at line 665 of file sbgl_core.c.

665 {
666 if (!ctx || !ctx->inner)
667 return;
669 sbgl_gfx_MemoryBarrier(inner->gfx, type);
670 ctx->result = SBGL_SUCCESS;
671}
void sbgl_gfx_MemoryBarrier(sbgl_GfxContext *ctx, sbgl_BarrierType type)

◆ sbgl_PushConstants()

void sbgl_PushConstants ( sbgl_Context * ctx,
size_t size,
const void * data )

Updates push constants for the currently bound pipeline.

Parameters
ctxThe engine context.
sizeSize of the data to push.
dataPointer to the data.

Definition at line 733 of file sbgl_core.c.

733 {
734 if (!ctx || !ctx->inner)
735 return;
737 sbgl_gfx_PushConstants(inner->gfx, size, data);
738 ctx->result = SBGL_SUCCESS;
739}
void sbgl_gfx_PushConstants(sbgl_GfxContext *ctx, size_t size, const void *data)

◆ sbgl_RenderQueues()

void sbgl_RenderQueues ( sbgl_Context * ctx,
sbgl_RenderQueue ** queues,
uint32_t queueCount,
const sbgl_Mat4 * viewProj )

Merges, sorts, and submits pending draw commands to the GPU.

This function processes render queues in a batching operation. A stable radix sort is performed on the packets to minimize state transitions, followed by baking them into indirect draw commands.

Parameters
ctxThe engine context.
queuesArray of pointers to the render queues to be processed.
queueCountNumber of queues in the array.
viewProjPointer to the view-projection matrix for the frame.

Definition at line 808 of file sbgl_core.c.

813 {
814 sbgl_RenderQueuesEx(ctx, queues, queueCount, viewProj, 0);
815}
void sbgl_RenderQueuesEx(sbgl_Context *ctx, sbgl_RenderQueue **queues, uint32_t queueCount, const sbgl_Mat4 *viewProj, uint64_t userAddress)
Extended version of sbgl_RenderQueues with user metadata.
Definition sbgl_core.c:817

◆ sbgl_RenderQueuesEx()

void sbgl_RenderQueuesEx ( sbgl_Context * ctx,
sbgl_RenderQueue ** queues,
uint32_t queueCount,
const sbgl_Mat4 * viewProj,
uint64_t userAddress )

Extended version of sbgl_RenderQueues with user metadata.

Parameters
ctxThe engine context.
queuesArray of render queues.
queueCountNumber of queues.
viewProjView-projection matrix.
userAddressA 64-bit GPU address for user-defined data (e.g., SSBO heightmap).

Definition at line 817 of file sbgl_core.c.

823 {
824 if (!ctx || !ctx->inner || !queues || queueCount == 0) {
825 return;
826 }
827
829
830 // Total packet count is calculated across all queues to determine buffer requirements
831 uint32_t totalPackets = 0;
832 for (uint32_t i = 0; i < queueCount; ++i) {
833 if (queues[i]) {
834 totalPackets += queues[i]->count;
835 }
836 }
837
838 if (totalPackets == 0) {
839 return;
840 }
841
842 // The transient arena is reset for this frame's batching work
843 sbl_arena_reset(&inner->transientArena);
844
845 inner->sortStartTicks = sbgl_os_GetPerfCount(inner->window);
846
847 // Temporary host memory is allocated for merging and sorting from the transient arena
848 sbgl_DrawPacket* mergedPackets =
849 SBL_ARENA_PUSH_ARRAY(&inner->transientArena, sbgl_DrawPacket, totalPackets);
850 sbgl_InstanceData* mergedInstances =
851 SBL_ARENA_PUSH_ARRAY(&inner->transientArena, sbgl_InstanceData, totalPackets);
852 sbgl_SortKey* keys = SBL_ARENA_PUSH_ARRAY(&inner->transientArena, sbgl_SortKey, totalPackets);
853 uint32_t* indices = SBL_ARENA_PUSH_ARRAY(&inner->transientArena, uint32_t, totalPackets);
854
855 if (!mergedPackets || !mergedInstances || !keys || !indices) {
857 return;
858 }
859
860 // Packets and instance data are merged from all queues
861 uint32_t current = 0;
862 for (uint32_t i = 0; i < queueCount; ++i) {
863 if (!queues[i])
864 continue;
865 for (uint32_t j = 0; j < queues[i]->count; ++j) {
866 mergedPackets[current] = queues[i]->packets[j];
867 mergedInstances[current] = queues[i]->instances[j];
868 keys[current] = mergedPackets[current].key;
869 indices[current] = current;
870 current++;
871 }
872 // The queue is cleared for the next frame
873 queues[i]->count = 0;
874 }
875
876 // A stable radix sort is performed on the packets based on sort keys
877 sbgl_SortKey* temp_keys =
878 SBL_ARENA_PUSH_ARRAY(&inner->transientArena, sbgl_SortKey, totalPackets);
879 uint32_t* temp_indices = SBL_ARENA_PUSH_ARRAY(&inner->transientArena, uint32_t, totalPackets);
880 if (!temp_keys || !temp_indices) {
882 return;
883 }
884
885 sbgl_radix_sort(keys, indices, totalPackets, temp_keys, temp_indices);
886
887 // Packets and instances are reordered according to the sorted indices
888 sbgl_DrawPacket* sortedPackets =
889 SBL_ARENA_PUSH_ARRAY(&inner->transientArena, sbgl_DrawPacket, totalPackets);
890 sbgl_InstanceData* sortedInstances =
891 SBL_ARENA_PUSH_ARRAY(&inner->transientArena, sbgl_InstanceData, totalPackets);
892 if (!sortedPackets || !sortedInstances) {
894 return;
895 }
896
897 for (uint32_t i = 0; i < totalPackets; ++i) {
898 sortedPackets[i] = mergedPackets[indices[i]];
899 sortedInstances[i] = mergedInstances[indices[i]];
900 }
901
902 // Sorted packets are baked into optimized indirect draw commands
903 sbgl_IndirectCommand* commands =
904 SBL_ARENA_PUSH_ARRAY(&inner->transientArena, sbgl_IndirectCommand, totalPackets);
905 if (!commands) {
907 return;
908 }
909
910 uint32_t commandCount = sbgl_bake_commands(sortedPackets, totalPackets, commands, totalPackets);
911
912 uint64_t sortEndTicks = sbgl_os_GetPerfCount(inner->window);
913 inner->currentFrame.cpu_sort_time +=
914 (float)((double)(sortEndTicks - inner->sortStartTicks) * 1000.0 / (double)sbgl_os_GetPerfFreq(inner->window));
915
916 inner->currentFrame.draw_calls += commandCount;
917 inner->currentFrame.instance_count += totalPackets;
918
919 if (commandCount > 0) {
920 // Allocate transient GPU space for packed instance data
922 inner->gfx,
923 sizeof(sbgl_InstanceData) * totalPackets,
924 256 // Storage buffer alignment
925 );
926
927 if (instanceAlloc.buffer != SBGL_INVALID_HANDLE) {
928 memcpy(instanceAlloc.mapped, sortedInstances, sizeof(sbgl_InstanceData) * totalPackets);
929
930 // Retrieve BDA address and update push constants
931 uint64_t bdaAddress = instanceAlloc.deviceAddress;
932
933 struct {
934 sbgl_Mat4 viewProj;
935 uint64_t instanceAddress;
936 uint64_t userAddress;
937 } pc;
938 pc.viewProj = viewProj ? *viewProj : sbgl_Mat4Identity();
939 pc.instanceAddress = bdaAddress;
940 pc.userAddress = userAddress;
941
942 sbgl_gfx_PushConstants(inner->gfx, sizeof(pc), &pc);
943
944 // Allocate transient GPU space for baked commands and dispatch MDI
946 inner->gfx,
947 sizeof(sbgl_IndirectCommand) * commandCount,
948 16 // Indirect buffer alignment
949 );
950
951 if (indirectAlloc.buffer != SBGL_INVALID_HANDLE) {
952 memcpy(indirectAlloc.mapped, commands, sizeof(sbgl_IndirectCommand) * commandCount);
954 inner->gfx,
955 indirectAlloc.buffer,
956 indirectAlloc.offset,
957 commandCount
958 );
959 }
960 }
961 }
962
963 ctx->result = SBGL_SUCCESS;
964}
sbgl_GfxTransientAllocation sbgl_gfx_AllocateTransient(sbgl_GfxContext *ctx, size_t size, uint32_t alignment)
Allocates a slice of GPU-visible memory for transient per-frame data.
uint32_t sbgl_bake_commands(const sbgl_DrawPacket *packets, uint32_t packetCount, sbgl_IndirectCommand *outCommands, uint32_t maxCommands)
Definition sbgl_batcher.c:3
static sbgl_Mat4 sbgl_Mat4Identity(void)
Returns an identity matrix.
Definition sbgl_math.h:200
void sbgl_radix_sort(sbgl_SortKey *keys, uint32_t *values, uint32_t count, sbgl_SortKey *temp_keys, uint32_t *temp_values)
Performs a stable radix sort on an array of 64-bit keys and associated 32-bit values.
Definition sbgl_sort.c:8
uint64_t sbgl_SortKey
Bit-packed key used for sorting draw commands to minimize state changes.
Definition sbgl_types.h:57
SBL_ARENA_DEF void sbl_arena_reset(SblArena *arena)
#define SBL_ARENA_PUSH_ARRAY(arena, type, count)
Definition sbl_arena.h:21
sbgl_SortKey key
Definition sbgl_types.h:98
Represents a slice of a persistent GPU buffer used for transient data.
Standard Vulkan Indirect Draw command layout.
Definition sbgl_types.h:111
4x4 Matrix, 16-byte aligned, column-major.
Definition sbgl_math.h:83

◆ sbgl_SetClearColor()

void sbgl_SetClearColor ( sbgl_Context * ctx,
float r,
float g,
float b,
float a )

Sets the clear color for the next frame.

Parameters
ctxThe engine context.
rRed component (0.0 to 1.0).
gGreen component (0.0 to 1.0).
bBlue component (0.0 to 1.0).
aAlpha component (0.0 to 1.0).

Definition at line 401 of file sbgl_core.c.

401 {
402 if (!ctx || !ctx->inner)
403 return;
404
406 inner->clearColor[0] = r;
407 inner->clearColor[1] = g;
408 inner->clearColor[2] = b;
409 inner->clearColor[3] = a;
410 ctx->result = SBGL_SUCCESS;
411}

◆ sbgl_SetMouseMode()

void sbgl_SetMouseMode ( sbgl_Context * ctx,
sbgl_MouseMode mode )

Sets the cursor behavior and visibility.

Parameters
ctxThe engine context.
modeThe desired mouse mode.

Definition at line 422 of file sbgl_core.c.

422 {
423 if (!ctx || !ctx->inner)
424 return;
426 inner->mouseMode = mode;
427
428 bool visible = (mode == SBGL_MOUSE_MODE_NORMAL);
429 bool locked = (mode == SBGL_MOUSE_MODE_CAPTURED);
430
431 sbgl_os_SetCursorVisible(inner->window, visible);
432 sbgl_os_SetCursorLocked(inner->window, locked);
433 ctx->result = SBGL_SUCCESS;
434}

◆ sbgl_Shutdown()

void sbgl_Shutdown ( sbgl_Context * ctx)

Gracefully shuts down the engine and releases all resources.

Parameters
ctxThe context to destroy.

Definition at line 204 of file sbgl_core.c.

204 {
205 if (!ctx) {
207 "sbgl_Shutdown: NULL context");
208 return;
209 }
210 if (!ctx->inner) {
213 "sbgl_Shutdown: uninitialized context");
214 return;
215 }
217
218 sbgl_log_impl(SBGL_LOG_INFO, SBGL_LOG_CAT_CORE, "Initiating shutdown...");
219
220 if (!inner->state.isIdle) {
222 }
223
224 if (inner->gfx)
225 sbgl_gfx_Shutdown(inner->gfx);
226 if (inner->window)
227 sbgl_os_DestroyWindow(inner->window);
228
229 sbl_arena_free(&inner->transientArena);
230
231 // The context itself is allocated within inner->arena,
232 // so we cannot access it after the arena is freed.
233 ctx->result = SBGL_SUCCESS;
234
235 sbl_arena_free(&inner->arena);
236}
void sbgl_gfx_Shutdown(sbgl_GfxContext *ctx)

◆ sbgl_SubmitDraw()

void sbgl_SubmitDraw ( sbgl_RenderQueue * queue,
uint32_t mesh,
uint32_t material,
uint32_t blendMode,
uint32_t sidedness,
uint32_t tags,
sbgl_SortKey key,
const sbgl_InstanceData * data )

Appends a draw command to the render queue.

The packets are cached in the queue until the next frame.

Parameters
queueThe render queue to append to.
meshThe mesh identifier.
materialThe material identifier.
blendModeThe blend mode identifier.
sidednessThe sidedness flag (e.g., single/double sided).
tagsCustom user tags for filtering.
keyThe sort key for minimizing state changes.
dataPointer to the per-instance data (transform, color, etc).

Definition at line 772 of file sbgl_core.c.

781 {
782 if (!queue) {
783 return;
784 }
785
786 // Verify the queue has not reached maximum capacity
787 if (queue->count >= queue->capacity) {
788 return;
789 }
790
791 // The system appends the draw packet to the contiguous array
792 uint32_t index = queue->count++;
793 sbgl_DrawPacket* packet = &queue->packets[index];
794 packet->key = key;
795 packet->header = SBGL_PACK_HEADER(mesh, material, blendMode, sidedness, tags);
796
797 // Per-instance data is copied into the parallel array
798 if (data) {
799 queue->instances[index] = *data;
800 } else {
801 // An identity transform and white color are used if no data is provided
802 memset(&queue->instances[index], 0, sizeof(sbgl_InstanceData));
803 queue->instances[index].transform = sbgl_Mat4Identity();
804 queue->instances[index].color = sbgl_Vec4Set(1, 1, 1, 1);
805 }
806}
static sbgl_Vec4 sbgl_Vec4Set(float x, float y, float z, float w)
Creates a Vec4.
Definition sbgl_math.h:98
#define SBGL_PACK_HEADER(mesh, mat, blend, sided, tags)
Definition sbgl_types.h:82
uint32_t header
Definition sbgl_types.h:99
sbgl_Mat4 transform
Definition sbgl_types.h:20
sbgl_Vec4 color
Definition sbgl_types.h:21

◆ sbgl_UnmapBuffer()

void sbgl_UnmapBuffer ( sbgl_Context * ctx,
sbgl_Buffer buffer )

Unmaps a previously mapped GPU buffer.

Parameters
ctxThe engine context.
bufferThe buffer handle.

Definition at line 511 of file sbgl_core.c.

511 {
512 /* The mapping between the CPU and GPU buffer is released, finalizing any
513 pending memory writes and ensuring synchronization for subsequent GPU operations. */
514 if (!ctx || !ctx->inner)
515 return;
516
518 sbgl_gfx_UnmapBuffer(inner->gfx, buffer);
519 ctx->result = SBGL_SUCCESS;
520}
void sbgl_gfx_UnmapBuffer(sbgl_GfxContext *ctx, sbgl_Buffer handle)

◆ sbgl_WindowShouldClose()

bool sbgl_WindowShouldClose ( sbgl_Context * ctx)

Checks if the user or OS has requested to close the window.

This flag is set by the OS (for instance, clicking the 'X' button or Alt+F4). The application polls this flag in the main loop and manually initiates shutdown by calling sbgl_Shutdown() when it returns true.

Parameters
ctxThe active engine context.
Returns
True if a close request is pending, false otherwise.

Definition at line 238 of file sbgl_core.c.

238 {
239 if (!ctx || !ctx->inner)
240 return true;
242 return sbgl_os_WindowShouldClose(inner->window);
243}
bool sbgl_os_WindowShouldClose(sbgl_Window *window)
Checks the window's close flag.