ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
Renderer.c File Reference
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <Objectively/Vector.h>
#include <ObjectivelyGPU/CopyPass.h>
#include <ObjectivelyGPU/Mathlib.h>
#include <ObjectivelyGPU/RenderPass.h>
#include "Renderer.h"
#include "View.h"
#include "Window.h"
#include "../Assets/Renderer.frag.dxil.h"
#include "../Assets/Renderer.frag.metal.h"
#include "../Assets/Renderer.frag.spv.h"
#include "../Assets/Renderer.vert.dxil.h"
#include "../Assets/Renderer.vert.metal.h"
#include "../Assets/Renderer.vert.spv.h"

Go to the source code of this file.

Macros

#define _Class   _Renderer
 

Functions

Class * _Renderer (void)
 
static void beginFrame (Renderer *self)
 
static void beginFrameWith (Renderer *self, CommandBuffer *commands, Framebuffer *framebuffer)
 
static void dealloc (Object *self)
 
static void drawBevel (const Renderer *self, const SDL_Rect *rect, int radius, int width, const SDL_Color *topLeft, const SDL_Color *bottomRight)
 
static void drawLine (const Renderer *self, const SDL_Point *points, const SDL_Color *color)
 
static void drawLines (const Renderer *self, const SDL_Point *points, size_t count, const SDL_Color *color)
 
static void drawPolygon (const Renderer *self, const SDL_Point *points, size_t count, int width, const SDL_Color *color)
 
static void drawPolygonFilled (const Renderer *self, const SDL_Point *points, size_t count, int angle, const SDL_Color *from, const SDL_Color *to)
 
static void drawRect (const Renderer *self, const SDL_Rect *rect, const SDL_Color *color)
 
static void drawRectFilled (const Renderer *self, const SDL_Rect *rect, const SDL_Color *color)
 
static void drawRoundedRect (const Renderer *self, const SDL_Rect *rect, int radius, int width, const SDL_Color *color)
 
static void drawRoundedRectFilled (const Renderer *self, const SDL_Rect *rect, int radius, const SDL_Color *color)
 
static void drawRoundedRectGradientFilled (const Renderer *self, const SDL_Rect *rect, int radius, int angle, const SDL_Color *from, const SDL_Color *to)
 
static void drawRoundedTexture (const Renderer *self, Texture *texture, const SDL_FRect *dest, int radius, const SDL_Color *color)
 
static void drawRoundedTextureRegion (const Renderer *self, Texture *texture, const SDL_Rect *src, const SDL_FRect *dest, int radius, const SDL_Color *color)
 
static void drawTexture (const Renderer *self, Texture *texture, const SDL_FRect *dest, const SDL_Color *color)
 
static void drawTextureRegion (const Renderer *self, Texture *texture, const SDL_Rect *src, const SDL_FRect *dest, const SDL_Color *color)
 
static void drawView (Renderer *self, View *view)
 
static void endFrame (Renderer *self)
 
static SDL_Color gradientColor (float x, float y, const SDL_FRect *bounds, int angle, const SDL_Color *from, const SDL_Color *to)
 The color at x, y for a gradient of angle degrees across bounds.
 
static void initialize (Class *clazz)
 
static Renderer * initWithDevice (Renderer *self, RenderDevice *device)
 
static void pushDrawArrays (const Renderer *self, const MVC_Vertex *verts, size_t count, Texture *texture, const SDL_Color *color)
 
static void renderDeviceDidReset (Renderer *self)
 
static void renderDeviceWillReset (Renderer *self)
 
static void roundedRectVertices (MVC_Vertex *verts, const SDL_FRect *rect, int radius, int stroke, const SDL_FRect *uv)
 Fills six vertices covering rect as two triangles, tagged so the fragment shader shades them as a rounded rectangle of the given radius and stroke width.
 
static void setClippingFrame (Renderer *self, const SDL_Rect *clippingFrame)
 
static Data * shaderResourceProvider (const char *name)
 ResourceProvider for loading shaders from xxd headers.
 
static void strokePolyline (const Renderer *self, const SDL_Point *points, size_t count, bool closed, float width, const SDL_Color *color)
 Appends the quads stroking points, of width, optionally closing the loop.
 
static SDL_FRect textureRegion (const Texture *texture, const SDL_Rect *src)
 Resolves the normalized texture coordinates of src within texture, or of the whole texture when src is NULL.
 

Macro Definition Documentation

◆ _Class

#define _Class   _Renderer

Definition at line 69 of file Renderer.c.

Function Documentation

◆ _Renderer()

Class * _Renderer ( void  )

Definition at line 890 of file Renderer.c.

890 {
891 static Class *clazz;
892 static Once once;
893
894 do_once(&once, {
895 clazz = _initialize(&(const ClassDef) {
896 .name = "Renderer",
897 .superclass = _Object(),
898 .instanceSize = sizeof(Renderer),
899 .interfaceSize = sizeof(RendererInterface),
900 .initialize = initialize,
901 });
902 });
903
904 return clazz;
905}
static void initialize(Class *clazz)
Definition Renderer.c:855
Renderer extends Object with ObjectivelyMVC's UI rendering layer.
Definition Renderer.h:75

◆ beginFrame()

static void beginFrame ( Renderer *  self)
static

Definition at line 101 of file Renderer.c.

