Skip to content

Commit 2c378fd

Browse files
committed
Introduce Fence and TransfterBuffer.
1 parent acdbdc9 commit 2c378fd

15 files changed

Lines changed: 610 additions & 160 deletions

File tree

Examples/Hello.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ typedef struct {
9898
* @brief Occlusion query proof-of-concept: pool, readback buffer, and frame counter.
9999
*/
100100
QueryPool *occlusionQueryPool;
101-
SDL_GPUTransferBuffer *occlusionQueryTransfer;
101+
TransferBuffer *occlusionQueryTransfer;
102102
Uint32 frames;
103103
} AppState;
104104

@@ -238,19 +238,19 @@ static void logOcclusionQueryResult(AppState *app) {
238238

239239
CopyPass *copyPass = $(commands, beginCopyPass);
240240
$(copyPass, downloadQueryResults, app->occlusionQueryPool, 0, 1, &(SDL_GPUTransferBufferLocation) {
241-
.transfer_buffer = app->occlusionQueryTransfer,
241+
.transfer_buffer = app->occlusionQueryTransfer->buffer,
242242
});
243243
release(copyPass);
244244

245-
SDL_GPUFence *fence = $(commands, submitAndFence);
245+
Fence *fence = $(commands, submitAndFence);
246246
release(commands);
247247

248-
$(app->renderDevice, waitForFences, true, &fence, 1);
249-
$(app->renderDevice, releaseFence, fence);
248+
$(fence, wait);
249+
release(fence);
250250

251-
const Uint64 *result = $(app->renderDevice, mapTransferBuffer, app->occlusionQueryTransfer, false);
251+
const Uint64 *result = $(app->occlusionQueryTransfer, map, false);
252252
SDL_Log("Occlusion query result: %" SDL_PRIu64 " samples passed", *result);
253-
$(app->renderDevice, unmapTransferBuffer, app->occlusionQueryTransfer);
253+
$(app->occlusionQueryTransfer, unmap);
254254
}
255255

