ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
Renderer Struct Reference

Renderer extends Object with ObjectivelyMVC's UI rendering layer. More...

#include <Renderer.h>

Inheritance diagram for Renderer:

Public Member Functions

Class * _Renderer (void)
 The Renderer archetype.
 
void beginFrame (Renderer *self)
 Prepares this Renderer for a new frame using self->device's current command buffer and framebuffer.
 
void beginFrameWith (Renderer *self, CommandBuffer *commands, Framebuffer *framebuffer)
 Prepares this Renderer for a new frame using the given command buffer and framebuffer.
 
void drawLine (const Renderer *self, const SDL_Point *points, const SDL_Color *color)
 Records a line segment between two points.
 
void drawLines (const Renderer *self, const SDL_Point *points, size_t count, const SDL_Color *color)
 Records a polyline through the given points.
 
void drawRect (const Renderer *self, const SDL_Rect *rect, const SDL_Color *color)
 Records a rectangle outline.
 
void drawRectFilled (const Renderer *self, const SDL_Rect *rect, const SDL_Color *color)
 Records a filled rectangle.
 
void drawTexture (const Renderer *self, Texture *texture, const SDL_Rect *dest, const SDL_Color *color)
 Records a textured quad in the given destination rectangle.
 
void drawView (Renderer *self, View *view)
 Sets the clipping frame and invokes View::render for the given View.
 
void endFrame (Renderer *self)
 Uploads MVC vertices and executes the UI render pass into the Framebuffer given to beginFrame/beginFrameWith (LOAD_OP_LOAD).
 
RendererinitWithDevice (Renderer *self, RenderDevice *device)
 Initializes this Renderer with the given RenderDevice.
 
void pushDrawArrays (const Renderer *self, const MVC_Vertex *verts, size_t count, Texture *texture, const SDL_Color *color)
 Appends raw vertices and a draw call record to the frame queue.
 
void renderDeviceDidReset (Renderer *self)
 Recreates MVC GPU resources after the backing RenderDevice resets.
 
void renderDeviceWillReset (Renderer *self)
 Releases MVC GPU resources before the backing RenderDevice resets.
 
void setClippingFrame (Renderer *self, const SDL_Rect *clippingFrame)
 Sets the scissor rectangle for subsequent draw calls.
 

Data Fields

RenderDevice * device
 The backing RenderDevice.
 
Object object
 The superclass.
 

Protected Attributes

RendererInterface * interface
 The interface.
 

Detailed Description

Renderer extends Object with ObjectivelyMVC's UI rendering layer.

Create a WindowController to instantiate a default Renderer.

Definition at line 70 of file Renderer.h.

Member Function Documentation

◆ _Renderer()

Class * _Renderer ( void  )

The Renderer archetype.

Returns
The Renderer Class.

Definition at line 549 of file Renderer.c.

549 {
550 static Class *clazz;
551 static Once once;
552
553 do_once(&once, {
554 clazz = _initialize(&(const ClassDef) {
555 .name = "Renderer",
556 .superclass = _Object(),
557 .instanceSize = sizeof(Renderer),
558 .interfaceOffset = offsetof(Renderer, interface),
559 .interfaceSize = sizeof(RendererInterface),
561 });
562 });
563
564 return clazz;
565}
static void initialize(Class *clazz)
Definition Box.c:123
Renderer extends Object with ObjectivelyMVC's UI rendering layer.
Definition Renderer.h:70
RendererInterface * interface
The interface.
Definition Renderer.h:81

◆ beginFrame()

void beginFrame ( Renderer self)

Prepares this Renderer for a new frame using self->device's current command buffer and framebuffer.

Convenience over beginFrameWith for the common case of rendering the UI into the device's own current frame. Equivalent to $(self, beginFrameWith, self->device->commands, self->device->framebuffer).

Parameters
selfThe Renderer.

Definition at line 100 of file Renderer.c.