101 {
102 $(self, beginFrameWith, self->device->commands, self->device->framebuffer);
103}
static void beginFrameWith(Renderer *self, CommandBuffer *commands, Framebuffer *framebuffer)
Definition Renderer.c:109
RenderDevice * device
The backing RenderDevice.
Definition Renderer.h:97

◆ beginFrameWith()

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

Definition at line 109 of file Renderer.c.

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

◆ dealloc()

static void dealloc ( Object *  self)
static
See also
Object::dealloc(Object *)

Definition at line 76 of file Renderer.c.

76 {
77
78 Renderer *this = (Renderer *) self;
79
80 this->commands = NULL;
81
82 release(this->white);
83 release(this->sampler);
84 release(this->vertexBuffer);
85 release(this->transferBuffer);
86 release(this->pipeline);
87
88 release(this->vertices);
89 release(this->drawArrays);
90 release(this->device);
91
92 super(Object, self, dealloc);
93}
static void dealloc(Object *self)
Definition Renderer.c:76

◆ drawBevel()

static void drawBevel ( const Renderer *  self,
const SDL_Rect *  rect,
int  radius,
int  width,
const SDL_Color *  topLeft,
const SDL_Color *  bottomRight 
)
static

Definition at line 247 of file Renderer.c.

248 {
249
250 assert(rect);
251
252 SDL_FRect frect;
253 SDL_RectToFRect(rect, &frect);
254
255 MVC_Vertex verts[6];
256 roundedRectVertices(verts, &frect, radius, width, NULL);
257
258 $(self, pushDrawArrays, verts, 3, NULL, topLeft);
259 $(self, pushDrawArrays, verts + 3, 3, NULL, bottomRight);
260}
static void pushDrawArrays(const Renderer *self, const MVC_Vertex *verts, size_t count, Texture *texture, const SDL_Color *color)
Definition Renderer.c:660
static void roundedRectVertices(MVC_Vertex *verts, const SDL_FRect *rect, int radius, int stroke, const SDL_FRect *uv)
Fills six vertices covering rect as two triangles, tagged so the fragment shader shades them as a rou...
Definition Renderer.c:128
Interleaved position + texcoord + color + rounded rect shape vertex for GPU upload.
Definition Renderer.h:51

◆ drawLine()

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

Definition at line 266 of file Renderer.c.

266 {
267
268 assert(points);
269
270 $(self, drawLines, points, 2, color);
271}
static void drawLines(const Renderer *self, const SDL_Point *points, size_t count, const SDL_Color *color)
Definition Renderer.c:277

◆ drawLines()

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

Definition at line 277 of file Renderer.c.

277 {
278
279 assert(points);
280 assert(color);
281
282 if (count < 2) {
283 return;
284 }
285
286 strokePolyline(self, points, count, false, 1.f, color);
287}
static void strokePolyline(const Renderer *self, const SDL_Point *points, size_t count, bool closed, float width, const SDL_Color *color)
Appends the quads stroking points, of width, optionally closing the loop.
Definition Renderer.c:202

◆ drawPolygon()

static void drawPolygon ( const Renderer *  self,
const SDL_Point *  points,
size_t  count,
int  width,
const SDL_Color *  color 
)
static

Definition at line 293 of file Renderer.c.

294 {
295
296 assert(points);
297 assert(color);
298
299 if (count < 3 || width < 1) {
300 return;
301 }
302
303 strokePolyline(self, points, count, true, (float) width, color);
304}

◆ drawPolygonFilled()

static void drawPolygonFilled ( const Renderer *  self,
const SDL_Point *  points,
size_t  count,
int  angle,
const SDL_Color *  from,
const SDL_Color *  to 
)
static

Definition at line 310 of file Renderer.c.

311 {
312
313 assert(points);
314 assert(from);
315
316 if (count < 3) {
317 return;
318 }
319
320 SDL_FRect bounds = { (float) points[0].x, (float) points[0].y, 0.f, 0.f };
321 float x2 = bounds.x, y2 = bounds.y;
322
323 for (size_t i = 1; i < count; i++) {
324 bounds.x = min(bounds.x, (float) points[i].x);
325 bounds.y = min(bounds.y, (float) points[i].y);
326 x2 = max(x2, (float) points[i].x);
327 y2 = max(y2, (float) points[i].y);
328 }
329
330 bounds.w = x2 - bounds.x;
331 bounds.h = y2 - bounds.y;
332
333 // a convex polygon triangulates as a fan from its first vertex
334 const size_t triangles = count - 2;
335
336 MVC_Vertex verts[16 * 3];
337 const size_t batchSize = lengthof(verts) / 3;
338
339 for (size_t t = 0; t < triangles; ) {
340
341 const size_t batch = min(triangles - t, batchSize);
342
343 for (size_t i = 0; i < batch; i++, t++) {
344
345 const SDL_Point *p[3] = { &points[0], &points[t + 1], &points[t + 2] };
346
347 for (size_t j = 0; j < 3; j++) {
348
349 const float x = (float) p[j]->x, y = (float) p[j]->y;
350
351 verts[i * 3 + j] = (MVC_Vertex) { { { x, y } }, { { 0.f, 0.f } },
352 to ? gradientColor(x, y, &bounds, angle, from, to) : *from
353 };
354 }
355 }
356
357 $(self, pushDrawArrays, verts, batch * 3, NULL, NULL);
358 }
359}
static SDL_Color gradientColor(float x, float y, const SDL_FRect *bounds, int angle, const SDL_Color *from, const SDL_Color *to)
The color at x, y for a gradient of angle degrees across bounds.
Definition Renderer.c:170
static SDL_Rect bounds(const View *self)
Definition View.c:552

