Skip to content

Commit 8e26427

Browse files
jdolanclaude
andcommitted
Framebuffer: sampleable depth + resolveColorTexture/resolveDepthTexture
Add a resolveDepthTexture() accessor mirroring resolveColorTexture() (renamed from resolvedColorTexture): both return the single-sample, sampleable texture to read back (the attachment itself when single-sample, the resolve target under MSAA). Single-sample depth attachments now carry SAMPLER so scene depth can be read directly (e.g. soft particles); multisampled depth stays a plain depth-stencil target (its separate resolveDepthTexture, populated by a resolve pass, is reserved for when MSAA lands, since SDL has no depth store-op resolve). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent b076f45 commit 8e26427

4 files changed

Lines changed: 51 additions & 16 deletions

File tree

Sources/ObjectivelyGPU/Framebuffer.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ static void dealloc(Object *self) {
4343
release(this->resolveTextures[i]);
4444
}
4545
release(this->depthTexture);
46+
release(this->resolveDepthTexture);
4647

4748
release(this->device);
4849

@@ -146,16 +147,15 @@ static bool resize(Framebuffer *self, const SDL_Size *size) {
146147
}
147148
release(self->depthTexture);
148149
self->depthTexture = NULL;
150+
release(self->resolveDepthTexture);
151+
self->resolveDepthTexture = NULL;
149152

150153
self->size = *size;
151154

152155
const bool multisampled = self->sampleCount > SDL_GPU_SAMPLECOUNT_1;
153156

