@@ -39,6 +39,13 @@ typedef struct Framebuffer Framebuffer;
3939typedef struct FramebufferInterface FramebufferInterface ;
4040typedef struct Texture Texture ;
4141
42+ /**
43+ * @brief The maximum number of color attachments a Framebuffer may have.
44+ * @details A render pass binds N color targets plus at most one depth-stencil target;
45+ * this caps N. Four matches the minimum guaranteed across backends.
46+ */
47+ #define GPU_MAX_COLOR_TARGETS 4
48+
4249/**
4350 * @brief Parameters for creating a Framebuffer.
4451 * @details The GPU-layer analogue of SDL's `*CreateInfo` structs, for a target that
@@ -56,18 +63,26 @@ typedef struct GPU_FramebufferCreateInfo {
5663 SDL_Size size ;
5764
5865 /**
59- * @brief Color attachment format, or `SDL_GPU_TEXTUREFORMAT_INVALID` to omit.
66+ * @brief The color attachment formats, one per render target (MRT).
67+ * @details Indices `[0, numColorTargets)` are used. With MSAA, every color attachment
68+ * is multisampled and gets its own single-sample resolve target.
69+ */
70+ SDL_GPUTextureFormat colorFormats [GPU_MAX_COLOR_TARGETS ];
71+
72+ /**
73+ * @brief The number of color attachments; `0` to omit color entirely.
6074 */
61- SDL_GPUTextureFormat colorFormat ;
75+ Uint32 numColorTargets ;
6276
6377 /**
6478 * @brief Depth attachment format, or `SDL_GPU_TEXTUREFORMAT_INVALID` to omit.
79+ * @details A render pass supports at most one depth-stencil target, so this is singular.
6580 */
6681 SDL_GPUTextureFormat depthFormat ;
6782
6883 /**
6984 * @brief MSAA sample count; `SDL_GPU_SAMPLECOUNT_1` for no multisampling.
70- * @details When greater, a single-sample resolve target is allocated alongside the
85+ * @details When greater, a single-sample resolve target is allocated alongside each
7186 * multisampled color attachment.
7287 */
7388 SDL_GPUSampleCount sampleCount ;
@@ -88,12 +103,13 @@ typedef struct GPU_FramebufferCreateInfo {
88103 * @code
89104 * Framebuffer *fb = $(renderDevice, createFramebuffer, &(GPU_FramebufferCreateInfo) {
90105 * .size = { 1920, 1080 },
91- * .colorFormat = SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT,
106+ * .colorFormats = { SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT },
107+ * .numColorTargets = 1,
92108 * .depthFormat = SDL_GPU_TEXTUREFORMAT_D32_FLOAT,
93109 * .sampleCount = SDL_GPU_SAMPLECOUNT_1,
94110 * });
95111 *
96- * SDL_GPUColorTargetInfo color = $(fb, colorTargetInfo,
112+ * SDL_GPUColorTargetInfo color = $(fb, colorTargetInfo, 0,
97113 * SDL_GPU_LOADOP_CLEAR, SDL_GPU_STOREOP_STORE,
98114 * &(SDL_FColor) { 0, 0, 0, 1 });
99115 *
@@ -127,38 +143,48 @@ struct Framebuffer {
127143 SDL_Size size ;
128144
129145 /**
130- * @brief The color attachment texture format, or `SDL_GPU_TEXTUREFORMAT_INVALID` if none.
146+ * @brief The MSAA sample count of all attachments.
147+ * @details `SDL_GPU_SAMPLECOUNT_1` for no multisampling. When greater, every color
148+ * attachment and the depth attachment are multisampled (a render pass requires a
149+ * single sample count across all attachments); each color attachment also gets a
150+ * single-sample resolve target in `resolveTextures`.
131151 */
132- SDL_GPUTextureFormat colorFormat ;
152+ SDL_GPUSampleCount sampleCount ;
133153
134154 /**
135- * @brief The depth attachment texture format, or `SDL_GPU_TEXTUREFORMAT_INVALID` if none .
155+ * @brief The number of color attachments; indices `[0, numColorTargets)` are valid .
136156 */
137- SDL_GPUTextureFormat depthFormat ;
157+ Uint32 numColorTargets ;
138158
139159 /**
140- * @brief The MSAA sample count of the color and depth attachments.
141- * @details `SDL_GPU_SAMPLECOUNT_1` for no multisampling. When greater, `colorTexture`
142- * is multisampled and `resolveTexture` holds the single-sample resolve.
160+ * @brief The color attachment texture formats, indices `[0, numColorTargets)`.
143161 */
144- SDL_GPUSampleCount sampleCount ;
162+ SDL_GPUTextureFormat colorFormats [GPU_MAX_COLOR_TARGETS ];
163+
164+ /**
165+ * @brief The color attachment textures, indices `[0, numColorTargets)`.
166+ * @details Multisampled when `sampleCount` > `SDL_GPU_SAMPLECOUNT_1`; in that case
167+ * sample/blit/present the corresponding `resolveTextures` entry, not these.
168+ */
169+ Texture * colorTextures [GPU_MAX_COLOR_TARGETS ];
145170
146171 /**
147- * @brief The color attachment texture , or `NULL` if `colorFormat` is invalid .
148- * @details Multisampled when `sampleCount` is greater than `SDL_GPU_SAMPLECOUNT_1`;
149- * in that case sample the single-sample `resolveTexture`, not this texture .
172+ * @brief The single-sample resolve targets , or all `NULL` unless `sampleCount` > `SDL_GPU_SAMPLECOUNT_1` .
173+ * @details Render passes resolve `colorTextures[i]` into `resolveTextures[i]`; that is
174+ * the texture to sample, blit, or present. See `resolvedColorTexture` .
150175 */
151- Texture * colorTexture ;
176+ Texture * resolveTextures [ GPU_MAX_COLOR_TARGETS ] ;
152177
153178 /**
154- * @brief The single-sample resolve target, or `NULL` unless `sampleCount` is greater than `SDL_GPU_SAMPLECOUNT_1`.
155- * @details Render passes resolve `colorTexture` into this texture; it is the texture
156- * to sample, blit, or present. See `resolvedColorTexture`.
179+ * @brief The depth attachment texture format, or `SDL_GPU_TEXTUREFORMAT_INVALID` if none.
157180 */
158- Texture * resolveTexture ;
181+ SDL_GPUTextureFormat depthFormat ;
159182
160183 /**
161184 * @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).
162188 * @private
163189 */
164190 Texture * depthTexture ;
@@ -181,18 +207,20 @@ struct FramebufferInterface {
181207 ObjectInterface objectInterface ;
182208
183209 /**
184- * @fn SDL_GPUColorTargetInfo Framebuffer::colorTargetInfo(const Framebuffer *self, SDL_GPULoadOp loadOp, SDL_GPUStoreOp storeOp, const SDL_FColor *clearColor)
185- * @brief Returns a populated `SDL_GPUColorTargetInfo` for this framebuffer's color attachment.
186- * @details Pass the result directly to `CommandBuffer::beginRenderPass`.
187- * `assert`s that `colorTexture` is non-NULL.
210+ * @fn SDL_GPUColorTargetInfo Framebuffer::colorTargetInfo(const Framebuffer *self, Uint32 index, SDL_GPULoadOp loadOp, SDL_GPUStoreOp storeOp, const SDL_FColor *clearColor)
211+ * @brief Returns a populated `SDL_GPUColorTargetInfo` for color attachment @p index.
212+ * @details Assemble an array of these (one per color target) and pass it to
213+ * `CommandBuffer::beginRenderPass`. When multisampled, the resolve target and a
214+ * `RESOLVE_AND_STORE` store op are wired in automatically. `assert`s @p index is valid.
188215 * @param self The Framebuffer.
216+ * @param index The color attachment index, in `[0, numColorTargets)`.
189217 * @param loadOp Load operation at the start of the pass.
190218 * @param storeOp Store operation at the end of the pass.
191219 * @param clearColor Clear color used when `loadOp` is `SDL_GPU_LOADOP_CLEAR`, or NULL for black.
192220 * @return A stack-allocated `SDL_GPUColorTargetInfo`.
193221 * @memberof Framebuffer
194222 */
195- SDL_GPUColorTargetInfo (* colorTargetInfo )(const Framebuffer * self ,
223+ SDL_GPUColorTargetInfo (* colorTargetInfo )(const Framebuffer * self , Uint32 index ,
196224 SDL_GPULoadOp loadOp , SDL_GPUStoreOp storeOp , const SDL_FColor * clearColor );
197225
198226 /**
@@ -222,14 +250,15 @@ struct FramebufferInterface {
222250 Framebuffer * (* initWithDevice )(Framebuffer * self , RenderDevice * device , const GPU_FramebufferCreateInfo * info );
223251
224252 /**
225- * @fn Texture *Framebuffer::resolvedColorTexture(const Framebuffer *self)
226- * @brief Returns the single-sample color texture to sample, blit, or present.
227- * @details Returns `resolveTexture ` when multisampled, otherwise `colorTexture `.
253+ * @fn Texture *Framebuffer::resolvedColorTexture(const Framebuffer *self, Uint32 index )
254+ * @brief Returns the single-sample color texture for attachment @p index, to sample, blit, or present.
255+ * @details Returns `resolveTextures[index] ` when multisampled, otherwise `colorTextures[index] `.
228256 * @param self The Framebuffer.
229- * @return The resolved color texture, or `NULL` if this framebuffer has no color attachment.
257+ * @param index The color attachment index, in `[0, numColorTargets)`.
258+ * @return The resolved color texture, or `NULL` if @p index has no attachment.
230259 * @memberof Framebuffer
231260 */
232- Texture * (* resolvedColorTexture )(const Framebuffer * self );
261+ Texture * (* resolvedColorTexture )(const Framebuffer * self , Uint32 index );
233262
234263 /**
235264 * @fn bool Framebuffer::resize(Framebuffer *self, const SDL_Size *size)
0 commit comments