◆ drawRect()

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

Definition at line 365 of file Renderer.c.

365 {
366
367 assert(rect);
368
369 const SDL_Point points[5] = {
370 { rect->x, rect->y },
371 { rect->x + rect->w, rect->y },
372 { rect->x + rect->w, rect->y + rect->h },
373 { rect->x, rect->y + rect->h },
374 { rect->x, rect->y },
375 };
376
377 $(self, drawLines, points, 5, color);
378}

◆ drawRectFilled()

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

Definition at line 384 of file Renderer.c.

384 {
385
386 assert(rect);
387
388 const float x1 = (float) rect->x, y1 = (float) rect->y;
389 const float x2 = (float) rect->x + rect->w, y2 = (float) rect->y + rect->h;
390
391 const MVC_Vertex verts[6] = {
392 { { { x1, y1 } }, { { 0.0f, 0.0f } }, { 0 } },
393 { { { x2, y1 } }, { { 0.0f, 0.0f } }, { 0 } },
394 { { { x1, y2 } }, { { 0.0f, 0.0f } }, { 0 } },
395 { { { x2, y1 } }, { { 0.0f, 0.0f } }, { 0 } },
396 { { { x2, y2 } }, { { 0.0f, 0.0f } }, { 0 } },
397 { { { x1, y2 } }, { { 0.0f, 0.0f } }, { 0 } },
398 };
399
400 $(self, pushDrawArrays, verts, 6, NULL, color);
401}

◆ drawRoundedRect()

static void drawRoundedRect ( const Renderer *  self,
const SDL_Rect *  rect,
int  radius,
int  width,
const SDL_Color *  color 
)
static

Definition at line 407 of file Renderer.c.

407 {
408
409 assert(rect);
410
411 SDL_FRect frect;
412 SDL_RectToFRect(rect, &frect);
413
414 MVC_Vertex verts[6];
415 roundedRectVertices(verts, &frect, radius, width, NULL);
416
417 $(self, pushDrawArrays, verts, 6, NULL, color);
418}

◆ drawRoundedRectFilled()

static void drawRoundedRectFilled ( const Renderer *  self,
const SDL_Rect *  rect,
int  radius,
const SDL_Color *  color 
)
static

Definition at line 424 of file Renderer.c.

424 {
425
426 assert(rect);
427
428 SDL_FRect frect;
429 SDL_RectToFRect(rect, &frect);
430
431 MVC_Vertex verts[6];
432 roundedRectVertices(verts, &frect, radius, 0, NULL);
433
434 $(self, pushDrawArrays, verts, 6, NULL, color);
435}

◆ drawRoundedRectGradientFilled()

static void drawRoundedRectGradientFilled ( const Renderer *  self,
const SDL_Rect *  rect,
int  radius,
int  angle,
const SDL_Color *  from,
const SDL_Color *  to 
)
static

Definition at line 441 of file Renderer.c.

442 {
443
444 assert(rect);
445 assert(from);
446 assert(to);
447
448 SDL_FRect frect;
449 SDL_RectToFRect(rect, &frect);
450
451 MVC_Vertex verts[6];
452 roundedRectVertices(verts, &frect, radius, 0, NULL);
453
454 for (size_t i = 0; i < lengthof(verts); i++) {
455 verts[i].color = gradientColor(verts[i].position.x, verts[i].position.y, &frect, angle, from, to);
456 }
457
458 $(self, pushDrawArrays, verts, lengthof(verts), NULL, NULL);
459}
SDL_Color color
Definition Renderer.h:54

◆ drawRoundedTexture()

static void drawRoundedTexture ( const Renderer *  self,
Texture *  texture,
const SDL_FRect *  dest,
int  radius,
const SDL_Color *  color 
)
static

Definition at line 465 of file Renderer.c.

465 {
466 $(self, drawRoundedTextureRegion, texture, NULL, dest, radius, color);
467}
static Texture * texture(ImageAtlas *self, RenderDevice *device)
Definition ImageAtlas.c:289
static void drawRoundedTextureRegion(const Renderer *self, Texture *texture, const SDL_Rect *src, const SDL_FRect *dest, int radius, const SDL_Color *color)
Definition Renderer.c:473

◆ drawRoundedTextureRegion()

static void drawRoundedTextureRegion ( const Renderer *  self,
Texture *  texture,
const SDL_Rect *  src,
const SDL_FRect *  dest,
int  radius,
const SDL_Color *  color 
)
static

Definition at line 473 of file Renderer.c.

473 {
474
475 assert(dest);
476
477 const SDL_FRect uv = textureRegion(texture, src);
478
479 MVC_Vertex verts[6];
480 roundedRectVertices(verts, dest, radius, 0, &uv);
481
482 $(self, pushDrawArrays, verts, 6, texture, color);
483}
static SDL_FRect textureRegion(const Texture *texture, const SDL_Rect *src)
Resolves the normalized texture coordinates of src within texture, or of the whole texture when src i...
Definition Renderer.c:151

◆ drawTexture()

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

Definition at line 489 of file Renderer.c.