100 {
101 $(self, beginFrameWith, self->device->commands, self->device->framebuffer);
102}
RenderDevice * device
The backing RenderDevice.
Definition Renderer.h:92
void beginFrameWith(Renderer *self, CommandBuffer *commands, Framebuffer *framebuffer)
Prepares this Renderer for a new frame using the given command buffer and framebuffer.
Definition Renderer.c:108

◆ beginFrameWith()

void beginFrameWith ( Renderer self,
CommandBuffer *  commands,
Framebuffer *  framebuffer 
)

Prepares this Renderer for a new frame using the given command buffer and framebuffer.

Parameters
selfThe Renderer.
commandsThe frame's CommandBuffer. The caller retains ownership and must submit and release it.
framebufferThe target Framebuffer for this frame. Borrowed for the duration of the frame.

Definition at line 108 of file Renderer.c.

108 {
109
110 assert(commands);
111 assert(framebuffer);
112
113 self->commands = commands;
114 self->framebuffer = framebuffer;
115
116 $(self->vertices, removeAll);
117 $(self->drawArrays, removeAll);
118
119 self->scissor = MakeRect(0, 0, framebuffer->size.w, framebuffer->size.h);
120}

◆ drawLine()

void drawLine ( const Renderer self,
const SDL_Point *  points,
const SDL_Color *  color 
)

Records a line segment between two points.

Parameters
selfThe Renderer.
pointsTwo points defining the line segment.
colorThe line color.

Definition at line 126 of file Renderer.c.

126 {
127
128 assert(points);
129
130 $(self, drawLines, points, 2, color);
131}
void drawLines(const Renderer *self, const SDL_Point *points, size_t count, const SDL_Color *color)
Records a polyline through the given points.
Definition Renderer.c:137

◆ drawLines()

void drawLines ( const Renderer self,
const SDL_Point *  points,
size_t  count,
const SDL_Color *  color 
)

Records a polyline through the given points.

Parameters
selfThe Renderer.
pointsThe points.
countThe number of points.
colorThe line color.

Definition at line 137 of file Renderer.c.

137 {
138
139 assert(points);
140 assert(color);
141
142 if (count < 2) {
143 return;
144 }
145
146 const size_t segCount = count - 1;
147 MVC_Vertex *verts = malloc(segCount * 6 * sizeof(MVC_Vertex));
148 assert(verts);
149
150 for (size_t i = 0; i < segCount; i++) {
151 const float ax = (float) points[i].x, ay = (float) points[i].y;
152 const float bx = (float) points[i+1].x, by = (float) points[i+1].y;
153
154 const float dx = bx - ax, dy = by - ay;
155 const float len = sqrtf(dx * dx + dy * dy);
156
157 float nx = 0.0f, ny = 0.0f;
158 if (len > 0.001f) {
159 nx = (-dy / len) * 0.5f;
160 ny = ( dx / len) * 0.5f;
161 }
162
163 MVC_Vertex *v = &verts[i * 6];
164 v[0] = (MVC_Vertex) { { { ax - nx, ay - ny } }, { { 0.0f, 0.0f } }, { 0 } };
165 v[1] = (MVC_Vertex) { { { ax + nx, ay + ny } }, { { 0.0f, 0.0f } }, { 0 } };
166 v[2] = (MVC_Vertex) { { { bx - nx, by - ny } }, { { 0.0f, 0.0f } }, { 0 } };
167 v[3] = (MVC_Vertex) { { { ax + nx, ay + ny } }, { { 0.0f, 0.0f } }, { 0 } };
168 v[4] = (MVC_Vertex) { { { bx + nx, by + ny } }, { { 0.0f, 0.0f } }, { 0 } };
169 v[5] = (MVC_Vertex) { { { bx - nx, by - ny } }, { { 0.0f, 0.0f } }, { 0 } };
170 }
171
172 $(self, pushDrawArrays, verts, segCount * 6, NULL, color);
173
174 free(verts);
175}
Interleaved position + texcoord + color vertex for GPU upload.
Definition Renderer.h:48
void pushDrawArrays(const Renderer *self, const MVC_Vertex *verts, size_t count, Texture *texture, const SDL_Color *color)
Appends raw vertices and a draw call record to the frame queue.
Definition Renderer.c:360