256256
#pragma mark - SDL application callbacks
@@ -349,7 +349,7 @@ void SDL_AppQuit(void *appState, SDL_AppResult result) {
349349
release(app->scene.vertexBuffer);
350350

351351
release(app->occlusionQueryPool);
352-
$(app->renderDevice, releaseTransferBuffer, app->occlusionQueryTransfer);
352+
release(app->occlusionQueryTransfer);
353353

354354
release(app->framebuffer);
355355
release(app->renderDevice);

ObjectivelyGPU.vs15/ObjectivelyGPU.vcxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<ClInclude Include="..\Sources\ObjectivelyGPU\ComputePass.h" />
2626
<ClInclude Include="..\Sources\ObjectivelyGPU\ComputePipeline.h" />
2727
<ClInclude Include="..\Sources\ObjectivelyGPU\CopyPass.h" />
28+
<ClInclude Include="..\Sources\ObjectivelyGPU\Fence.h" />
2829
<ClInclude Include="..\Sources\ObjectivelyGPU\Framebuffer.h" />
2930
<ClInclude Include="..\Sources\ObjectivelyGPU\GraphicsPipeline.h" />
3031
<ClInclude Include="..\Sources\ObjectivelyGPU\Mathlib.h" />
@@ -34,6 +35,7 @@
3435
<ClInclude Include="..\Sources\ObjectivelyGPU\Sampler.h" />
3536
<ClInclude Include="..\Sources\ObjectivelyGPU\Shader.h" />
3637
<ClInclude Include="..\Sources\ObjectivelyGPU\Texture.h" />
38+
<ClInclude Include="..\Sources\ObjectivelyGPU\TransferBuffer.h" />
3739
<ClInclude Include="..\Sources\ObjectivelyGPU\Types.h" />
3840
<ClInclude Include="Sources\WindowlyGPU.h" />
3941
</ItemGroup>
@@ -43,6 +45,7 @@
4345
<ClCompile Include="..\Sources\ObjectivelyGPU\ComputePass.c" />
4446
<ClCompile Include="..\Sources\ObjectivelyGPU\ComputePipeline.c" />
4547
<ClCompile Include="..\Sources\ObjectivelyGPU\CopyPass.c" />
48+
<ClCompile Include="..\Sources\ObjectivelyGPU\Fence.c" />
4649
<ClCompile Include="..\Sources\ObjectivelyGPU\Framebuffer.c" />
4750
<ClCompile Include="..\Sources\ObjectivelyGPU\GraphicsPipeline.c" />
4851
<ClCompile Include="..\Sources\ObjectivelyGPU\QueryPool.c" />
@@ -51,6 +54,7 @@
5154
<ClCompile Include="..\Sources\ObjectivelyGPU\Sampler.c" />
5255
<ClCompile Include="..\Sources\ObjectivelyGPU\Shader.c" />
5356
<ClCompile Include="..\Sources\ObjectivelyGPU\Texture.c" />
57+
<ClCompile Include="..\Sources\ObjectivelyGPU\TransferBuffer.c" />
5458
</ItemGroup>
5559
<ItemGroup>
5660
<ProjectReference Include="..\..\Objectively\Objectively.vs15\Objectively.vcxproj">

ObjectivelyGPU.xcodeproj/project.pbxproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@
5757
CECF83982FEF6A7200CB0CCA /* Mathlib.h in Headers */ = {isa = PBXBuildFile; fileRef = CECF83972FEF6A7200CB0CCA /* Mathlib.h */; settings = {ATTRIBUTES = (Public, ); }; };
5858
496C55EEAC4C86E3B7C6550E /* QueryPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 989E64805790578A941CB5B9 /* QueryPool.h */; settings = {ATTRIBUTES = (Public, ); }; };
5959
45AD38AB8109398E51564CFD /* QueryPool.c in Sources */ = {isa = PBXBuildFile; fileRef = 872CC6C6CBA08F1A36626F57 /* QueryPool.c */; };
60+
3402D3208D6A4CF886402DD3 /* Fence.h in Headers */ = {isa = PBXBuildFile; fileRef = B89C52ADCD12486D934D4F78 /* Fence.h */; settings = {ATTRIBUTES = (Public, ); }; };
61+
E7138396FE1A4AF0B311EBD6 /* Fence.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F7C26CE48FB43D393B963EA /* Fence.c */; };
62+
8D678937240347D29F529D34 /* TransferBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = F20F19F296684FA89413FFC0 /* TransferBuffer.h */; settings = {ATTRIBUTES = (Public, ); }; };
63+
17034614E26D4BBCA2806D4B /* TransferBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = DCDE588CEB8C4ED0A63C96C7 /* TransferBuffer.c */; };
6064
CECF83B62FEF765400CB0CCA /* Hello.frag.metal in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECF83A52FEF712F00CB0CCA /* Hello.frag.metal */; };
6165
CECF83B72FEF765400CB0CCA /* Hello.frag.spv in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECF83A62FEF712F00CB0CCA /* Hello.frag.spv */; };
6266
CECF83B92FEF765400CB0CCA /* Hello.vert.metal in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECF83A82FEF712F00CB0CCA /* Hello.vert.metal */; };
@@ -186,6 +190,10 @@
186190
CECF83972FEF6A7200CB0CCA /* Mathlib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Mathlib.h; sourceTree = "<group>"; };
187191
989E64805790578A941CB5B9 /* QueryPool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QueryPool.h; sourceTree = "<group>"; };
188192
872CC6C6CBA08F1A36626F57 /* QueryPool.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = QueryPool.c; sourceTree = "<group>"; };
193+
B89C52ADCD12486D934D4F78 /* Fence.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Fence.h; sourceTree = "<group>"; };
194+
0F7C26CE48FB43D393B963EA /* Fence.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = Fence.c; sourceTree = "<group>"; };
195+
F20F19F296684FA89413FFC0 /* TransferBuffer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TransferBuffer.h; sourceTree = "<group>"; };
196+
DCDE588CEB8C4ED0A63C96C7 /* TransferBuffer.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = TransferBuffer.c; sourceTree = "<group>"; };
189197
CECF83A32FEF6F5B00CB0CCA /* Makefile.am */ = {isa = PBXFileReference; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
190198
CECF83A42FEF712F00CB0CCA /* Hello.frag.glsl */ = {isa = PBXFileReference; lastKnownFileType = text; path = Hello.frag.glsl; sourceTree = "<group>"; };
191199
CECF83A52FEF712F00CB0CCA /* Hello.frag.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Hello.frag.metal; sourceTree = "<group>"; };
@@ -305,6 +313,8 @@
305313
338EC7DCAE74C1B8C397D189 /* Config.h */,
306314
59334BDAED542AB6DE565EDD /* CopyPass.h */,
307315
C862954A9DD435C1FF7CD31D /* CopyPass.c */,
316+
B89C52ADCD12486D934D4F78 /* Fence.h */,
317+
0F7C26CE48FB43D393B963EA /* Fence.c */,
308318
CECF83C52FF03A3500CB0CCA /* Framebuffer.h */,
309319
CECF83C62FF03A3500CB0CCA /* Framebuffer.c */,
310320
CEBA3F652FF3211F00758CA6 /* GraphicsPipeline.h */,
@@ -322,6 +332,8 @@
322332
CEBA3F6A2FF3211F00758CA6 /* Shader.c */,
323333
CEBA3F5E2FF30F1E00758CA6 /* Texture.h */,
324334
CEBA3F5F2FF30F1E00758CA6 /* Texture.c */,
335+
F20F19F296684FA89413FFC0 /* TransferBuffer.h */,
336+
DCDE588CEB8C4ED0A63C96C7 /* TransferBuffer.c */,
325337
38F069D0DBE26C7E00B5B5EE /* Types.h */,
326338
CECF83942FEF65EB00CB0CCA /* Makefile.am */,
327339
);
@@ -400,6 +412,7 @@
400412
CEBA3F6D2FF3211F00758CA6 /* ComputePipeline.h in Headers */,
401413
6F351DFD232DA78A42D8B494 /* Config.h in Headers */,
402414
DF73824CC25298378B451056 /* CopyPass.h in Headers */,
415+
3402D3208D6A4CF886402DD3 /* Fence.h in Headers */,
403416
CECF83C72FF03A3500CB0CCA /* Framebuffer.h in Headers */,
404417
CEBA3F6B2FF3211F00758CA6 /* GraphicsPipeline.h in Headers */,
405418
CECF83982FEF6A7200CB0CCA /* Mathlib.h in Headers */,
@@ -410,6 +423,7 @@
410423
CEBA3F6C2FF3211F00758CA6 /* Sampler.h in Headers */,
411424
CEBA3F6E2FF3211F00758CA6 /* Shader.h in Headers */,
412425
CEBA3F602FF30F1E00758CA6 /* Texture.h in Headers */,
426+
8D678937240347D29F529D34 /* TransferBuffer.h in Headers */,
413427
90C29A39257729BE8D4FDDF8 /* Types.h in Headers */,
414428
);
415429
runOnlyForDeploymentPostprocessing = 0;
@@ -584,6 +598,7 @@
584598
F73340493879CD8034F99D51 /* ComputePass.c in Sources */,
585599
CEBA3F752FF330A700758CA6 /* ComputePipeline.c in Sources */,
586600
CCCB11DB70E4A962360A2119 /* CopyPass.c in Sources */,
601+
E7138396FE1A4AF0B311EBD6 /* Fence.c in Sources */,
587602
CECF83C82FF03A3500CB0CCA /* Framebuffer.c in Sources */,
588603
CEBA3F702FF3211F00758CA6 /* GraphicsPipeline.c in Sources */,
589604
45AD38AB8109398E51564CFD /* QueryPool.c in Sources */,
@@ -592,6 +607,7 @@
592607
86E5132F5DCCB6B674151343 /* RenderDevice.c in Sources */,
593608
FF30FF2D56F9E4EA3C492CBB /* RenderPass.c in Sources */,
594609
CEBA3F612FF30F1E00758CA6 /* Texture.c in Sources */,
610+
17034614E26D4BBCA2806D4B /* TransferBuffer.c in Sources */,
595611
);
596612
runOnlyForDeploymentPostprocessing = 0;
597613
};