489 {
490 $(self, drawTextureRegion, texture, NULL, dest, color);
491}
static void drawTextureRegion(const Renderer *self, Texture *texture, const SDL_Rect *src, const SDL_FRect *dest, const SDL_Color *color)
Definition Renderer.c:497

◆ drawTextureRegion()

static void drawTextureRegion ( const Renderer *  self,
Texture *  texture,
const SDL_Rect *  src,
const SDL_FRect *  dest,
const SDL_Color *  color 
)
static

Definition at line 497 of file Renderer.c.

497 {
498
499 assert(dest);
500
501 const float x1 = dest->x, y1 = dest->y;
502 const float x2 = dest->x + dest->w, y2 = dest->y + dest->h;
503
504 const SDL_FRect uv = textureRegion(texture, src);
505
506 const float u1 = uv.x, v1 = uv.y;
507 const float u2 = uv.x + uv.w, v2 = uv.y + uv.h;
508
509 const MVC_Vertex verts[6] = {
510 { { { x1, y1 } }, { { u1, v1 } }, { 0 } },
511 { { { x2, y1 } }, { { u2, v1 } }, { 0 } },
512 { { { x1, y2 } }, { { u1, v2 } }, { 0 } },
513 { { { x2, y1 } }, { { u2, v1 } }, { 0 } },
514 { { { x2, y2 } }, { { u2, v2 } }, { 0 } },
515 { { { x1, y2 } }, { { u1, v2 } }, { 0 } },
516 };
517
518 $(self, pushDrawArrays, verts, 6, texture, color);
519}

◆ drawView()

static void drawView ( Renderer *  self,
View *  view 
)
static

Definition at line 525 of file Renderer.c.

525 {
526
527 assert(view);
528
529 const SDL_Rect clippingFrame = $(view, clippingFrame);
530 if (clippingFrame.w && clippingFrame.h) {
531
532 // The scissor enforces clipsSubviews, so it comes from the nearest clipping ancestor
533 // rather than from this View's own frame: siblings then share a scissor, which is one of
534 // the two conditions under which pushDrawArrays merges their draw calls.
535 const View *superview = view->superview;
536 while (superview && !superview->clipsSubviews) {
537 superview = superview->superview;
538 }
539
540 if (superview) {
541 const SDL_Rect scissor = $(superview, clippingFrame);
542 $(self, setClippingFrame, &scissor);
543 } else {
544 $(self, setClippingFrame, NULL);
545 }
546
547 $(view, render, self);
548 }
549}
static void render(View *self, Renderer *renderer)
Definition Control.c:181
static void setClippingFrame(Renderer *self, const SDL_Rect *clippingFrame)
Definition Renderer.c:841
static SDL_Rect clippingFrame(const View *self)
Definition View.c:606
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition View.h:225
bool clipsSubviews
If true, subviews will be clipped to this View's frame.
Definition View.h:309
View * superview
The super View.
Definition View.h:454

◆ endFrame()

static void endFrame ( Renderer *  self)
static

Definition at line 555 of file Renderer.c.

555 {
556
557 Framebuffer *framebuffer = self->framebuffer;
558 assert(framebuffer);
559
560 const SDL_GPUColorTargetInfo colorTarget = $(framebuffer, colorTargetInfo, 0, SDL_GPU_LOADOP_LOAD, SDL_GPU_STOREOP_STORE);
561
562 const size_t vertexCount = self->vertices->count;
563
564 CopyPass *copyPass = $(self->commands, beginCopyPass);
565
566 if (vertexCount > 0) {
567 const Uint32 vertexSize = (Uint32) (vertexCount * sizeof(MVC_Vertex));
568
569 if (vertexCount > self->vertexBufferCapacity) {
570 release(self->vertexBuffer);
571 self->vertexBuffer = $(self->device, createBuffer, &(SDL_GPUBufferCreateInfo) {
572 .usage = SDL_GPU_BUFFERUSAGE_VERTEX,
573 .size = vertexSize
574 });
575
576 release(self->transferBuffer);
577 self->transferBuffer = $(self->device, createTransferBuffer, &(SDL_GPUTransferBufferCreateInfo) {
578 .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
579 .size = vertexSize,
580 });
581
582 self->vertexBufferCapacity = (Uint32) vertexCount;
583 }
584
585 $(self->transferBuffer, write, self->vertices->elements, vertexSize, true);
586
587 $(copyPass, uploadBuffer,
588 &(SDL_GPUTransferBufferLocation) { .transfer_buffer = self->transferBuffer->buffer },
589 &(SDL_GPUBufferRegion) { .buffer = self->vertexBuffer->buffer, .size = vertexSize },
590 true);
591 }
592
593 release(copyPass);
594
595 RenderPass *renderPass = $(self->commands, beginRenderPass, &colorTarget, 1, NULL);
596
597 $(renderPass, setViewport, &(SDL_GPUViewport){
598 .x = 0.0f, .y = 0.0f,
599 .w = (float) framebuffer->size.w, .h = (float) framebuffer->size.h,
600 .min_depth = 0.0f, .max_depth = 1.0f,
601 });
602
603 int winW, winH;
604 SDL_GetWindowSize(self->device->window, &winW, &winH);
605 const mat4 projection = mat4_ortho(0.f, (float) winW, (float) winH, 0.f, -1.f, 1.f);
606 $(self->commands, pushVertexUniformData, 0, projection.f, sizeof(projection));
607
608 $(renderPass, bindPipeline, self->pipeline);
609
610 // the vertex buffer is created only once something has been drawn into the frame, so a
611 // frame that drew nothing has none to bind
612 if (vertexCount) {
613
614 $(renderPass, bindVertexBuffers, 0, &(SDL_GPUBufferBinding) { .buffer = self->vertexBuffer->buffer }, 1);
615
616 for (size_t i = 0; i < self->drawArrays->count; i++) {
617 const MVC_DrawArrays *draw = VectorElement(self->drawArrays, MVC_DrawArrays, i);
618
619 $(renderPass, setScissor, &draw->scissor);
620
621 $(renderPass, bindFragmentSamplers, 0, &(SDL_GPUTextureSamplerBinding) {
622 .texture = draw->texture->texture, .sampler = self->sampler->sampler,
623 }, 1);
624
625 $(renderPass, drawPrimitives, draw->vertexCount, 1, draw->firstVertex, 0);
626 }
627 }
628
629 release(renderPass);
630
631 self->commands = NULL;
632 self->framebuffer = NULL;
633}
static void draw(View *self, Renderer *renderer)
Definition View.c:778