◆ drawRect()

void drawRect ( const Renderer self,
const SDL_Rect *  rect,
const SDL_Color *  color 
)

Records a rectangle outline.

Parameters
selfThe Renderer.
rectThe rectangle.
colorThe outline color.

Definition at line 181 of file Renderer.c.

181 {
182
183 assert(rect);
184
185 const SDL_Point points[5] = {
186 { rect->x, rect->y },
187 { rect->x + rect->w, rect->y },
188 { rect->x + rect->w, rect->y + rect->h },
189 { rect->x, rect->y + rect->h },
190 { rect->x, rect->y },
191 };
192
193 $(self, drawLines, points, 5, color);
194}

◆ drawRectFilled()

void drawRectFilled ( const Renderer self,
const SDL_Rect *  rect,
const SDL_Color *  color 
)

Records a filled rectangle.

Parameters
selfThe Renderer.
rectThe rectangle.
colorThe fill color.

Definition at line 200 of file Renderer.c.

200 {
201
202 assert(rect);
203
204 const float x1 = (float) rect->x, y1 = (float) rect->y;
205 const float x2 = (float) rect->x + rect->w, y2 = (float) rect->y + rect->h;
206
207 const MVC_Vertex verts[6] = {
208 { { { x1, y1 } }, { { 0.0f, 0.0f } }, { 0 } },
209 { { { x2, y1 } }, { { 0.0f, 0.0f } }, { 0 } },
210 { { { x1, y2 } }, { { 0.0f, 0.0f } }, { 0 } },
211 { { { x2, y1 } }, { { 0.0f, 0.0f } }, { 0 } },
212 { { { x2, y2 } }, { { 0.0f, 0.0f } }, { 0 } },
213 { { { x1, y2 } }, { { 0.0f, 0.0f } }, { 0 } },
214 };
215
216 $(self, pushDrawArrays, verts, 6, NULL, color);
217}

◆ drawTexture()

void drawTexture ( const Renderer self,
Texture *  texture,
const SDL_Rect *  dest,
const SDL_Color *  color 
)

Records a textured quad in the given destination rectangle.

Parameters
selfThe Renderer.
textureThe Texture to sample.
destThe destination rectangle in logical screen coordinates.
colorThe color multiplier (use &Colors.White for no tint).

Definition at line 223 of file Renderer.c.

223 {
224
225 assert(rect);
226
227 const float x1 = (float) rect->x, y1 = (float) rect->y;
228 const float x2 = (float) rect->x + rect->w, y2 = (float) rect->y + rect->h;
229
230 const MVC_Vertex verts[6] = {
231 { { { x1, y1 } }, { { 0.0f, 0.0f } }, { 0 } },
232 { { { x2, y1 } }, { { 1.0f, 0.0f } }, { 0 } },
233 { { { x1, y2 } }, { { 0.0f, 1.0f } }, { 0 } },
234 { { { x2, y1 } }, { { 1.0f, 0.0f } }, { 0 } },
235 { { { x2, y2 } }, { { 1.0f, 1.0f } }, { 0 } },
236 { { { x1, y2 } }, { { 0.0f, 1.0f } }, { 0 } },
237 };
238
239 $(self, pushDrawArrays, verts, 6, texture, color);
240}

◆ drawView()

void drawView ( Renderer self,
View view 
)

Sets the clipping frame and invokes View::render for the given View.

Parameters
selfThe Renderer.
viewThe View to render.

Definition at line 246 of file Renderer.c.