Sources/ObjectivelyGPU.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <ObjectivelyGPU/ComputePass.h>
2929
#include <ObjectivelyGPU/ComputePipeline.h>
3030
#include <ObjectivelyGPU/CopyPass.h>
31+
#include <ObjectivelyGPU/Fence.h>
3132
#include <ObjectivelyGPU/Framebuffer.h>
3233
#include <ObjectivelyGPU/GraphicsPipeline.h>
3334
#include <ObjectivelyGPU/Mathlib.h>
@@ -37,3 +38,4 @@
3738
#include <ObjectivelyGPU/Sampler.h>
3839
#include <ObjectivelyGPU/Shader.h>
3940
#include <ObjectivelyGPU/Texture.h>
41+
#include <ObjectivelyGPU/TransferBuffer.h>

Sources/ObjectivelyGPU/CommandBuffer.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "CommandBuffer.h"
2727
#include "ComputePass.h"
2828
#include "CopyPass.h"
29+
#include "Fence.h"
2930
#include "RenderDevice.h"
3031
#include "RenderPass.h"
3132

@@ -251,10 +252,10 @@ static bool submit(CommandBuffer *self) {
251252
}
252253

253254
/**
254-
* @fn SDL_GPUFence *CommandBuffer::submitAndFence(CommandBuffer *self)
255+
* @fn Fence *CommandBuffer::submitAndFence(CommandBuffer *self)
255256
* @memberof CommandBuffer
256257
*/
257-
static SDL_GPUFence *submitAndFence(CommandBuffer *self) {
258+
static Fence *submitAndFence(CommandBuffer *self) {
258259

259260
GPU_Assert(!self->submitted, "command buffer already submitted");
260261
GPU_Assert(!self->pass, "cannot submit with an open pass");
@@ -264,7 +265,7 @@ static SDL_GPUFence *submitAndFence(CommandBuffer *self) {
264265

265266
self->submitted = true;
266267

267-
return fence;
268+
return $(alloc(Fence), initWithDevice, self->device, fence);
268269
}
269270

270271
/**

Sources/ObjectivelyGPU/CommandBuffer.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
typedef struct ComputePass ComputePass;
3636
typedef struct CopyPass CopyPass;
37+
typedef struct Fence Fence;
3738
typedef struct RenderPass RenderPass;
3839
typedef struct RenderDevice RenderDevice;
3940
typedef struct CommandBuffer CommandBuffer;
@@ -288,15 +289,14 @@ struct CommandBufferInterface {
288289
bool (*submit)(CommandBuffer *self);
289290

290291
/**
291-
* @fn SDL_GPUFence *CommandBuffer::submitAndFence(CommandBuffer *self)
292-
* @brief Submits this command buffer and returns a fence for GPU completion.
293-
* @details The fence must be released via `RenderDevice::releaseFence` when
294-
* no longer needed.
292+
* @fn Fence *CommandBuffer::submitAndFence(CommandBuffer *self)
293+
* @brief Submits this command buffer and returns a Fence for GPU completion.
294+
* @details The returned Fence is retained; release it when no longer needed.
295295
* @param self The CommandBuffer.
296-
* @return A new SDL_GPUFence, or NULL on error.
296+
* @return A new, retained Fence, or `NULL` on error.
297297
* @memberof CommandBuffer
298298
*/
299-
SDL_GPUFence *(*submitAndFence)(CommandBuffer *self);
299+
Fence *(*submitAndFence)(CommandBuffer *self);
300300

301301
/**
302302
* @fn bool CommandBuffer::waitAndAcquireSwapchainTexture(const CommandBuffer *self, SwapchainTexture *swapchain)

Sources/ObjectivelyGPU/CopyPass.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,13 @@ static void downloadQueryResults(const CopyPass *self, QueryPool *pool, Uint32 f
103103
// write a "not occluded" sentinel for every requested result directly, rather
104104
// than leaving the destination uninitialized (which callers would likely
105105
// misread as "occluded", incorrectly culling everything).
106-
Uint64 *results = $(self->commands->device, mapTransferBuffer, dst->transfer_buffer, false);
106+
Uint64 *results = SDL_MapGPUTransferBuffer(self->commands->device->device, dst->transfer_buffer, false);
107+
GPU_Assert(results, "SDL_MapGPUTransferBuffer");
108+
107109
for (Uint32 i = 0; i < count; i++) {
108110
results[(dst->offset / sizeof(Uint64)) + i] = 1;
109111
}
110-
$(self->commands->device, unmapTransferBuffer, dst->transfer_buffer);
112+
SDL_UnmapGPUTransferBuffer(self->commands->device->device, dst->transfer_buffer);
111113
}
112114

113115
/**

Sources/ObjectivelyGPU/Fence.c

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* ObjectivelyGPU: Object oriented graphics framework for SDL3 and C.
3+
* Copyright (C) 2026 Jay Dolan <jay@jaydolan.com>
4+
*
5+
* This software is provided 'as-is', without any express or implied
6+
* warranty. In no event will the authors be held liable for any damages
7+
* arising from the use of this software.
8+
*
9+
* Permission is granted to anyone to use this software for any purpose,
10+
* including commercial applications, and to alter it and redistribute it
11+
* freely, subject to the following restrictions:
12+
*
13+
* 1. The origin of this software must not be misrepresented; you must not
14+
* claim that you wrote the original software. If you use this software
15+
* in a product, an acknowledgment in the product documentation would be
16+
* appreciated but is not required.
17+
*
18+
* 2. Altered source versions must be plainly marked as such, and must not be
19+
* misrepresented as being the original software.
20+
*
21+
* 3. This notice may not be removed or altered from any source distribution.
22+
*/
23+
24+
#include <assert.h>
25+
26+
#include "Fence.h"
27+
#include "RenderDevice.h"
28+
29+
#define _Class _Fence
30+
31+
#pragma mark - Object
32+
33+
/**
34+
* @see Object::dealloc(Object *)
35+
*/
36+
static void dealloc(Object *self) {
37+
38+
Fence *this = (Fence *) self;
39+
40+
if (this->device) {
41+
if (this->fence) {
42+
SDL_ReleaseGPUFence(this->device->device, this->fence);
43+
}
44+
release(this->device);
45+
}
46+
47+
super(Object, self, dealloc);
48+
}
49+
50+
#pragma mark - Fence
51+
52+
/**
53+
* @fn Fence *Fence::initWithDevice(Fence *self, RenderDevice *device, SDL_GPUFence *fence)
54+
* @memberof Fence
55+
*/
56+
static Fence *initWithDevice(Fence *self, RenderDevice *device, SDL_GPUFence *fence) {
57+
58+
assert(device);
59+
assert(fence);
60+
61+
self = (Fence *) super(Object, self, init);
62+
if (self) {
63+
self->device = retain(device);
64+
self->fence = fence;
65+
}
66+
67+
return self;
68+
}
69+
70+
/**
71+
* @fn bool Fence::query(const Fence *self)
72+
* @memberof Fence
73+
*/
74+
static bool query(const Fence *self) {
75+
return SDL_QueryGPUFence(self->device->device, self->fence);
76+
}
77+
78+
/**
79+
* @fn bool Fence::wait(const Fence *self)
80+
* @memberof Fence
81+
*/
82+
static bool _wait(const Fence *self) {
83+
return SDL_WaitForGPUFences(self->device->device, true, &self->fence, 1);
84+
}
85+
86+
#pragma mark - Class lifecycle
87+
88+
/**
89+
* @see Class::initialize(Class *)
90+
*/
91+
static void initialize(Class *clazz) {
92+
93+
((ObjectInterface *) clazz->interface)->dealloc = dealloc;
94+
95+
((FenceInterface *) clazz->interface)->initWithDevice = initWithDevice;
96+
((FenceInterface *) clazz->interface)->query = query;
97+
((FenceInterface *) clazz->interface)->wait = _wait;
98+
}
99+
100+
/**
101+
* @fn Class *Fence::_Fence(void)
102+
* @memberof Fence
103+
*/
104+
Class *_Fence(void) {
105+
static Class *clazz;
106+
static Once once;
107+
108+
do_once(&once, {
109+
clazz = _initialize(&(const ClassDef) {
110+
.name = "Fence",
111+
.superclass = _Object(),
112+
.instanceSize = sizeof(Fence),
113+
.interfaceOffset = offsetof(Fence, interface),
114+
.interfaceSize = sizeof(FenceInterface),
115+
.initialize = initialize,
116+
});
117+
});
118+
119+
return clazz;
120+
}
121+
122+
#undef _Class

0 commit comments

Comments
 (0)