◆ gradientColor()

static SDL_Color gradientColor ( float  x,
float  y,
const SDL_FRect *  bounds,
int  angle,
const SDL_Color *  from,
const SDL_Color *  to 
)
static

The color at x, y for a gradient of angle degrees across bounds.

angle is measured clockwise from south, so 0 fills downwards and 90 to the right. The interpolant is the point's projection onto the gradient axis, normalized by the extent of bounds along that axis, which makes the color an affine function of position: barycentric interpolation across the triangle then reproduces the gradient exactly, at any angle, from colors assigned only at the vertices.

Definition at line 170 of file Renderer.c.

171 {
172
173 const float radians = float_radians((float) angle);
174 const float dx = sinf(radians), dy = cosf(radians);
175
176 const float cx = bounds->x + bounds->w * 0.5f;
177 const float cy = bounds->y + bounds->h * 0.5f;
178
179 // the extent of the bounds along the axis, which is the box's support in that direction
180 const float extent = fabsf(bounds->w * dx) + fabsf(bounds->h * dy);
181
182 float t = 0.5f;
183 if (extent > 0.001f) {
184 t = 0.5f + ((x - cx) * dx + (y - cy) * dy) / extent;
185 t = t < 0.f ? 0.f : (t > 1.f ? 1.f : t);
186 }
187
188 return (SDL_Color) {
189 (Uint8) (from->r + (to->r - from->r) * t + 0.5f),
190 (Uint8) (from->g + (to->g - from->g) * t + 0.5f),
191 (Uint8) (from->b + (to->b - from->b) * t + 0.5f),
192 (Uint8) (from->a + (to->a - from->a) * t + 0.5f),
193 };
194}

◆ initialize()

static void initialize ( Class *  clazz)
static
See also
Class::initialize(Class *)

Definition at line 855 of file Renderer.c.