246 {
247
248 assert(view);
249
250 const SDL_Rect clippingFrame = $(view, clippingFrame);
251 if (clippingFrame.w && clippingFrame.h) {
253 $(view, render, self);
254 }
255}
static void render(View *self, Renderer *renderer)
Definition Control.c:179
static SDL_Rect clippingFrame(const View *self)
Definition View.c:546
void setClippingFrame(Renderer *self, const SDL_Rect *clippingFrame)
Sets the scissor rectangle for subsequent draw calls.
Definition Renderer.c:509

◆ endFrame()

void endFrame ( Renderer self)

Uploads MVC vertices and executes the UI render pass into the Framebuffer given to beginFrame/beginFrameWith (LOAD_OP_LOAD).

The caller is responsible for submitting the command buffer after this returns.

Parameters
selfThe Renderer.

Definition at line 261 of file Renderer.c.

261 {
262
263 Framebuffer *framebuffer = self->framebuffer;
264 assert(framebuffer);
265
266 const SDL_GPUColorTargetInfo colorTarget = $(framebuffer, colorTargetInfo, 0, SDL_GPU_LOADOP_LOAD, SDL_GPU_STOREOP_STORE);
267
268 const size_t vertexCount = self->vertices->count;
269
270 CopyPass *copyPass = $(self->commands, beginCopyPass);
271
272 if (vertexCount > 0) {
273 const Uint32 vertexSize = (Uint32) (vertexCount * sizeof(MVC_Vertex));
274
275 if (vertexCount > self->vertexBufferCapacity) {
276 release(self->vertexBuffer);
277 self->vertexBuffer = $(self->device, createBuffer, &(SDL_GPUBufferCreateInfo) {
278 .usage = SDL_GPU_BUFFERUSAGE_VERTEX,
279 .size = vertexSize
280 });
281
282 release(self->transferBuffer);
283 self->transferBuffer = $(self->device, createTransferBuffer, &(SDL_GPUTransferBufferCreateInfo) {
284 .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
285 .size = vertexSize,
286 });
287
288 self->vertexBufferCapacity = (Uint32) vertexCount;
289 }
290
291 $(self->transferBuffer, write, self->vertices->elements, vertexSize, true);
292
293 $(copyPass, uploadBuffer,
294 &(SDL_GPUTransferBufferLocation) { .transfer_buffer = self->transferBuffer->buffer },
295 &(SDL_GPUBufferRegion) { .buffer = self->vertexBuffer->buffer, .size = vertexSize },
296 true);
297 }
298
299 release(copyPass);
300
301 RenderPass *renderPass = $(self->commands, beginRenderPass, &colorTarget, 1, NULL);
302
303 $(renderPass, setViewport, &(SDL_GPUViewport){
304 .x = 0.0f, .y = 0.0f,
305 .w = (float) framebuffer->size.w, .h = (float) framebuffer->size.h,
306 .min_depth = 0.0f, .max_depth = 1.0f,
307 });
308
309 int winW, winH;
310 SDL_GetWindowSize(self->device->window, &winW, &winH);
311 const mat4 projection = mat4_ortho(0.f, (float) winW, (float) winH, 0.f, -1.f, 1.f);
312 $(self->commands, pushVertexUniformData, 0, projection.f, sizeof(projection));
313
314 $(renderPass, bindPipeline, self->pipeline);
315 $(renderPass, bindVertexBuffers, 0, &(SDL_GPUBufferBinding) { .buffer = self->vertexBuffer->buffer }, 1);
316
317 for (size_t i = 0; i < self->drawArrays->count; i++) {
318 const MVC_DrawArrays *draw = VectorElement(self->drawArrays, MVC_DrawArrays, i);
319
320 $(renderPass, setScissor, &draw->scissor);
321
322 $(renderPass, bindFragmentSamplers, 0, &(SDL_GPUTextureSamplerBinding) {
323 .texture = draw->texture->texture, .sampler = self->sampler->sampler,
324 }, 1);
325
326 $(renderPass, drawPrimitives, draw->vertexCount, 1, draw->firstVertex, 0);
327 }
328
329 release(renderPass);
330
331 self->commands = NULL;
332 self->framebuffer = NULL;
333}
static void draw(View *self, Renderer *renderer)
Definition View.c:709