154157
for (Uint32 i = 0; i < self->numColorTargets; i++) {
155158

156-
// A multisampled color target is resolved before sampling, so it needs only
157-
// COLOR_TARGET usage; the single-sample paths (no MSAA, or the resolve target)
158-
// also carry SAMPLER so they can be blit/sampled.
159159
self->colorTextures[i] = $(self->device, createTexture, &(SDL_GPUTextureCreateInfo) {
160160
.type = SDL_GPU_TEXTURETYPE_2D,
161161
.format = self->colorFormats[i],
@@ -182,10 +182,15 @@ static bool resize(Framebuffer *self, const SDL_Size *size) {
182182
}
183183

184184
if (self->depthFormat != SDL_GPU_TEXTUREFORMAT_INVALID) {
185+
186+
// Single-sample depth carries SAMPLER so it can be read directly via
187+
// resolveDepthTexture (e.g. soft particles). Multisampled depth is a plain
188+
// depth-stencil target; sampling it requires the separate resolveDepthTexture,
189+
// populated by a resolve pass (SDL has no depth store-op resolve) — TODO when MSAA lands.
185190
self->depthTexture = $(self->device, createTexture, &(SDL_GPUTextureCreateInfo) {
186191
.type = SDL_GPU_TEXTURETYPE_2D,
187192
.format = self->depthFormat,
188-
.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET,
193+
.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET | (multisampled ? 0 : SDL_GPU_TEXTUREUSAGE_SAMPLER),
189194
.width = (Uint32) self->size.w,
190195
.height = (Uint32) self->size.h,
191196
.layer_count_or_depth = 1,
@@ -198,14 +203,22 @@ static bool resize(Framebuffer *self, const SDL_Size *size) {
198203
}
199204

200205
/**
201-
* @fn Texture *Framebuffer::resolvedColorTexture(const Framebuffer *self, Uint32 index)
206+
* @fn Texture *Framebuffer::resolveColorTexture(const Framebuffer *self, Uint32 index)
202207
* @memberof Framebuffer
203208
*/
204-
static Texture *resolvedColorTexture(const Framebuffer *self, Uint32 index) {
209+
static Texture *resolveColorTexture(const Framebuffer *self, Uint32 index) {
205210
assert(index < self->numColorTargets);
206211
return self->resolveTextures[index] ?: self->colorTextures[index];
207212
}
208213

214+
/**
215+
* @fn Texture *Framebuffer::resolveDepthTexture(const Framebuffer *self)
216+
* @memberof Framebuffer
217+
*/
218+
static Texture *resolveDepthTexture(const Framebuffer *self) {
219+
return self->resolveDepthTexture ?: self->depthTexture;
220+
}
221+
209222
#pragma mark - Class lifecycle
210223

211224
/**
@@ -219,7 +232,8 @@ static void initialize(Class *clazz) {
219232
((FramebufferInterface *) clazz->interface)->depthTargetInfo = depthTargetInfo;
220233
((FramebufferInterface *) clazz->interface)->initWithDevice = initWithDevice;
221234
((FramebufferInterface *) clazz->interface)->resize = resize;
222-
((FramebufferInterface *) clazz->interface)->resolvedColorTexture = resolvedColorTexture;
235+
((FramebufferInterface *) clazz->interface)->resolveColorTexture = resolveColorTexture;
236+
((FramebufferInterface *) clazz->interface)->resolveDepthTexture = resolveDepthTexture;
223237
}
224238

225239
/**

Sources/ObjectivelyGPU/Framebuffer.h

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ struct Framebuffer {
171171
/**
172172
* @brief The single-sample resolve targets, or all `NULL` unless `sampleCount` > `SDL_GPU_SAMPLECOUNT_1`.
173173
* @details Render passes resolve `colorTextures[i]` into `resolveTextures[i]`; that is
174-
* the texture to sample, blit, or present. See `resolvedColorTexture`.
174+
* the texture to sample, blit, or present. See `resolveColorTexture`.
175175
*/
176176
Texture *resolveTextures[GPU_MAX_COLOR_TARGETS];
177177

@@ -182,13 +182,22 @@ struct Framebuffer {
182182

183183
/**
184184
* @brief The depth attachment texture, or `NULL` if `depthFormat` is invalid.
185-
* @details Multisampled with the pass when `sampleCount` > `SDL_GPU_SAMPLECOUNT_1`. It is
186-
* not resolved: depth is used for testing and discarded. A consumer needing sampleable
187-
* resolved depth must resolve it in its own shader pass (SDL has no depth store-op resolve).
185+
* @details Single-sample depth also carries `SAMPLER` and is returned directly by
186+
* `resolveDepthTexture`. Multisampled depth is a plain depth-stencil target
187+
* (`resolveDepthTexture` returns the separate `resolveDepthTexture` instead, since
188+
* SDL has no depth store-op resolve). Sample via `resolveDepthTexture`, not this.
188189
* @private
189190
*/
190191
Texture *depthTexture;
191192

193+
/**
194+
* @brief The single-sample, sampleable depth resolve target, or `NULL`.
195+
* @details Only allocated when multisampled (single-sample reads the depth attachment
196+
* directly). Populated by an explicit resolve pass; returned by `resolveDepthTexture`.
197+
* @private
198+
*/
199+
Texture *resolveDepthTexture;
200+
192201
/**
193202
* @brief The owning RenderDevice, used for texture allocation and dealloc.
194203
* @private
@@ -255,15 +264,27 @@ struct FramebufferInterface {
255264
Framebuffer *(*initWithDevice)(Framebuffer *self, RenderDevice *device, const GPU_FramebufferCreateInfo *info);
256265

257266
/**
258-
* @fn Texture *Framebuffer::resolvedColorTexture(const Framebuffer *self, Uint32 index)
259-
* @brief Returns the single-sample color texture for attachment @p index, to sample, blit, or present.
267+
* @fn Texture *Framebuffer::resolveColorTexture(const Framebuffer *self, Uint32 index)
268+
* @brief Returns the single-sample, sampleable color texture for attachment @p index, to sample, blit, or present.
260269
* @details Returns `resolveTextures[index]` when multisampled, otherwise `colorTextures[index]`.
261270
* @param self The Framebuffer.
262271
* @param index The color attachment index, in `[0, numColorTargets)`.
263272
* @return The resolved color texture, or `NULL` if @p index has no attachment.
264273
* @memberof Framebuffer
265274
*/
266-
Texture *(*resolvedColorTexture)(const Framebuffer *self, Uint32 index);
275+
Texture *(*resolveColorTexture)(const Framebuffer *self, Uint32 index);
276+
277+
/**
278+
* @fn Texture *Framebuffer::resolveDepthTexture(const Framebuffer *self)
279+
* @brief Returns the single-sample, sampleable depth texture, to read scene depth (e.g. soft particles).
280+
* @details Single-sample: the depth attachment itself (created with `SAMPLER`). Multisampled: the
281+
* separate `resolveDepthTexture`, which must first be populated by a resolve pass (SDL has no depth
282+
* store-op resolve). Returns `NULL` if the framebuffer has no depth attachment.
283+
* @param self The Framebuffer.
284+
* @return The sampleable depth texture, or `NULL` if there is no depth attachment.
285+
* @memberof Framebuffer
286+
*/
287+
Texture *(*resolveDepthTexture)(const Framebuffer *self);
267288

268289
/**
269290
* @fn bool Framebuffer::resize(Framebuffer *self, const SDL_Size *size)

Sources/ObjectivelyGPU/RenderDevice.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ static void endFrame(RenderDevice *self) {
121121

122122
GPU_Assert(self->commands, "endFrame called without a frame in flight");
123123

124-
Texture *color = $(self->framebuffer, resolvedColorTexture, 0);
124+
Texture *color = $(self->framebuffer, resolveColorTexture, 0);
125125
GPU_Assert(color, "framebuffer has no color attachment to present");
126126

127127
$(self->commands, blitTexture, &(SDL_GPUBlitInfo) {

Sources/ObjectivelyGPU/RenderDevice.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ struct RenderDeviceInterface {
290290
/**
291291
* @fn void RenderDevice::endFrame(RenderDevice *self)
292292
* @brief Ends the frame begun by `beginFrame`: presents the framebuffer and submits.
293-
* @details Blits the framebuffer's resolved color (`Framebuffer::resolvedColorTexture`,
293+
* @details Blits the framebuffer's resolved color (`Framebuffer::resolveColorTexture`,
294294
* the resolve target when multisampled) into the acquired swapchain texture, submits
295295
* the frame's command buffer, and releases it. Must be paired with a non-NULL
296296
* `beginFrame` return.

0 commit comments

Comments
 (0)