855 {
856
857 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
858
859 ((RendererInterface *) clazz->interface)->beginFrame = beginFrame;
860 ((RendererInterface *) clazz->interface)->beginFrameWith = beginFrameWith;
861 ((RendererInterface *) clazz->interface)->drawBevel = drawBevel;
862 ((RendererInterface *) clazz->interface)->drawLine = drawLine;
863 ((RendererInterface *) clazz->interface)->drawLines = drawLines;
864 ((RendererInterface *) clazz->interface)->drawPolygon = drawPolygon;
865 ((RendererInterface *) clazz->interface)->drawPolygonFilled = drawPolygonFilled;
866 ((RendererInterface *) clazz->interface)->drawRect = drawRect;
867 ((RendererInterface *) clazz->interface)->drawRectFilled = drawRectFilled;
868 ((RendererInterface *) clazz->interface)->drawRoundedRect = drawRoundedRect;
869 ((RendererInterface *) clazz->interface)->drawRoundedRectFilled = drawRoundedRectFilled;
870 ((RendererInterface *) clazz->interface)->drawRoundedRectGradientFilled = drawRoundedRectGradientFilled;
871 ((RendererInterface *) clazz->interface)->drawRoundedTexture = drawRoundedTexture;
872 ((RendererInterface *) clazz->interface)->drawRoundedTextureRegion = drawRoundedTextureRegion;
873 ((RendererInterface *) clazz->interface)->drawTexture = drawTexture;
874 ((RendererInterface *) clazz->interface)->drawTextureRegion = drawTextureRegion;
875 ((RendererInterface *) clazz->interface)->drawView = drawView;
876 ((RendererInterface *) clazz->interface)->endFrame = endFrame;
877 ((RendererInterface *) clazz->interface)->initWithDevice = initWithDevice;
878 ((RendererInterface *) clazz->interface)->pushDrawArrays = pushDrawArrays;
879 ((RendererInterface *) clazz->interface)->renderDeviceDidReset = renderDeviceDidReset;
880 ((RendererInterface *) clazz->interface)->renderDeviceWillReset = renderDeviceWillReset;
881 ((RendererInterface *) clazz->interface)->setClippingFrame = setClippingFrame;
882
883 $$(Resource, addResourceProvider, shaderResourceProvider);
884}
static void drawView(Renderer *self, View *view)
Definition Renderer.c:525
static Data * shaderResourceProvider(const char *name)
ResourceProvider for loading shaders from xxd headers.
Definition Renderer.c:48
static void drawTexture(const Renderer *self, Texture *texture, const SDL_FRect *dest, const SDL_Color *color)
Definition Renderer.c:489
static void drawRoundedRectGradientFilled(const Renderer *self, const SDL_Rect *rect, int radius, int angle, const SDL_Color *from, const SDL_Color *to)
Definition Renderer.c:441
static void drawPolygon(const Renderer *self, const SDL_Point *points, size_t count, int width, const SDL_Color *color)
Definition Renderer.c:293
static void beginFrame(Renderer *self)
Definition Renderer.c:101
static void drawRoundedRect(const Renderer *self, const SDL_Rect *rect, int radius, int width, const SDL_Color *color)
Definition Renderer.c:407
static void drawRectFilled(const Renderer *self, const SDL_Rect *rect, const SDL_Color *color)
Definition Renderer.c:384
static void drawRect(const Renderer *self, const SDL_Rect *rect, const SDL_Color *color)
Definition Renderer.c:365
static Renderer * initWithDevice(Renderer *self, RenderDevice *device)
Definition Renderer.c:639
static void drawRoundedRectFilled(const Renderer *self, const SDL_Rect *rect, int radius, const SDL_Color *color)
Definition Renderer.c:424
static void drawPolygonFilled(const Renderer *self, const SDL_Point *points, size_t count, int angle, const SDL_Color *from, const SDL_Color *to)
Definition Renderer.c:310
static void drawLine(const Renderer *self, const SDL_Point *points, const SDL_Color *color)
Definition Renderer.c:266
static void endFrame(Renderer *self)
Definition Renderer.c:555
static void drawBevel(const Renderer *self, const SDL_Rect *rect, int radius, int width, const SDL_Color *topLeft, const SDL_Color *bottomRight)
Definition Renderer.c:247
static void renderDeviceDidReset(Renderer *self)
Definition Renderer.c:705
static void drawRoundedTexture(const Renderer *self, Texture *texture, const SDL_FRect *dest, int radius, const SDL_Color *color)
Definition Renderer.c:465
static void renderDeviceWillReset(Renderer *self)
Definition Renderer.c:820
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:660

◆ initWithDevice()

static Renderer * initWithDevice ( Renderer *  self,
RenderDevice *  device 
)
static

Definition at line 639 of file Renderer.c.

639 {
640
641 self = (Renderer *) super(Object, self, init);
642 if (self) {
643 self->device = retain(device);
644 assert(self->device);
645
646 self->vertices = $(alloc(Vector), initWithSize, sizeof(MVC_Vertex));
647 assert(self->vertices);
648
649 self->drawArrays = $(alloc(Vector), initWithSize, sizeof(MVC_DrawArrays));
650 assert(self->drawArrays);
651 }
652
653 return self;
654}
static View * init(View *self)
Definition Box.c:67

◆ pushDrawArrays()

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

Definition at line 660 of file Renderer.c.

660 {
661
662 assert(verts);
663
664 const MVC_DrawArrays draw = {
665 .firstVertex = (Uint32) self->vertices->count,
666 .vertexCount = (Uint32) count,
667 .texture = texture ? texture : self->white,
668 .scissor = self->scissor,
669 };
670
671 Vector *vertices = self->vertices;
672
673 if (vertices->count + count > vertices->capacity) {
674 $(vertices, resize, max(vertices->capacity * 2, vertices->count + count));
675 }
676
677 MVC_Vertex *out = VectorElement(vertices, MVC_Vertex, vertices->count);
678 for (size_t i = 0; i < count; i++) {
679 out[i] = verts[i];
680 if (color) {
681 out[i].color = *color;
682 }
683 }
684
685 vertices->count += count;
686
687 // If this drawArrays is contiguous with the last one, combine them into a single call
688
689 if (self->drawArrays->count) {
690 MVC_DrawArrays *last = VectorElement(self->drawArrays, MVC_DrawArrays, self->drawArrays->count - 1);
691 if (last->texture == draw.texture && SDL_RectsEqual(&last->scissor, &draw.scissor)) {
692 last->vertexCount += draw.vertexCount;
693 } else {
694 $(self->drawArrays, add, (MVC_DrawArrays *) &draw);
695 }
696 } else {
697 $(self->drawArrays, add, (MVC_DrawArrays *) &draw);
698 }
699}
static void resize(View *self, const SDL_Size *size)
Definition View.c:1708

◆ renderDeviceDidReset()

static void renderDeviceDidReset ( Renderer *  self)
static

Definition at line 705 of file Renderer.c.

705 {
706
707 Shader *vertexShader = $(self->device, loadShader, "Renderer.vert", &(SDL_GPUShaderCreateInfo) {
708 .stage = SDL_GPU_SHADERSTAGE_VERTEX,
709 .num_uniform_buffers = 1,
710 });
711
712 Shader *fragmentShader = $(self->device, loadShader, "Renderer.frag", &(SDL_GPUShaderCreateInfo) {
713 .stage = SDL_GPU_SHADERSTAGE_FRAGMENT,
714 .num_samplers = 1,
715 });
716
717 const SDL_GPUVertexBufferDescription vertexBufferInfo = {
718 .slot = 0,
719 .pitch = sizeof(MVC_Vertex),
720 .input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX,
721 .instance_step_rate = 0,
722 };
723
724 const SDL_GPUColorTargetDescription colorTarget = {
725 .format = $(self->device, getSwapchainTextureFormat),
726 .blend_state = {
727 .enable_blend = true,
728 .color_blend_op = SDL_GPU_BLENDOP_ADD,
729 .alpha_blend_op = SDL_GPU_BLENDOP_ADD,
730 .src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA,
731 .dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
732 .src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE,
733 .dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
734 },
735 };
736
737 const SDL_GPUGraphicsPipelineCreateInfo pipelineInfo = {
738 .vertex_shader = vertexShader->shader,
739 .fragment_shader = fragmentShader->shader,
740 .vertex_input_state = {
741 .vertex_buffer_descriptions = &vertexBufferInfo,
742 .num_vertex_buffers = 1,
743 .vertex_attributes = (SDL_GPUVertexAttribute[]) {
744 {
745 .location = 0,
746 .buffer_slot = 0,
747 .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,
748 .offset = offsetof(MVC_Vertex, position)
749 },
750 {
751 .location = 1,
752 .buffer_slot = 0,
753 .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,
754 .offset = offsetof(MVC_Vertex, uv)
755 },
756 {
757 .location = 2,
758 .buffer_slot = 0,
759 .format = SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM,
760 .offset = offsetof(MVC_Vertex, color)
761 },
762 {
763 .location = 3,
764 .buffer_slot = 0,
765 .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4,
766 .offset = offsetof(MVC_Vertex, rect)
767 },
768 {
769 .location = 4,
770 .buffer_slot = 0,
771 .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,
772 .offset = offsetof(MVC_Vertex, shape)
773 },
774 },
775 .num_vertex_attributes = 5,
776 },
777 .primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
778 .rasterizer_state = {
779 .fill_mode = SDL_GPU_FILLMODE_FILL,
780 .cull_mode = SDL_GPU_CULLMODE_NONE,
781 .front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE,
782 },
783 .target_info = {
784 .color_target_descriptions = &colorTarget,
785 .num_color_targets = 1,
786 },
787 };
788
789 self->pipeline = $(self->device, createGraphicsPipeline, &pipelineInfo);
790
791 release(vertexShader);
792 release(fragmentShader);
793
794 self->sampler = $(self->device, createSampler, &(const SDL_GPUSamplerCreateInfo) {
795 .min_filter = SDL_GPU_FILTER_LINEAR,
796 .mag_filter = SDL_GPU_FILTER_LINEAR,
797 .mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR,
798 .address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
799 .address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
800 .address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
801 });
802
803 const Uint8 white[4] = { 255, 255, 255, 255 };
804
805 self->white = $(self->device, createTexture, &(const SDL_GPUTextureCreateInfo) {
806 .type = SDL_GPU_TEXTURETYPE_2D,
807 .format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM,
808 .usage = SDL_GPU_TEXTUREUSAGE_SAMPLER,
809 .width = 1,
810 .height = 1,
811 .layer_count_or_depth = 1,
812 .num_levels = 1,
813 }, white);
814}

◆ renderDeviceWillReset()

static void renderDeviceWillReset ( Renderer *  self)
static

Definition at line 820 of file Renderer.c.

820 {
821
822 self->commands = NULL;
823
824 self->white = release(self->white);
825 self->sampler = release(self->sampler);
826
827 self->vertexBuffer = release(self->vertexBuffer);
828 self->transferBuffer = release(self->transferBuffer);
829 self->vertexBufferCapacity = 0;
830
831 self->pipeline = release(self->pipeline);
832
833 $(self->vertices, removeAll);
834 $(self->drawArrays, removeAll);
835}

◆ roundedRectVertices()

static void roundedRectVertices ( MVC_Vertex *  verts,
const SDL_FRect *  rect,
int  radius,
int  stroke,
const SDL_FRect *  uv 
)
static

Fills six vertices covering rect as two triangles, tagged so the fragment shader shades them as a rounded rectangle of the given radius and stroke width.

The first triangle is the half above the bottom-left to top-right diagonal.

Definition at line 128 of file Renderer.c.

128 {
129
130 const float x1 = rect->x, y1 = rect->y;
131 const float x2 = rect->x + rect->w, y2 = rect->y + rect->h;
132
133 const float u1 = uv ? uv->x : 0.0f, v1 = uv ? uv->y : 0.0f;
134 const float u2 = uv ? uv->x + uv->w : 0.0f, v2 = uv ? uv->y + uv->h : 0.0f;
135
136 const vec4 shapeRect = { { (x1 + x2) * 0.5f, (y1 + y2) * 0.5f, rect->w * 0.5f, rect->h * 0.5f } };
137 const vec2 shape = { { (float) radius, (float) stroke } };
138
139 verts[0] = (MVC_Vertex) { { { x1, y1 } }, { { u1, v1 } }, { 0 }, shapeRect, shape };
140 verts[1] = (MVC_Vertex) { { { x2, y1 } }, { { u2, v1 } }, { 0 }, shapeRect, shape };
141 verts[2] = (MVC_Vertex) { { { x1, y2 } }, { { u1, v2 } }, { 0 }, shapeRect, shape };
142 verts[3] = (MVC_Vertex) { { { x2, y1 } }, { { u2, v1 } }, { 0 }, shapeRect, shape };
143 verts[4] = (MVC_Vertex) { { { x2, y2 } }, { { u2, v2 } }, { 0 }, shapeRect, shape };
144 verts[5] = (MVC_Vertex) { { { x1, y2 } }, { { u1, v2 } }, { 0 }, shapeRect, shape };
145}

◆ setClippingFrame()

static void setClippingFrame ( Renderer *  self,
const SDL_Rect *  clippingFrame 
)
static

Definition at line 841 of file Renderer.c.

841 {
842
843 if (clippingFrame) {
844 self->scissor = MVC_TransformToWindow(self->device->window, clippingFrame);
845 } else {
846 self->scissor = MakeRect(0, 0, self->framebuffer->size.w, self->framebuffer->size.h);
847 }
848}
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

◆ shaderResourceProvider()

static Data * shaderResourceProvider ( const char *  name)
static

ResourceProvider for loading shaders from xxd headers.

Definition at line 48 of file Renderer.c.

48 {
49
50 Data *data = NULL;
51
52 if (!strcmp("Renderer.frag.dxil", name)) {
53 data = $(alloc(Data), initWithConstMemory, Renderer_frag_dxil, Renderer_frag_dxil_len);
54 } else if (!strcmp("Renderer.frag.metal", name)) {
55 data = $(alloc(Data), initWithConstMemory, Renderer_frag_metal, Renderer_frag_metal_len);
56 } else if (!strcmp("Renderer.frag.spv", name)) {
57 data = $(alloc(Data), initWithConstMemory, Renderer_frag_spv, Renderer_frag_spv_len);
58 } else if (!strcmp("Renderer.vert.dxil", name)) {
59 data = $(alloc(Data), initWithConstMemory, Renderer_vert_dxil, Renderer_vert_dxil_len);
60 } else if (!strcmp("Renderer.vert.metal", name)) {
61 data = $(alloc(Data), initWithConstMemory, Renderer_vert_metal, Renderer_vert_metal_len);
62 } else if (!strcmp("Renderer.vert.spv", name)) {
63 data = $(alloc(Data), initWithConstMemory, Renderer_vert_spv, Renderer_vert_spv_len);
64 }
65
66 return data;
67}
static String * name(const Font *self)
Definition Font.c:303

◆ strokePolyline()

static void strokePolyline ( const Renderer *  self,
const SDL_Point *  points,
size_t  count,
bool  closed,
float  width,
const SDL_Color *  color 
)
static

Appends the quads stroking points, of width, optionally closing the loop.

Remarks
Each segment is stroked as its own quad, centered on the segment. The joints are therefore not mitered; at the one and two pixel widths borders actually use, the notch is smaller than a pixel.

Definition at line 202 of file Renderer.c.

203 {
204
205 const size_t segments = closed ? count : count - 1;
206
207 MVC_Vertex verts[16 * 6];
208 const size_t batchSize = lengthof(verts) / 6;
209
210 for (size_t s = 0; s < segments; ) {
211
212 const size_t batch = min(segments - s, batchSize);
213
214 for (size_t i = 0; i < batch; i++, s++) {
215
216 const SDL_Point *a = &points[s], *b = &points[(s + 1) % count];
217
218 const float ax = (float) a->x, ay = (float) a->y;
219 const float bx = (float) b->x, by = (float) b->y;
220
221 const float dx = bx - ax, dy = by - ay;
222 const float len = sqrtf(dx * dx + dy * dy);
223
224 float nx = 0.0f, ny = 0.0f;
225 if (len > 0.001f) {
226 nx = (-dy / len) * width * 0.5f;
227 ny = ( dx / len) * width * 0.5f;
228 }
229
230 MVC_Vertex *v = &verts[i * 6];
231 v[0] = (MVC_Vertex) { { { ax - nx, ay - ny } }, { { 0.0f, 0.0f } }, { 0 } };
232 v[1] = (MVC_Vertex) { { { ax + nx, ay + ny } }, { { 0.0f, 0.0f } }, { 0 } };
233 v[2] = (MVC_Vertex) { { { bx - nx, by - ny } }, { { 0.0f, 0.0f } }, { 0 } };
234 v[3] = (MVC_Vertex) { { { ax + nx, ay + ny } }, { { 0.0f, 0.0f } }, { 0 } };
235 v[4] = (MVC_Vertex) { { { bx + nx, by + ny } }, { { 0.0f, 0.0f } }, { 0 } };
236 v[5] = (MVC_Vertex) { { { bx - nx, by - ny } }, { { 0.0f, 0.0f } }, { 0 } };
237 }
238
239 $(self, pushDrawArrays, verts, batch * 6, NULL, color);
240 }
241}

◆ textureRegion()

static SDL_FRect textureRegion ( const Texture *  texture,
const SDL_Rect *  src 
)
static

Resolves the normalized texture coordinates of src within texture, or of the whole texture when src is NULL.

Definition at line 151 of file Renderer.c.

151 {
152
153 if (src == NULL || texture == NULL) {
154 return (SDL_FRect) { 0.0f, 0.0f, 1.0f, 1.0f };
155 }
156
157 const float w = (float) texture->size.w, h = (float) texture->size.h;
158
159 return (SDL_FRect) { src->x / w, src->y / h, src->w / w, src->h / h };
160}