@@ -93,6 +93,13 @@ typedef struct {
9393 * @brief The @c Scene.
9494 */
9595 Scene scene ;
96+
97+ /**
98+ * @brief Occlusion query proof-of-concept: pool, readback buffer, and frame counter.
99+ */
100+ QueryPool * occlusionQueryPool ;
101+ SDL_GPUTransferBuffer * occlusionQueryTransfer ;
102+ Uint32 frames ;
96103} AppState ;
97104
98105static AppState application ;
@@ -213,10 +220,39 @@ static void drawScene(AppState *app, CommandBuffer *commands) {
213220 RenderPass * pass = $ (commands , beginRenderPass , & color , 1 , & depth );
214221 $ (pass , bindPipeline , scene -> pipeline );
215222 $ (pass , bindVertexBuffers , 0 , & (SDL_GPUBufferBinding ) { .buffer = scene -> vertexBuffer -> buffer }, 1 );
223+ $ (pass , beginQuery , app -> occlusionQueryPool , 0 );
216224 $ (pass , drawPrimitives , (Uint32 ) SDL_arraysize (vertices ), 1 , 0 , 0 );
225+ $ (pass , endQuery , app -> occlusionQueryPool , 0 );
217226 release (pass );
218227}
219228
229+ /**
230+ * @brief Occlusion query proof-of-concept: downloads and logs this frame's result.
231+ * @details A real application would defer the readback to a later frame instead
232+ * of blocking on the GPU like this. When occlusion queries are unsupported by
233+ * the linked SDL3, this always reports a "not occluded" sentinel value.
234+ */
235+ static void logOcclusionQueryResult (AppState * app ) {
236+
237+ CommandBuffer * commands = $ (app -> renderDevice , acquireCommandBuffer );
238+
239+ CopyPass * copyPass = $ (commands , beginCopyPass );
240+ $ (copyPass , downloadQueryResults , app -> occlusionQueryPool , 0 , 1 , & (SDL_GPUTransferBufferLocation ) {
241+ .transfer_buffer = app -> occlusionQueryTransfer ,
242+ });
243+ release (copyPass );
244+
245+ SDL_GPUFence * fence = $ (commands , submitAndFence );
246+ release (commands );
247+
248+ $ (app -> renderDevice , waitForFences , true, & fence , 1 );
249+ $ (app -> renderDevice , releaseFence , fence );
250+
251+ const Uint64 * result = $ (app -> renderDevice , mapTransferBuffer , app -> occlusionQueryTransfer , false);
252+ SDL_Log ("Occlusion query result: %" SDL_PRIu64 " samples passed" , * result );
253+ $ (app -> renderDevice , unmapTransferBuffer , app -> occlusionQueryTransfer );
254+ }
255+
220256#pragma mark - SDL application callbacks
221257
222258/**
@@ -254,6 +290,16 @@ SDL_AppResult SDL_AppInit(void **appState, int argc, char *argv[]) {
254290
255291 initScene (app );
256292
293+ app -> occlusionQueryPool = $ (app -> renderDevice , createQueryPool , & (SDL_GPUQueryPoolCreateInfo ) {
294+ .type = SDL_GPU_QUERY_PRECISE_OCCLUSION ,
295+ .query_count = 1 ,
296+ });
297+
298+ app -> occlusionQueryTransfer = $ (app -> renderDevice , createTransferBuffer , & (SDL_GPUTransferBufferCreateInfo ) {
299+ .usage = SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD ,
300+ .size = sizeof (Uint64 ),
301+ });
302+
257303 return SDL_APP_CONTINUE ;
258304}
259305
@@ -268,6 +314,10 @@ SDL_AppResult SDL_AppIterate(void *appState) {
268314 if (commands ) {
269315 drawScene (app , commands );
270316 $ (app -> renderDevice , endFrame );
317+
318+ if ((app -> frames ++ % 60 ) == 0 ) {
319+ logOcclusionQueryResult (app );
320+ }
271321 }
272322
273323 return SDL_APP_CONTINUE ;
@@ -298,6 +348,9 @@ void SDL_AppQuit(void *appState, SDL_AppResult result) {
298348 release (app -> scene .pipeline );
299349 release (app -> scene .vertexBuffer );
300350
351+ release (app -> occlusionQueryPool );
352+ $ (app -> renderDevice , releaseTransferBuffer , app -> occlusionQueryTransfer );
353+
301354 release (app -> framebuffer );
302355 release (app -> renderDevice );
303356
0 commit comments