SBgl 0.1.0
A graphics framework in C99
Loading...
Searching...
No Matches
sbgl_voxel.h File Reference

Public API for the SBgl voxel rendering system. More...

#include "sbgl.h"
Include dependency graph for sbgl_voxel.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  sbgl_VoxelConfig
 Configuration parameters for initializing the voxel system. More...
 
struct  sbgl_Material
 

Typedefs

typedef struct sbgl_VoxelSystem sbgl_VoxelSystem
 Opaque handle to the voxel system. The internal structure is hidden to maintain a clean public API and facilitate backend-agnostic module development.
 

Functions

sbgl_VoxelSystemsbgl_Voxel_Create (sbgl_Context *ctx, const sbgl_VoxelConfig *config)
 Creates and initializes a new voxel system. This operation allocates GPU resources and internal tracking structures.
 
void sbgl_Voxel_Update (sbgl_VoxelSystem *sys, sbgl_Vec3 camera_pos)
 Updates the voxel system state based on the current camera position. This function handles chunk generation requests and manages the LRU cache.
 
void sbgl_Voxel_Cull (sbgl_VoxelSystem *sys, sbgl_Mat4 view_proj)
 Performs GPU-driven frustum culling on voxel chunks. MUST be called BEFORE sbgl_BeginDrawing.
 
void sbgl_Voxel_Render (sbgl_VoxelSystem *sys)
 Issues the indirect draw calls for visible voxel chunks. MUST be called BETWEEN sbgl_BeginDrawing and sbgl_EndDrawing.
 
void sbgl_Voxel_Destroy (sbgl_VoxelSystem *sys)
 Destroys the voxel system and releases all associated resources.
 
uint64_t sbgl_Voxel_GetAABBAddress (sbgl_VoxelSystem *sys)
 Returns the device address of the AABB buffer.
 
uint64_t sbgl_Voxel_GetInstanceAddress (sbgl_VoxelSystem *sys)
 Returns the device address of the instance buffer.
 
uint64_t sbgl_Voxel_GetPaletteAddress (sbgl_VoxelSystem *sys)
 Returns the device address of the material palette buffer.
 

Detailed Description

Public API for the SBgl voxel rendering system.

Definition in file sbgl_voxel.h.

Typedef Documentation

◆ sbgl_VoxelSystem

typedef struct sbgl_VoxelSystem sbgl_VoxelSystem

Opaque handle to the voxel system. The internal structure is hidden to maintain a clean public API and facilitate backend-agnostic module development.

Definition at line 16 of file sbgl_voxel.h.

Function Documentation

◆ sbgl_Voxel_Create()

sbgl_VoxelSystem * sbgl_Voxel_Create ( sbgl_Context * ctx,
const sbgl_VoxelConfig * config )

Creates and initializes a new voxel system. This operation allocates GPU resources and internal tracking structures.

Parameters
ctxPointer to the active SBgl context.
configPointer to the configuration parameters.
Returns
Pointer to the initialized voxel system, or NULL if creation failed.

Definition at line 143 of file sbgl_voxel.c.