◆ initWithDevice()

Renderer * initWithDevice ( Renderer self,
RenderDevice *  device 
)

Initializes this Renderer with the given RenderDevice.

Parameters
selfThe Renderer.
deviceThe RenderDevice.
Returns
The initialized Renderer, or NULL on error.

Definition at line 339 of file Renderer.c.

339 {
340
341 self = (Renderer *) super(Object, self, init);
342 if (self) {
343 self->device = retain(device);
344 assert(self->device);
345
346 self->vertices = $(alloc(Vector), initWithSize, sizeof(MVC_Vertex));
347 assert(self->vertices);
348
349 self->drawArrays = $(alloc(Vector), initWithSize, sizeof(MVC_DrawArrays));
350 assert(self->drawArrays);
351 }
352
353 return self;
354}
static View * init(View *self)
Definition Box.c:67

◆ pushDrawArrays()

void pushDrawArrays ( const Renderer self,
const MVC_Vertex verts,
size_t  count,
Texture *  texture,
const SDL_Color *  color 
)

Appends raw vertices and a draw call record to the frame queue.

Views that need full draw-call control can call this directly instead of going through the drawLine/drawRect/drawTexture helpers.

Parameters
selfThe Renderer.
vertsThe vertices to append (in logical screen coordinates).
countThe number of vertices.
textureThe texture to bind, or NULL to use the 1×1 white fallback.
colorThe color multiplier applied in the fragment shader.

Definition at line 360 of file Renderer.c.

360 {
361
362 assert(verts);
363 assert(color);
364
365 const MVC_DrawArrays draw = {
366 .firstVertex = (Uint32) self->vertices->count,
367 .vertexCount = (Uint32) count,
368 .texture = texture ? texture : self->white,
369 .scissor = self->scissor,
370 };
371
372 for (size_t i = 0; i < count; i++) {
373 MVC_Vertex v = verts[i];
374 v.color = *color;
375 $(self->vertices, add, &v);
376 }
377
378 $(self->drawArrays, add, (MVC_DrawArrays *) &draw);
379}
SDL_Color color
Definition Renderer.h:51

◆ renderDeviceDidReset()

void renderDeviceDidReset ( Renderer self)

Recreates MVC GPU resources after the backing RenderDevice resets.

Parameters
selfThe Renderer.

Definition at line 385 of file Renderer.c.