143 {
144 /*
145 * The voxel system is allocated from the context's persistent arena
146 * to remain consistent with the library's memory model.
147 */
148 SblArena* ctxArena = sbgl_GetContextArena(ctx);
149 if (!ctxArena)
150 return NULL;
151
153 if (!sys)
154 return NULL;
155
156 sys->ctx = ctx;
157 sys->chunk_radius = config->chunk_radius;
158 sys->enable_telemetry = config->enable_telemetry;
159 sys->last_update_time = sbgl_GetTime(ctx);
160
161 /*
162 * Initialize the voxel pool with the requested capacity.
163 * We utilize a dedicated internal arena for the CPU-side pool management metadata.
164 */
165 sbl_arena_init(&sys->poolArena, 1 * 1024 * 1024);
166 sys->pool = VoxelPool_Init(&sys->poolArena, config->max_slots);
167
168 if (!sys->pool) {
170 return NULL;
171 }
172
173 /*
174 * Allocate a CPU-side mirror of the AABB data. This avoids the read-write
175 * hazard that occurs when mapping the previous frame's GPU buffer while
176 * the GPU may still be consuming it.
177 */
179 if (!sys->cpuAABB) {
181 return NULL;
182 }
183
184 /*
185 * Initialize the camera tracking state to force an initial update.
186 */
187 sys->last_cam_chunk = (sbgl_ivec3){ -1000000, -1000000, -1000000 };
188
189 /*
190 * Load the compute shaders required for the voxel processing pipeline.
191 * These shaders handle generation, shell extraction, and frustum culling.
192 */
193 sbgl_Shader genShader = sbgl_LoadShader(ctx, SBGL_SHADER_STAGE_COMPUTE, (const uint32_t*)voxel_gen_comp_spv, voxel_gen_comp_spv_len);
194 sbgl_Shader shellShader = sbgl_LoadShader(ctx, SBGL_SHADER_STAGE_COMPUTE, (const uint32_t*)voxel_mesh_comp_spv, voxel_mesh_comp_spv_len);
195 sbgl_Shader cullShader = sbgl_LoadShader(ctx, SBGL_SHADER_STAGE_COMPUTE, (const uint32_t*)voxel_cull_comp_spv, voxel_cull_comp_spv_len);
196
197 if (genShader == SBGL_INVALID_HANDLE || shellShader == SBGL_INVALID_HANDLE || cullShader == SBGL_INVALID_HANDLE) {
198 if (genShader != SBGL_INVALID_HANDLE) sbgl_DestroyShader(ctx, genShader);
199 if (shellShader != SBGL_INVALID_HANDLE) sbgl_DestroyShader(ctx, shellShader);
200 if (cullShader != SBGL_INVALID_HANDLE) sbgl_DestroyShader(ctx, cullShader);
202 return NULL;
203 }
204
205 /*
206 * Create the compute pipelines. Once created, the shader modules can be released.
207 */
208 sys->genPipe = sbgl_CreateComputePipeline(ctx, genShader);
209 sys->shellPipe = sbgl_CreateComputePipeline(ctx, shellShader);
210 sys->cullPipe = sbgl_CreateComputePipeline(ctx, cullShader);
211
212 sbgl_DestroyShader(ctx, genShader);
213 sbgl_DestroyShader(ctx, shellShader);
214 sbgl_DestroyShader(ctx, cullShader);
215
216 /*
217 * Allocate the GPU-side buffers. The mask and instance buffers store the voxel
218 * data, while the AABB buffer is used for GPU-driven culling.
219 */
221 sys->instBuf = sbgl_CreateBuffer(ctx, SBGL_BUFFER_USAGE_STORAGE, config->max_slots * 65536 * sizeof(uint32_t) * 2, NULL);
222 for (int i = 0; i < 2; ++i) {
223 sys->aabbBuf[i] = sbgl_CreateBuffer(ctx, SBGL_BUFFER_USAGE_STORAGE, config->max_slots * sizeof(sbgl_AABB), NULL);
224 void* mappedAABB = sbgl_MapBuffer(ctx, sys->aabbBuf[i]);
225 if (mappedAABB) {
226 memset(mappedAABB, 0, config->max_slots * sizeof(sbgl_AABB));
227 sbgl_UnmapBuffer(ctx, sys->aabbBuf[i]);
228 }
229 }
230
231 /*
232 * Initialize the material palette with default terrain attributes.
233 * This provides a consistent base for procedural generation and shading.
234 */
237 if (palette) {
238 memset(palette, 0, 64 * sizeof(sbgl_Material));
239
240 /* Index 0: Grass (Greenish) */
241 palette[0].color = sbgl_Vec4Set(0.3f, 0.6f, 0.2f, 1.0f);
242 palette[0].roughness = 0.8f;
243 palette[0].metalness = 0.0f;
244
245 /* Index 1: Dirt (Brownish) */
246 palette[1].color = sbgl_Vec4Set(0.4f, 0.3f, 0.2f, 1.0f);
247 palette[1].roughness = 0.9f;
248 palette[1].metalness = 0.0f;
249
250 /* Index 2: Stone (Grey) */
251 palette[2].color = sbgl_Vec4Set(0.5f, 0.5f, 0.5f, 1.0f);
252 palette[2].roughness = 0.6f;
253 palette[2].metalness = 0.0f;
254
255 /* Index 3: Sand (Yellowish) */
256 palette[3].color = sbgl_Vec4Set(0.8f, 0.7f, 0.4f, 1.0f);
257 palette[3].roughness = 0.7f;
258 palette[3].metalness = 0.0f;
259
261 }
262
263 /*
264 * Initialize control buffers.
265 */
267 void* mappedShell = sbgl_MapBuffer(ctx, sys->shellCountsBuf);
268 if (mappedShell) {
269 memset(mappedShell, 0, config->max_slots * sizeof(uint32_t));
271 }
272
273 for (int i = 0; i < 2; ++i) {
275
276 void* mappedCmd = sbgl_MapBuffer(ctx, sys->indirectCmdBuf[i]);
277 if (mappedCmd) {
278 memset(mappedCmd, 0, config->max_slots * sizeof(sbgl_IndirectCommand));
279 sbgl_UnmapBuffer(ctx, sys->indirectCmdBuf[i]);
280 }
281 }
282
283 return sys;
284}
void sbgl_DestroyShader(sbgl_Context *ctx, sbgl_Shader shader)
Destroys a shader module.
Definition sbgl_core.c:565
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_ComputePipeline sbgl_CreateComputePipeline(sbgl_Context *ctx, sbgl_Shader shader)
Creates a compute pipeline.
Definition sbgl_core.c:619
void * sbgl_MapBuffer(sbgl_Context *ctx, sbgl_Buffer buffer)
Maps a GPU buffer into the CPU's address space.
Definition sbgl_core.c:497
void sbgl_UnmapBuffer(sbgl_Context *ctx, sbgl_Buffer buffer)
Unmaps a previously mapped GPU buffer.
Definition sbgl_core.c:511
double sbgl_GetTime(sbgl_Context *ctx)
Retrieves the current monotonic system time in seconds.
Definition sbgl_core.c:245
sbgl_Buffer sbgl_CreateBuffer(sbgl_Context *ctx, sbgl_BufferUsage usage, size_t size, const void *data)
Creates a GPU buffer.
Definition sbgl_core.c:445
SblArena * sbgl_GetContextArena(sbgl_Context *ctx)
Retrieves the persistent arena associated with a context.
Definition sbgl_core.c:62
static sbgl_Vec4 sbgl_Vec4Set(float x, float y, float z, float w)
Creates a Vec4.
Definition sbgl_math.h:98
VoxelPool * VoxelPool_Init(SblArena *arena, uint32_t capacity)
Initializes a VoxelPool on the provided arena.
Definition sbgl_pool.c:10
@ SBGL_BUFFER_USAGE_INDIRECT
Definition sbgl_types.h:126
@ SBGL_BUFFER_USAGE_TRANSFER_DST
Definition sbgl_types.h:127
@ SBGL_BUFFER_USAGE_STORAGE
Definition sbgl_types.h:125
uint32_t sbgl_Shader
Handle for a shader module.
Definition sbgl_types.h:42
@ SBGL_SHADER_STAGE_COMPUTE
Definition sbgl_types.h:136
#define SBGL_INVALID_HANDLE
Definition sbgl_types.h:8
#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
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
Axis-Aligned Bounding Box (AABB).
Definition sbgl_camera.h:56
Standard Vulkan Indirect Draw command layout.
Definition sbgl_types.h:111
float roughness
Definition sbgl_voxel.h:42
float metalness
Definition sbgl_voxel.h:43
sbgl_Vec4 color
Definition sbgl_voxel.h:41
bool enable_telemetry
Enables console output for performance metrics and visible chunk counts.
Definition sbgl_voxel.h:37
uint32_t max_slots
Maximum number of chunk slots to manage in the GPU-side pool. This value determines the total memory ...
Definition sbgl_voxel.h:26
uint32_t chunk_radius
Radius (in chunks) around the camera that should be maintained. Chunks outside this radius are candid...
Definition sbgl_voxel.h:32
Internal state for the voxel system. This structure adheres to Data-Oriented Design principles by mai...
Definition sbgl_voxel.c:70
sbgl_Buffer materialPaletteBuf
GPU buffer storing the material palette attributes.
Definition sbgl_voxel.c:102
sbgl_AABB * cpuAABB
CPU-side mirror of AABB data to avoid mapping the previous frame's GPU buffer.
Definition sbgl_voxel.c:108
sbgl_ComputePipeline shellPipe
Compute pipeline for shell/boundary processing.
Definition sbgl_voxel.c:90
sbgl_Buffer shellCountsBuf
GPU storage for shell/active instance counts.
Definition sbgl_voxel.c:119
sbgl_Buffer maskBuf
GPU buffer storing the presence mask for all active chunks.
Definition sbgl_voxel.c:96
uint32_t chunk_radius
Radius (in chunks) for visibility and generation logic.
Definition sbgl_voxel.c:81
SblArena poolArena
Arena for the CPU-side management pool for chunk slots.
Definition sbgl_voxel.c:75
sbgl_ivec3 last_cam_chunk
World-space chunk coordinates of the camera in the previous frame.
Definition sbgl_voxel.c:122
sbgl_Context * ctx
Pointer to the active engine context.
Definition sbgl_voxel.c:72
double last_update_time
Time of the previous update for delta calculation.
Definition sbgl_voxel.c:140
sbgl_Buffer indirectCmdBuf[2]
Double-buffered GPU storage for indirect draw commands. Double buffering prevents CPU-GPU synchroniza...
Definition sbgl_voxel.c:114
sbgl_Buffer aabbBuf[2]
GPU buffer storing Axis-Aligned Bounding Boxes for culling. Double-buffered.
Definition sbgl_voxel.c:105
sbgl_Buffer instBuf
GPU buffer containing per-instance data for visible chunks.
Definition sbgl_voxel.c:99
sbgl_ComputePipeline genPipe
Compute pipeline for initial chunk generation.
Definition sbgl_voxel.c:87
sbgl_ComputePipeline cullPipe
Compute pipeline for frustum culling and indirect command generation.
Definition sbgl_voxel.c:93
VoxelPool * pool
Pointer to the CPU-side management pool for chunk slots.
Definition sbgl_voxel.c:78
bool enable_telemetry
Enables console output for performance metrics.
Definition sbgl_voxel.c:84
3D integer vector for chunk coordinates.
Definition sbgl_pool.h:12

◆ sbgl_Voxel_Cull()

void sbgl_Voxel_Cull ( sbgl_VoxelSystem * sys,
sbgl_Mat4 view_proj )

Performs GPU-driven frustum culling on voxel chunks. MUST be called BEFORE sbgl_BeginDrawing.

Parameters
sysPointer to the voxel system.
view_projThe view-projection matrix for the current frame.

Definition at line 436 of file sbgl_voxel.c.

436 {
437 /*
438 * Voxel culling follows a GPU-driven pipeline. A compute shader performs
439 * frustum culling on all managed chunks, populating an indirect draw buffer
440 * with commands for only the visible portions of the world.
441 * This is done outside the main render pass to avoid validation errors.
442 */
444
445 /* Ensure any prior host writes to mapped buffers are visible. */
447
448 /*
449 * Retrieve the double-buffered GPU handles for the current frame.
450 */
451 sbgl_Buffer currCmds = sys->indirectCmdBuf[sys->frame_idx];
452 sbgl_Buffer currCounts = sys->shellCountsBuf;
453
454 /*
455 * Bind the culling pipeline and upload the camera and frustum metadata via
456 * push constants.
457 */
459
461 cpc.viewProj = view_proj;
463 cpc.commandAddress = sbgl_GetBufferDeviceAddress(sys->ctx, currCmds);
464 cpc.countsAddress = sbgl_GetBufferDeviceAddress(sys->ctx, currCounts);
465 cpc.cameraPos = sbgl_Vec4Set(sys->camera_pos.x, sys->camera_pos.y, sys->camera_pos.z, 1.0f);
466 cpc.maxDistance = (float)(sys->chunk_radius + 1) * SBGL_VOXEL_CHUNK_SIZE * 1.5f;
467
468 sbgl_PushConstants(sys->ctx, sizeof(cpc), &cpc);
469
470 /*
471 * Dispatch the culling kernel. Each workgroup processes a subset of the
472 * total chunk slots available in the pool.
473 */
474 uint32_t groupCount = (sys->pool->capacity + 63) / 64;
475 sbgl_DispatchCompute(sys->ctx, groupCount, 1, 1);
476
477 /*
478 * Inject memory barriers to ensure that the indirect command buffer and
479 * graphics pipelines can safely consume the results of the compute culling.
480 */
483
484 sbgl_EndCompute(sys->ctx);
485}
void sbgl_PushConstants(sbgl_Context *ctx, size_t size, const void *data)
Updates push constants for the currently bound pipeline.
Definition sbgl_core.c:733
void sbgl_BindComputePipeline(sbgl_Context *ctx, sbgl_ComputePipeline pipeline)
Binds a compute pipeline for subsequent dispatch calls.
Definition sbgl_core.c:649
void sbgl_EndCompute(sbgl_Context *ctx)
Finalizes the compute phase.
Definition sbgl_core.c:385
void sbgl_BeginCompute(sbgl_Context *ctx)
Prepares the engine for compute operations before the main drawing pass.
Definition sbgl_core.c:344
void sbgl_MemoryBarrier(sbgl_Context *ctx, sbgl_BarrierType type)
Injects a memory barrier to synchronize compute and graphics operations.
Definition sbgl_core.c:665
uint64_t sbgl_GetBufferDeviceAddress(sbgl_Context *ctx, sbgl_Buffer buffer)
Retrieves the 64-bit GPU virtual address for a buffer.
Definition sbgl_core.c:689
void sbgl_DispatchCompute(sbgl_Context *ctx, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ)
Dispatches a compute workload.
Definition sbgl_core.c:657
@ SBGL_BARRIER_COMPUTE_TO_INDIRECT
Definition sbgl_types.h:144
@ SBGL_BARRIER_HOST_TO_COMPUTE
Definition sbgl_types.h:147
@ SBGL_BARRIER_COMPUTE_TO_GRAPHICS
Definition sbgl_types.h:145
uint32_t sbgl_Buffer
Handle for a GPU-side buffer.
Definition sbgl_types.h:37
#define SBGL_VOXEL_CHUNK_SIZE
World-space size of a single voxel chunk in meters.
Definition sbgl_voxel.c:19
uint32_t capacity
Definition sbgl_pool.h:24
Push constants for the frustum culling and indirect draw compute shader.
Definition sbgl_voxel.c:54
sbgl_Vec3 camera_pos
Current world-space position of the camera.
Definition sbgl_voxel.c:125
uint32_t frame_idx
Monotonically increasing index for double-buffer rotation.
Definition sbgl_voxel.c:128
float y
Definition sbgl_math.h:52
float z
Definition sbgl_math.h:52
float x
Definition sbgl_math.h:52

◆ sbgl_Voxel_Destroy()

void sbgl_Voxel_Destroy ( sbgl_VoxelSystem * sys)

Destroys the voxel system and releases all associated resources.

Parameters
sysPointer to the voxel system to destroy.

Definition at line 525 of file sbgl_voxel.c.

525 {
526 /*
527 * GPU resources are released back to the driver before the context
528 * handle itself is discarded.
529 */
530 if (!sys) return;
531
532 /*
533 * Ensure the GPU is no longer accessing the resources before destruction.
534 */
536
537 if (sys->genPipe) sbgl_DestroyComputePipeline(sys->ctx, sys->genPipe);
539 if (sys->cullPipe) sbgl_DestroyComputePipeline(sys->ctx, sys->cullPipe);
540
541 if (sys->maskBuf) sbgl_DestroyBuffer(sys->ctx, sys->maskBuf);
542 if (sys->instBuf) sbgl_DestroyBuffer(sys->ctx, sys->instBuf);
544 for (int i = 0; i < 2; ++i) {
545 if (sys->aabbBuf[i]) sbgl_DestroyBuffer(sys->ctx, sys->aabbBuf[i]);
546 }
547
549 for (int i = 0; i < 2; ++i) {
550 if (sys->indirectCmdBuf[i]) sbgl_DestroyBuffer(sys->ctx, sys->indirectCmdBuf[i]);
551 }
552
554 /* The sys struct itself is allocated from the context arena and is freed
555 when the context is shut down. */
556}
void sbgl_DestroyComputePipeline(sbgl_Context *ctx, sbgl_ComputePipeline pipeline)
Destroys a compute pipeline.
Definition sbgl_core.c:629
void sbgl_DestroyBuffer(sbgl_Context *ctx, sbgl_Buffer buffer)
Destroys a GPU buffer.
Definition sbgl_core.c:457
void sbgl_DeviceWaitIdle(sbgl_Context *ctx)
Synchronizes the CPU with the GPU, waiting for all commands to complete.
Definition sbgl_core.c:391

◆ sbgl_Voxel_GetAABBAddress()

uint64_t sbgl_Voxel_GetAABBAddress ( sbgl_VoxelSystem * sys)

Returns the device address of the AABB buffer.

Definition at line 558 of file sbgl_voxel.c.

558 {
559 if (!sys) return 0;
560 return sbgl_GetBufferDeviceAddress(sys->ctx, sys->aabbBuf[sys->frame_idx]);
561}

◆ sbgl_Voxel_GetInstanceAddress()

uint64_t sbgl_Voxel_GetInstanceAddress ( sbgl_VoxelSystem * sys)

Returns the device address of the instance buffer.

Definition at line 563 of file sbgl_voxel.c.

563 {
564 if (!sys) return 0;
565 return sbgl_GetBufferDeviceAddress(sys->ctx, sys->instBuf);
566}

◆ sbgl_Voxel_GetPaletteAddress()

uint64_t sbgl_Voxel_GetPaletteAddress ( sbgl_VoxelSystem * sys)

Returns the device address of the material palette buffer.

Definition at line 568 of file sbgl_voxel.c.

568 {
569 if (!sys) return 0;
571}

◆ sbgl_Voxel_Render()

void sbgl_Voxel_Render ( sbgl_VoxelSystem * sys)

Issues the indirect draw calls for visible voxel chunks. MUST be called BETWEEN sbgl_BeginDrawing and sbgl_EndDrawing.

Parameters
sysPointer to the voxel system.

Definition at line 487 of file sbgl_voxel.c.

487 {
488 /*
489 * Issue the multi-draw indirect call. The GPU will execute the array of
490 * commands generated by the culling phase in a single submission.
491 * This must be called inside the dynamic rendering pass.
492 */
493 sbgl_Buffer currCmds = sys->indirectCmdBuf[sys->frame_idx];
494 sbgl_DrawIndirect(sys->ctx, currCmds, 0, sys->pool->capacity);
495
496 /*
497 * Handle telemetry reporting if enabled.
498 */
499 if (sys->enable_telemetry && sys->telemetry_timer >= 1.0f) {
500 uint32_t visibleCount = 0;
501 uint32_t totalInstances = 0;
503 if (cmds) {
504 for (uint32_t i = 0; i < sys->pool->capacity; i++) {
505 if (cmds[i].instanceCount > 0) {
506 visibleCount++;
507 totalInstances += cmds[i].instanceCount;
508 }
509 }
510 sbgl_UnmapBuffer(sys->ctx, currCmds);
511 }
512
513 printf("FPS: %u | GPU: %.2fms | Visible: %u/%u | Instances: %u\n",
514 sys->fps_frames,
516 visibleCount,
517 sys->pool->capacity,
518 totalInstances);
519
520 sys->telemetry_timer = 0.0f;
521 sys->fps_frames = 0;
522 }
523}
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.
Definition sbgl_core.c:720
sbgl_Telemetry sbgl_GetTelemetry(sbgl_Context *ctx)
Retrieves the performance telemetry data for the previous frame.
Definition sbgl_core.c:436
float gpu_render_time
Definition sbgl_types.h:178
float telemetry_timer
Timer for periodic telemetry reporting.
Definition sbgl_voxel.c:134
uint32_t fps_frames
Frame counter for FPS calculation.
Definition sbgl_voxel.c:137

◆ sbgl_Voxel_Update()

void sbgl_Voxel_Update ( sbgl_VoxelSystem * sys,
sbgl_Vec3 camera_pos )

Updates the voxel system state based on the current camera position. This function handles chunk generation requests and manages the LRU cache.

Parameters
sysPointer to the voxel system.
camera_posThe current world-space position of the camera.

Definition at line 286 of file sbgl_voxel.c.

286 {
287 /*
288 * The update phase performs camera-relative chunk management.
289 * It identifies chunks that have entered the visibility radius and
290 * schedules them for generation in the compute pipelines.
291 * The frame index is synchronized with the engine backend to ensure
292 * correct double-buffering.
293 */
294 sys->frame_idx = sbgl_GetFrameIndex(sys->ctx);
295
296 double currentTime = sbgl_GetTime(sys->ctx);
297 float dt = (float)(currentTime - sys->last_update_time);
298 sys->last_update_time = currentTime;
299
300 sys->telemetry_timer += dt;
301 sys->fps_frames++;
302
303 sys->camera_pos = camera_pos;
304 int camCX = (int)floorf(camera_pos.x / SBGL_VOXEL_CHUNK_SIZE);
305 int camCY = (int)floorf(camera_pos.y / SBGL_VOXEL_CHUNK_SIZE);
306 int camCZ = (int)floorf(camera_pos.z / SBGL_VOXEL_CHUNK_SIZE);
307
308 int radius = (int)sys->chunk_radius;
309
310 /* Always refresh timestamps for all slots that are within the visibility radius.
311 This prevents the LRU pool from prematurely recycling chunks while the camera
312 is moving within a single chunk or stationary. */
313 for (uint32_t i = 0; i < sys->pool->capacity; i++) {
314 if (sys->pool->active[i]) {
315 sbgl_ivec3 p = sys->pool->positions[i];
316 if (abs(p.x - camCX) <= radius && abs(p.z - camCZ) <= radius && p.y >= (camCY - 1) && p.y <= (camCY + 2)) {
317 sys->pool->last_used_frames[i] = sys->pool->current_frame;
318 }
319 }
320 }
321
322 /* Update logic is triggered only when the camera crosses a chunk boundary
323 to minimize unnecessary CPU/GPU overhead. */
324 if (camCX != sys->last_cam_chunk.x || camCY != sys->last_cam_chunk.y || camCZ != sys->last_cam_chunk.z) {
326
327 uint64_t maskBaseAddr = sbgl_GetBufferDeviceAddress(sys->ctx, sys->maskBuf);
328 uint64_t instBaseAddr = sbgl_GetBufferDeviceAddress(sys->ctx, sys->instBuf);
329 uint64_t shellCountsBaseAddr = sbgl_GetBufferDeviceAddress(sys->ctx, sys->shellCountsBuf);
330
331 sbgl_AABB* aabbPtr = (sbgl_AABB*)sbgl_MapBuffer(sys->ctx, sys->aabbBuf[sys->frame_idx]);
332
333 /* Copy current persistent state from the CPU-side mirror to the new frame's GPU buffer. */
334 if (aabbPtr) {
335 memcpy(aabbPtr, sys->cpuAABB, sys->pool->capacity * sizeof(sbgl_AABB));
336 }
337
338 /* First pass: Acquire and generate new chunks in the radius. */
339 for (int dz = -radius; dz <= radius; dz++) {
340 for (int dx = -radius; dx <= radius; dx++) {
341 for (int dy = -1; dy <= 2; dy++) {
342 sbgl_ivec3 pos = { camCX + dx, camCY + dy, camCZ + dz };
343
344 bool is_new = false;
345 int32_t slot = VoxelPool_AcquireSlot(sys->pool, pos, &is_new);
346
347 if (is_new) {
348 uint64_t maskAddr = maskBaseAddr + (slot * 65536);
349 uint64_t instAddr = instBaseAddr + (slot * 65536 * 8);
350 uint64_t countAddr = shellCountsBaseAddr + (slot * 4);
351
352 /* Clear the presence mask and instance counts using GPU-side commands
353 to avoid expensive host-to-device synchronization. */
354 sbgl_FillBuffer(sys->ctx, sys->maskBuf, slot * 65536, 65536, 0);
355 sbgl_FillBuffer(sys->ctx, sys->shellCountsBuf, slot * 4, 4, 0);
356
357 /* Ensure the GPU-side clears are visible before the compute shaders start.
358 SBGL_BARRIER_COMPUTE_TO_COMPUTE now includes TRANSFER synchronization. */
360
363 .maskAddress = maskAddr,
364 .offset = sbgl_Vec4Set((float)pos.x * SBGL_VOXEL_CHUNK_SIZE, (float)pos.y * SBGL_VOXEL_CHUNK_SIZE, (float)pos.z * SBGL_VOXEL_CHUNK_SIZE, 0.0f),
365 .seed = 42.0f
366 };
367 sbgl_PushConstants(sys->ctx, sizeof(gpc), &gpc);
368 sbgl_DispatchCompute(sys->ctx, 4096, 1, 1);
369
371
374 .maskAddress = maskAddr,
375 .instanceAddress = instAddr,
376 .counterAddress = countAddr,
377 .offset = gpc.offset
378 };
379 sbgl_PushConstants(sys->ctx, sizeof(spc), &spc);
380 sbgl_DispatchCompute(sys->ctx, 1, 64, 1);
381
382 sbgl_AABB aabb;
383 aabb.min = sbgl_Vec4Set((float)pos.x * SBGL_VOXEL_CHUNK_SIZE, (float)pos.y * SBGL_VOXEL_CHUNK_SIZE, (float)pos.z * SBGL_VOXEL_CHUNK_SIZE, 1.0f);
385 if (aabbPtr) memcpy(aabbPtr + slot, &aabb, sizeof(sbgl_AABB));
386 sys->cpuAABB[slot] = aabb;
387 }
388 }
389 }
390 }
391
392 /* Second pass: Deactivate slots that have left the visibility radius.
393 This prevents orphaned chunks from flickering or appearing in the wrong location. */
394 for (uint32_t i = 0; i < sys->pool->capacity; i++) {
395 if (sys->pool->active[i]) {
396 sbgl_ivec3 p = sys->pool->positions[i];
397 if (abs(p.x - camCX) > radius || abs(p.z - camCZ) > radius || p.y < (camCY - 1) || p.y > (camCY + 2)) {
398 sys->pool->active[i] = 0;
399 /* Reset the instance count on the GPU to immediately silence the chunk. */
400 sbgl_FillBuffer(sys->ctx, sys->shellCountsBuf, i * 4, 4, 0);
401 }
402 }
403 }
404
405 if (aabbPtr) sbgl_UnmapBuffer(sys->ctx, sys->aabbBuf[sys->frame_idx]);
406
407 /* Ensure that the host-side updates to the AABB buffer are visible to the GPU
408 before the culling and graphics phases begin. */
411
412 /* Ensure that the GPU-side clears from the deactivation pass are visible
413 before the culling phase starts. */
415
416 sbgl_EndCompute(sys->ctx);
417
418 sys->last_cam_chunk = (sbgl_ivec3){ camCX, camCY, camCZ };
419 } else {
420 /* Even if the camera hasn't crossed a chunk boundary, we must still update the
421 AABB buffer for the current frame by copying from the CPU-side mirror.
422 This ensures that the culling shader always has a valid set of AABBs. */
423 sbgl_AABB* aabbPtr = (sbgl_AABB*)sbgl_MapBuffer(sys->ctx, sys->aabbBuf[sys->frame_idx]);
424 if (aabbPtr) {
425 memcpy(aabbPtr, sys->cpuAABB, sys->pool->capacity * sizeof(sbgl_AABB));
426 sbgl_UnmapBuffer(sys->ctx, sys->aabbBuf[sys->frame_idx]);
427 }
430 }
431
433}
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.
Definition sbgl_core.c:480
uint32_t sbgl_GetFrameIndex(sbgl_Context *ctx)
Retrieves the current frame index for double/triple buffering.
Definition sbgl_core.c:489
void VoxelPool_UpdateFrame(VoxelPool *pool, uint64_t frame)
Advances the pool's internal frame counter for LRU tracking.
Definition sbgl_pool.c:78
int32_t VoxelPool_AcquireSlot(VoxelPool *pool, sbgl_ivec3 pos, bool *is_new)
Acquires a slot for a given chunk position. Returns the slot index (0 to capacity-1)....
Definition sbgl_pool.c:25
@ SBGL_BARRIER_COMPUTE_TO_COMPUTE
Definition sbgl_types.h:143
@ SBGL_BARRIER_HOST_TO_GRAPHICS
Definition sbgl_types.h:148
uint64_t * last_used_frames
Definition sbgl_pool.h:22
sbgl_ivec3 * positions
Definition sbgl_pool.h:21
uint64_t current_frame
Definition sbgl_pool.h:25
uint8_t * active
Definition sbgl_pool.h:23
sbgl_Vec3 max
Definition sbgl_camera.h:58
sbgl_Vec3 min
Definition sbgl_camera.h:57
Push constants for the voxel generation compute shader.
Definition sbgl_voxel.c:32
Push constants for the shell extraction compute shader.
Definition sbgl_voxel.c:43
uint64_t total_frames
Total number of frames processed for LRU tracking.
Definition sbgl_voxel.c:131
int32_t x
Definition sbgl_pool.h:13
int32_t y
Definition sbgl_pool.h:13
int32_t z
Definition sbgl_pool.h:13