385 {
386
387 Shader *vertexShader = $(self->device, loadShader, "Renderer.vert", &(SDL_GPUShaderCreateInfo) {
388 .stage = SDL_GPU_SHADERSTAGE_VERTEX,
389 .num_uniform_buffers = 1,
390 });
391
392 Shader *fragmentShader = $(self->device, loadShader, "Renderer.frag", &(SDL_GPUShaderCreateInfo) {
393 .stage = SDL_GPU_SHADERSTAGE_FRAGMENT,
394 .num_samplers = 1,
395 });
396
397 const SDL_GPUVertexBufferDescription vertexBufferInfo = {
398 .slot = 0,
399 .pitch = sizeof(MVC_Vertex),
400 .input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX,
401 .instance_step_rate = 0,
402 };
403
404 const SDL_GPUColorTargetDescription colorTarget = {
405 .format = $(self->device, getSwapchainTextureFormat),
406 .blend_state = {
407 .enable_blend = true,
408 .color_blend_op = SDL_GPU_BLENDOP_ADD,
409 .alpha_blend_op = SDL_GPU_BLENDOP_ADD,
410 .src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA,
411 .dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
412 .src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE,
413 .dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
414 },
415 };
416
417 const SDL_GPUGraphicsPipelineCreateInfo pipelineInfo = {
418 .vertex_shader = vertexShader->shader,
419 .fragment_shader = fragmentShader->shader,
420 .vertex_input_state = {
421 .vertex_buffer_descriptions = &vertexBufferInfo,
422 .num_vertex_buffers = 1,
423 .vertex_attributes = (SDL_GPUVertexAttribute[]) {
424 {
425 .location = 0,
426 .buffer_slot = 0,
427 .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,
428 .offset = offsetof(MVC_Vertex, position)
429 },
430 {
431 .location = 1,
432 .buffer_slot = 0,
433 .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,
434 .offset = offsetof(MVC_Vertex, uv)
435 },
436 {
437 .location = 2,
438 .buffer_slot = 0,
439 .format = SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM,
440 .offset = offsetof(MVC_Vertex, color)
441 },
442 },
443 .num_vertex_attributes = 3,
444 },
445 .primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
446 .rasterizer_state = {
447 .fill_mode = SDL_GPU_FILLMODE_FILL,
448 .cull_mode = SDL_GPU_CULLMODE_NONE,
449 .front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE,
450 },
451 .target_info = {
452 .color_target_descriptions = &colorTarget,
453 .num_color_targets = 1,
454 },
455 };
456
457 self->pipeline = $(self->device, createGraphicsPipeline, &pipelineInfo);
458
459 release(vertexShader);
460 release(fragmentShader);
461
462 self->sampler = $(self->device, createSampler, &(const SDL_GPUSamplerCreateInfo) {
463 .min_filter = SDL_GPU_FILTER_LINEAR,
464 .mag_filter = SDL_GPU_FILTER_LINEAR,
465 .mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR,
466 .address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
467 .address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
468 .address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
469 });
470
471 const Uint8 white[4] = { 255, 255, 255, 255 };
472
473 self->white = $(self->device, createTexture, &(const SDL_GPUTextureCreateInfo) {
474 .type = SDL_GPU_TEXTURETYPE_2D,
475 .format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM,
476 .usage = SDL_GPU_TEXTUREUSAGE_SAMPLER,
477 .width = 1,
478 .height = 1,
479 .layer_count_or_depth = 1,
480 .num_levels = 1,
481 }, white);
482}

◆ renderDeviceWillReset()

void renderDeviceWillReset ( Renderer self)

Releases MVC GPU resources before the backing RenderDevice resets.

Parameters
selfThe Renderer.

Definition at line 488 of file Renderer.c.

488 {
489
490 self->commands = NULL;
491
492 self->white = release(self->white);
493 self->sampler = release(self->sampler);
494
495 self->vertexBuffer = release(self->vertexBuffer);
496 self->transferBuffer = release(self->transferBuffer);
497 self->vertexBufferCapacity = 0;
498
499 self->pipeline = release(self->pipeline);
500
501 $(self->vertices, removeAll);
502 $(self->drawArrays, removeAll);
503}

◆ setClippingFrame()

void setClippingFrame ( Renderer self,
const SDL_Rect *  clippingFrame 
)

Sets the scissor rectangle for subsequent draw calls.

Parameters
selfThe Renderer.
clippingFrameThe clipping rectangle in logical screen coordinates, or NULL to disable clipping (full window scissor).

Definition at line 509 of file Renderer.c.

509 {
510
511 if (clippingFrame) {
512 self->scissor = MVC_TransformToWindow(self->device->window, clippingFrame);
513 } else {
514 self->scissor = MakeRect(0, 0, self->framebuffer->size.w, self->framebuffer->size.h);
515 }
516}
SDL_Rect MVC_TransformToWindow(SDL_Window *window, const SDL_Rect *rect)
Transforms the specified rectangle to normalized device coordinates in window.
Definition Window.c:29

Field Documentation

◆ device

RenderDevice* Renderer::device

The backing RenderDevice.

Definition at line 92 of file Renderer.h.

◆ interface

RendererInterface* Renderer::interface
protected

The interface.

Definition at line 81 of file Renderer.h.

◆ object

Object Renderer::object

The superclass.

Definition at line 75 of file Renderer.h.


The documentation for this struct was generated from the following files: