summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xgraphics.c749
-rwxr-xr-xreduced_system_layer.c11
2 files changed, 591 insertions, 169 deletions
diff --git a/graphics.c b/graphics.c
index edb9707..f5e8af2 100755
--- a/graphics.c
+++ b/graphics.c
@@ -60,6 +60,26 @@ exit $STATUS # */
#define ANTIALIASING_SCALE 3
#endif
+#ifndef GRAPHICS_CACHE_DEPTH
+#define GRAPHICS_CACHE_DEPTH 4
+#endif
+
+#ifndef GRAPHICS_CACHE_SIZE
+#define GRAPHICS_CACHE_SIZE (128 * GRAPHICS_CACHE_DEPTH)
+#endif
+
+#ifndef GRAPHICS_FRAME_BUDGET
+#define GRAPHICS_FRAME_BUDGET (100.0)
+#endif
+
+#ifndef GRAPHICS_RENDER_COST
+#define GRAPHICS_RENDER_COST (0.1)
+#endif
+
+#ifndef GRAPHICS_DOWNSCALE_COST
+#define GRAPHICS_DOWNSCALE_COST (0.1)
+#endif
+
// ================================================================
//
// Vector math
@@ -89,26 +109,21 @@ typedef struct {
} Box;
enum {
- GRAPHICS_OP_NONE = 0,
- GRAPHICS_OP_REQUESTS,
- GRAPHICS_OP_PIXELS,
- GRAPHICS_OP_RECTANGLE,
- GRAPHICS_OP_TRIANGLE,
- GRAPHICS_OP_QUAD,
- GRAPHICS_OP_ELLIPSE,
- GRAPHICS_OP_LINE,
- GRAPHICS_OP_TEXT_AREA,
- GRAPHICS_OP_TEXT_CURSOR,
+ GRAPHICS_NOOP = 0,
+ GRAPHICS_DRAW_PIXELS,
+ GRAPHICS_FILL_RECTANGLE,
+ GRAPHICS_FILL_TRIANGLE,
+ GRAPHICS_FILL_QUAD,
+ GRAPHICS_FILL_ELLIPSE,
+ GRAPHICS_FILL_LINE,
+ GRAPHICS_DRAW_TEXT_AREA,
+ GRAPHICS_DRAW_TEXT_CURSOR,
};
typedef struct graphics_request_ {
u16 op;
union {
struct {
- i64 num;
- struct graphics_request_ *values;
- } requests;
- struct {
Box area;
Pixel_Buffer src;
} pixels;
@@ -153,6 +168,7 @@ typedef struct graphics_request_ {
} Graphics_Request;
typedef struct {
+ b8 disable_antialiasing;
vec2 scale;
Pixel_Buffer dst;
} Graphics_Context;
@@ -200,6 +216,9 @@ void fill_line (vec4_f32 color, vec2 vertices[2], f64 width);
void draw_text_area (vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text);
void draw_text_cursor(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection);
+void draw_pixels_quick (Box area, Pixel_Buffer src);
+void fill_rectangle_quick(vec4_f32 color, Box area);
+
void perform_graphics_request(Graphics_Context context, Graphics_Request req);
#endif // GRAPHICS_HEADER_GUARD_
@@ -240,6 +259,26 @@ static f64 max3_(f64 a, f64 b, f64 c) {
return c;
}
+static f64 min4_(f64 a, f64 b, f64 c, f64 d) {
+ if (a < b && a < c && a < d)
+ return a;
+ if (b < c && b < d)
+ return b;
+ if (c < d)
+ return c;
+ return d;
+}
+
+static f64 max4_(f64 a, f64 b, f64 c, f64 d) {
+ if (a > b && a > c && a > d)
+ return a;
+ if (b > c && b > d)
+ return b;
+ if (c > d)
+ return c;
+ return d;
+}
+
static b8 same_sign_(f64 a, f64 b) {
if (a >= EPSILON && b <= -EPSILON) return 0;
if (a <= -EPSILON && b >= EPSILON) return 0;
@@ -277,57 +316,30 @@ static void put_pixel_(Pixel_Buffer dst, vec4_f32 color, i64 x, i64 y) {
}
void draw_pixels_raw(Pixel_Buffer dst, Box area, Pixel_Buffer src) {
- f64 x1 = area.x + area.width;
- f64 y1 = area.y + area.height;
-
if (area.width < EPSILON || area.height < EPSILON)
return;
i64 i0 = (i64) floor(area.x);
- i64 i1 = (i64) floor(x1);
+ i64 i1 = (i64) floor(area.x + area.width);
i64 j0 = (i64) floor(area.y);
- i64 j1 = (i64) floor(y1);
+ i64 j1 = (i64) floor(area.y + area.height);
- if (i0 < 0) i0 = 0;
- if (i1 >= dst.width) i1 = dst.width - 1;
- if (j0 < 0) j0 = 0;
- if (j1 >= dst.height) j1 = dst.height - 1;
+ if (i0 < 0) i0 = 0;
+ if (i1 > dst.width) i1 = dst.width;
+ if (j0 < 0) j0 = 0;
+ if (j1 > dst.height) j1 = dst.height;
f64 w_inv = 1. / area.width;
f64 h_inv = 1. / area.height;
- for (i64 j = j0; j <= j1; ++j) {
- i64 src_j0 = (i64) floor(((j - area.y) * src.height) * h_inv + .5);
- i64 src_j1 = (i64) floor(((j + 1 - area.y) * src.height) * h_inv + .5);
- if (src_j0 == src_j1) src_j1 = src_j0 + 1;
- if (src_j1 <= 0 || src_j0 >= src.height) continue;
- if (src_j0 < 0) src_j0 = 0;
- if (src_j1 > src.height) src_j1 = src.height;
- for (i64 i = i0; i <= i1; ++i) {
- i64 src_i0 = (i64) floor(((i - area.x) * src.width) * w_inv + .5);
- i64 src_i1 = (i64) floor(((i + 1 - area.x) * src.width) * w_inv + .5);
- if (src_i0 == src_i1) src_i1 = src_i0 + 1;
- if (src_i1 <= 0 || src_i0 >= src.width) continue;
- if (src_i0 < 0) src_i0 = 0;
- if (src_i1 > src.width) src_i1 = src.width;
- vec4_f32 color = {0};
- i64 n = 0;
- for (i64 jj = src_j0; jj < src_j1; ++jj)
- for (i64 ii = src_i0; ii < src_i1; ++ii) {
- color.x += gamma_re_(src.pixels[jj * src.stride + ii].x);
- color.y += gamma_re_(src.pixels[jj * src.stride + ii].y);
- color.z += gamma_re_(src.pixels[jj * src.stride + ii].z);
- color.w += gamma_re_(src.pixels[jj * src.stride + ii].w);
- ++n;
- }
- if (n == 0)
- continue;
- f32 n_inv = 1.f / n;
- color.x = gamma_(color.x * n_inv);
- color.y = gamma_(color.y * n_inv);
- color.z = gamma_(color.z * n_inv);
- color.w = gamma_(color.w * n_inv);
- put_pixel_(dst, color, i, j);
+ for (i64 j = j0; j < j1; ++j) {
+ i64 src_j = (i64) floor(((j - area.y + .5) * src.height) * h_inv);
+ if (src_j < 0 || src_j >= src.height) continue;
+ i64 src_n = src_j * src.stride;
+ for (i64 i = i0; i < i1; ++i) {
+ i64 src_i = (i64) floor(((i - area.x + .5) * src.width) * w_inv);
+ if (src_i < 0 || src_i >= src.width) continue;
+ put_pixel_(dst, src.pixels[src_n + src_i], i, j);
}
}
}
@@ -472,7 +484,6 @@ void fill_quad_raw(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[4]) {
f64 x3 = vertices[3].x;
f64 y3 = vertices[3].y;
-
i64 i0 = (i64) floor(min3_(x0, x1, x2));
i64 j0 = (i64) floor(min3_(y0, y1, y2));
i64 i1 = (i64) ceil (max3_(x0, x1, x2));
@@ -543,10 +554,7 @@ void fill_ellipse_raw(Pixel_Buffer dst, vec4_f32 color, Box area) {
}
}
-void fill_line_raw(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[2], f64 width) {
- if (width < EPSILON)
- return;
-
+b8 quad_from_line_(vec2 *dst, vec2 vertices[2], f64 width) {
f64 x0 = vertices[0].x;
f64 y0 = vertices[0].y;
f64 x1 = vertices[1].x;
@@ -562,18 +570,24 @@ void fill_line_raw(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[2], f64 width
f64 tl = sqrt(tx * tx + ty * ty);
if (tl < EPSILON)
- return;
+ return 0;
f64 k = .5 / tl;
tx *= width * k;
ty *= width * k;
- fill_quad_raw(dst, color, (vec2[4]) {
- { x0 - tx, y0 - ty },
- { x0 + tx, y0 + ty },
- { x1 + tx, y1 + ty },
- { x1 - tx, y1 - ty },
- });
+ dst[0] = (vec2) { x0 - tx, y0 - ty };
+ dst[1] = (vec2) { x0 + tx, y0 + ty };
+ dst[2] = (vec2) { x1 + tx, y1 + ty };
+ dst[3] = (vec2) { x1 - tx, y1 - ty };
+ return 1;
+}
+
+void fill_line_raw(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[2], f64 width) {
+ if (width < EPSILON) return;
+ vec2 quad[4];
+ if (!quad_from_line_(quad, vertices, width)) return;
+ fill_quad_raw(dst, color, quad);
}
static i64 char_column_offset_(c32 c, i64 column_index) {
@@ -1231,7 +1245,7 @@ void draw_pixels(Box area, Pixel_Buffer src) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
- .op = GRAPHICS_OP_PIXELS,
+ .op = GRAPHICS_DRAW_PIXELS,
.pixels = {
.area = area,
.src = src,
@@ -1244,7 +1258,7 @@ void fill_rectangle(vec4_f32 color, Box area) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
- .op = GRAPHICS_OP_RECTANGLE,
+ .op = GRAPHICS_FILL_RECTANGLE,
.rectangle = {
.color = color,
.area = area,
@@ -1257,7 +1271,7 @@ void fill_triangle(vec4_f32 color, vec2 vertices[3]) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
- .op = GRAPHICS_OP_TRIANGLE,
+ .op = GRAPHICS_FILL_TRIANGLE,
.triangle = {
.color = color,
.vertices = {
@@ -1274,7 +1288,7 @@ void fill_quad(vec4_f32 color, vec2 vertices[4]) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
- .op = GRAPHICS_OP_QUAD,
+ .op = GRAPHICS_FILL_QUAD,
.quad = {
.color = color,
.vertices = {
@@ -1292,7 +1306,7 @@ void fill_ellipse(vec4_f32 color, Box area) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
- .op = GRAPHICS_OP_ELLIPSE,
+ .op = GRAPHICS_FILL_ELLIPSE,
.ellipse = {
.color = color,
.area = area,
@@ -1305,7 +1319,7 @@ void fill_line(vec4_f32 color, vec2 vertices[2], f64 width) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
- .op = GRAPHICS_OP_LINE,
+ .op = GRAPHICS_FILL_LINE,
.line = {
.color = color,
.vertices = { vertices[0], vertices[1], },
@@ -1319,7 +1333,7 @@ void draw_text_area(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
- .op = GRAPHICS_OP_TEXT_AREA,
+ .op = GRAPHICS_DRAW_TEXT_AREA,
.text_area = {
.color = color,
.area = area,
@@ -1335,7 +1349,7 @@ void draw_text_cursor(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c3
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
- .op = GRAPHICS_OP_TEXT_CURSOR,
+ .op = GRAPHICS_DRAW_TEXT_CURSOR,
.text_cursor = {
.color = color,
.area = area,
@@ -1349,116 +1363,151 @@ void draw_text_cursor(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c3
);
}
-// ================================================================
-
-void perform_raw_graphics_request_(Graphics_Context context, Graphics_Request req) {
- switch (req.op) {
- case GRAPHICS_OP_REQUESTS:
- for (i64 i = 0; i < req.requests.num; ++i)
- perform_raw_graphics_request_(context, req.requests.values[i]);
- break;
-
- case GRAPHICS_OP_PIXELS:
- draw_pixels_raw(
- context.dst,
- req.pixels.area,
- req.pixels.src
- );
- break;
+void draw_pixels_quick(Box area, Pixel_Buffer src) {
+ perform_graphics_request(
+ (Graphics_Context) { .disable_antialiasing = 1, },
+ (Graphics_Request) {
+ .op = GRAPHICS_DRAW_PIXELS,
+ .pixels = {
+ .area = area,
+ .src = src,
+ },
+ }
+ );
+}
- case GRAPHICS_OP_RECTANGLE:
- fill_rectangle_raw(
- context.dst,
- req.rectangle.color,
- req.rectangle.area
- );
- break;
+void fill_rectangle_quick(vec4_f32 color, Box area) {
+ perform_graphics_request(
+ (Graphics_Context) { .disable_antialiasing = 1, },
+ (Graphics_Request) {
+ .op = GRAPHICS_FILL_RECTANGLE,
+ .rectangle = {
+ .color = color,
+ .area = area,
+ },
+ }
+ );
+}
- case GRAPHICS_OP_TRIANGLE:
- fill_triangle_raw(
- context.dst,
- req.triangle.color,
- req.triangle.vertices
- );
- break;
+// ================================================================
- case GRAPHICS_OP_QUAD:
- fill_quad_raw(
- context.dst,
- req.quad.color,
- req.quad.vertices
- );
- break;
+typedef struct {
+ Graphics_Request req;
+ i64 x;
+ i64 y;
+ i64 width;
+ i64 height;
+} Graphics_Request_Norm_;
+
+typedef union {
+ u8 hash[BLAKE2B_OUTBYTES];
+ u64 index;
+} Graphics_Request_Id_;
- case GRAPHICS_OP_ELLIPSE:
- fill_ellipse_raw(
- context.dst,
- req.ellipse.color,
- req.ellipse.area
- );
- break;
+typedef struct {
+ b8 occupied;
+ b8 done_rendering;
+ b8 done_downscaling;
+ Graphics_Request_Id_ id;
+ Graphics_Request req;
+ i64 width;
+ i64 height;
+ i64 buffer_len;
+ vec4_f32 * buffer;
+ i64 upscaled_buffer_len;
+ vec4_f32 * upscaled_buffer;
+} Graphics_Cache_Slot_;
+
+static Graphics_Cache_Slot_ _graphics_cache[GRAPHICS_CACHE_SIZE] = {0};
+
+static i64 _graphics_cache_gc_index = 0;
+static f64 _graphics_rendering_budget = .0;
+
+static f64 _graphics_costs[] = {
+ [GRAPHICS_DRAW_PIXELS] = 2.,
+ [GRAPHICS_FILL_RECTANGLE] = 1.,
+ [GRAPHICS_FILL_TRIANGLE] = 2.,
+ [GRAPHICS_FILL_QUAD] = 2.,
+ [GRAPHICS_FILL_ELLIPSE] = 2.,
+ [GRAPHICS_FILL_LINE] = 3.,
+ [GRAPHICS_DRAW_TEXT_AREA] = 2.,
+ [GRAPHICS_DRAW_TEXT_CURSOR] = 2.,
+};
- case GRAPHICS_OP_LINE:
- fill_line_raw(
- context.dst,
- req.line.color,
- req.line.vertices,
- req.line.width
- );
+static void graphics_request_hash_(Blake2b_State *S, Graphics_Request req) {
+ switch (req.op) {
+ case GRAPHICS_DRAW_PIXELS:
+ blake2b_update(S, (u8 *) &req.pixels.area, sizeof req.pixels.area);
+ blake2b_update(S, (u8 *) &req.pixels.src.width, sizeof req.pixels.src.width);
+ blake2b_update(S, (u8 *) &req.pixels.src.height, sizeof req.pixels.src.height);
+ blake2b_update(S, (u8 *) &req.pixels.src.stride, sizeof req.pixels.src.stride);
+ for (i64 j = 0; j < req.pixels.src.height; ++j)
+ blake2b_update(
+ S,
+ (u8 *) (req.pixels.src.pixels + j * req.pixels.src.stride),
+ req.pixels.src.width * sizeof *req.pixels.src.pixels
+ );
break;
- case GRAPHICS_OP_TEXT_AREA:
- draw_text_area_raw(
- context.dst,
- req.text_area.color,
- req.text_area.area,
- req.text_area.max_size,
- req.text_area.num_chars,
- req.text_area.text
+ case GRAPHICS_DRAW_TEXT_AREA:
+ blake2b_update(S, (u8 *) &req.text_area.color, sizeof req.text_area.color);
+ blake2b_update(S, (u8 *) &req.text_area.area, sizeof req.text_area.area);
+ blake2b_update(S, (u8 *) &req.text_area.max_size, sizeof req.text_area.max_size);
+ blake2b_update(S, (u8 *) &req.text_area.num_chars, sizeof req.text_area.num_chars);
+ blake2b_update(
+ S,
+ (u8 *) req.text_area.text,
+ req.text_area.num_chars * sizeof *req.text_area.text
);
break;
- case GRAPHICS_OP_TEXT_CURSOR:
- draw_text_cursor_raw(
- context.dst,
- req.text_cursor.color,
- req.text_cursor.area,
- req.text_cursor.max_size,
- req.text_cursor.num_chars,
- req.text_cursor.text,
- req.text_cursor.cursor,
- req.text_cursor.selection
+ case GRAPHICS_DRAW_TEXT_CURSOR:
+ blake2b_update(S, (u8 *) &req.text_cursor.color, sizeof req.text_cursor.color);
+ blake2b_update(S, (u8 *) &req.text_cursor.area, sizeof req.text_cursor.area);
+ blake2b_update(S, (u8 *) &req.text_cursor.max_size, sizeof req.text_cursor.max_size);
+ blake2b_update(S, (u8 *) &req.text_cursor.num_chars, sizeof req.text_cursor.num_chars);
+ blake2b_update(S, (u8 *) req.text_cursor.text, req.text_cursor.num_chars * sizeof *req.text_cursor.text);
+ blake2b_update(S, (u8 *) &req.text_cursor.cursor, sizeof req.text_cursor.cursor);
+ blake2b_update(
+ S,
+ (u8 *) &req.text_cursor.selection,
+ sizeof req.text_cursor.selection
);
break;
- default:;
+ default:
+ blake2b_update(S, (u8 *) &req, sizeof req);
}
}
-Graphics_Request graphics_request_scaled_(Graphics_Request req, vec2 scale) {
+static Graphics_Request_Id_ graphics_request_id_(Graphics_Request req) {
+ Blake2b_State S[1];
+ blake2b_init(S, BLAKE2B_OUTBYTES);
+ graphics_request_hash_(S, req);
+ Graphics_Request_Id_ id = {0};
+ blake2b_final(S, id.hash, BLAKE2B_OUTBYTES);
+ return id;
+}
+
+static Graphics_Request graphics_request_scaled_(Graphics_Request req, vec2 scale) {
Graphics_Request scaled = req;
switch (req.op) {
- case GRAPHICS_OP_REQUESTS:
- for (i64 i = 0; i < req.requests.num; ++i)
- scaled.requests.values[i] = graphics_request_scaled_(req.requests.values[i], scale);
- break;
-
- case GRAPHICS_OP_PIXELS:
+ case GRAPHICS_DRAW_PIXELS:
scaled.pixels.area.x *= scale.x;
scaled.pixels.area.y *= scale.y;
scaled.pixels.area.width *= scale.x;
scaled.pixels.area.height *= scale.y;
break;
- case GRAPHICS_OP_RECTANGLE:
+ case GRAPHICS_FILL_RECTANGLE:
scaled.rectangle.area.x *= scale.x;
scaled.rectangle.area.y *= scale.y;
scaled.rectangle.area.width *= scale.x;
scaled.rectangle.area.height *= scale.y;
break;
- case GRAPHICS_OP_TRIANGLE:
+ case GRAPHICS_FILL_TRIANGLE:
scaled.triangle.vertices[0].x *= scale.x;
scaled.triangle.vertices[0].y *= scale.y;
scaled.triangle.vertices[1].x *= scale.x;
@@ -1467,7 +1516,7 @@ Graphics_Request graphics_request_scaled_(Graphics_Request req, vec2 scale) {
scaled.triangle.vertices[2].y *= scale.y;
break;
- case GRAPHICS_OP_QUAD:
+ case GRAPHICS_FILL_QUAD:
scaled.quad.vertices[0].x *= scale.x;
scaled.quad.vertices[0].y *= scale.y;
scaled.quad.vertices[1].x *= scale.x;
@@ -1478,14 +1527,14 @@ Graphics_Request graphics_request_scaled_(Graphics_Request req, vec2 scale) {
scaled.quad.vertices[3].y *= scale.y;
break;
- case GRAPHICS_OP_ELLIPSE:
+ case GRAPHICS_FILL_ELLIPSE:
scaled.ellipse.area.x *= scale.x;
scaled.ellipse.area.y *= scale.y;
scaled.ellipse.area.width *= scale.x;
scaled.ellipse.area.height *= scale.y;
break;
- case GRAPHICS_OP_LINE:
+ case GRAPHICS_FILL_LINE:
scaled.line.vertices[0].x *= scale.x;
scaled.line.vertices[0].y *= scale.y;
scaled.line.vertices[1].x *= scale.x;
@@ -1508,7 +1557,7 @@ Graphics_Request graphics_request_scaled_(Graphics_Request req, vec2 scale) {
break;
- case GRAPHICS_OP_TEXT_AREA:
+ case GRAPHICS_DRAW_TEXT_AREA:
scaled.text_area.area.x *= scale.x;
scaled.text_area.area.y *= scale.y;
scaled.text_area.area.width *= scale.x;
@@ -1517,7 +1566,7 @@ Graphics_Request graphics_request_scaled_(Graphics_Request req, vec2 scale) {
scaled.text_area.max_size.y *= scale.y;
break;
- case GRAPHICS_OP_TEXT_CURSOR:
+ case GRAPHICS_DRAW_TEXT_CURSOR:
scaled.text_cursor.area.x *= scale.x;
scaled.text_cursor.area.y *= scale.y;
scaled.text_cursor.area.width *= scale.x;
@@ -1532,13 +1581,363 @@ Graphics_Request graphics_request_scaled_(Graphics_Request req, vec2 scale) {
return scaled;
}
-void scale_and_perform_graphics_request_(Graphics_Context context, Graphics_Request req) {
- perform_raw_graphics_request_(context, graphics_request_scaled_(req, context.scale));
+static Graphics_Request graphics_request_moved_(Graphics_Request req, vec2 offset) {
+ Graphics_Request moved = req;
+
+ switch (req.op) {
+ case GRAPHICS_DRAW_PIXELS:
+ moved.pixels.area.x += offset.x;
+ moved.pixels.area.y += offset.y;
+ break;
+
+ case GRAPHICS_FILL_RECTANGLE:
+ moved.rectangle.area.x += offset.x;
+ moved.rectangle.area.y += offset.y;
+ break;
+
+ case GRAPHICS_FILL_TRIANGLE:
+ moved.triangle.vertices[0].x += offset.x;
+ moved.triangle.vertices[0].y += offset.y;
+ moved.triangle.vertices[1].x += offset.x;
+ moved.triangle.vertices[1].y += offset.y;
+ moved.triangle.vertices[2].x += offset.x;
+ moved.triangle.vertices[2].y += offset.y;
+ break;
+
+ case GRAPHICS_FILL_QUAD:
+ moved.quad.vertices[0].x += offset.x;
+ moved.quad.vertices[0].y += offset.y;
+ moved.quad.vertices[1].x += offset.x;
+ moved.quad.vertices[1].y += offset.y;
+ moved.quad.vertices[2].x += offset.x;
+ moved.quad.vertices[2].y += offset.y;
+ moved.quad.vertices[3].x += offset.x;
+ moved.quad.vertices[3].y += offset.y;
+ break;
+
+ case GRAPHICS_FILL_ELLIPSE:
+ moved.ellipse.area.x += offset.x;
+ moved.ellipse.area.y += offset.y;
+ break;
+
+ case GRAPHICS_FILL_LINE:
+ moved.line.vertices[0].x += offset.x;
+ moved.line.vertices[0].y += offset.y;
+ moved.line.vertices[1].x += offset.x;
+ moved.line.vertices[1].y += offset.y;
+ break;
+
+ case GRAPHICS_DRAW_TEXT_AREA:
+ moved.text_area.area.x += offset.x;
+ moved.text_area.area.y += offset.y;
+ break;
+
+ case GRAPHICS_DRAW_TEXT_CURSOR:
+ moved.text_cursor.area.x += offset.x;
+ moved.text_cursor.area.y += offset.y;
+ break;
+
+ default:;
+ }
+
+ return moved;
}
-// ================================================================
+static Box graphics_request_area_(Graphics_Request req) {
+ Box area = {0};
-void perform_graphics_request(Graphics_Context context, Graphics_Request req) {
+ switch (req.op) {
+ case GRAPHICS_DRAW_PIXELS: area = req.pixels .area; break;
+ case GRAPHICS_FILL_RECTANGLE: area = req.rectangle .area; break;
+ case GRAPHICS_FILL_ELLIPSE: area = req.ellipse .area; break;
+ case GRAPHICS_DRAW_TEXT_AREA: area = req.text_area .area; break;
+ case GRAPHICS_DRAW_TEXT_CURSOR: area = req.text_cursor.area; break;
+
+ case GRAPHICS_FILL_TRIANGLE: {
+ vec2 *v = req.triangle.vertices;
+ area.x = min3_(v[0].x, v[1].x, v[2].x);
+ area.y = min3_(v[0].y, v[1].y, v[2].y);
+ area.width = max3_(v[0].x, v[1].x, v[2].x) - area.x;
+ area.height = max3_(v[0].y, v[1].y, v[2].y) - area.y;
+ } break;
+
+ case GRAPHICS_FILL_QUAD:
+ case GRAPHICS_FILL_LINE: {
+ vec2 quad[4] = {0};
+
+ if (req.op == GRAPHICS_FILL_LINE)
+ quad_from_line_(quad, req.line.vertices, req.line.width);
+ else {
+ quad[0] = req.quad.vertices[0];
+ quad[1] = req.quad.vertices[1];
+ quad[2] = req.quad.vertices[2];
+ quad[3] = req.quad.vertices[3];
+ }
+
+ area.x = min4_(quad[0].x, quad[1].x, quad[2].x, quad[3].x);
+ area.y = min4_(quad[0].y, quad[1].y, quad[2].y, quad[3].y);
+ area.width = max4_(quad[0].x, quad[1].x, quad[2].x, quad[3].x) - area.x;
+ area.height = max4_(quad[0].y, quad[1].y, quad[2].y, quad[3].y) - area.y;
+ } break;
+
+ default:;
+ }
+
+ return area;
+}
+
+static Graphics_Request_Norm_ normalize_graphics_request_(Graphics_Request req) {
+ Box area = graphics_request_area_(req);
+ i64 x = (i64) floor(area.x - .5);
+ i64 y = (i64) floor(area.y - .5);
+ i64 w = (i64) ceil (area.x + area.width + .5) - x;
+ i64 h = (i64) ceil (area.y + area.height + .5) - y;
+
+ return (Graphics_Request_Norm_) {
+ .req = graphics_request_moved_(req, (vec2) { -x, -y }),
+ .x = x,
+ .y = y,
+ .width = w,
+ .height = h,
+ };
+}
+
+static void perform_raw_graphics_request_(Pixel_Buffer dst, Graphics_Request req) {
+ switch (req.op) {
+ case GRAPHICS_DRAW_PIXELS:
+ draw_pixels_raw(
+ dst,
+ req.pixels.area,
+ req.pixels.src
+ );
+ break;
+
+ case GRAPHICS_FILL_RECTANGLE:
+ fill_rectangle_raw(
+ dst,
+ req.rectangle.color,
+ req.rectangle.area
+ );
+ break;
+
+ case GRAPHICS_FILL_TRIANGLE:
+ fill_triangle_raw(
+ dst,
+ req.triangle.color,
+ req.triangle.vertices
+ );
+ break;
+
+ case GRAPHICS_FILL_QUAD:
+ fill_quad_raw(
+ dst,
+ req.quad.color,
+ req.quad.vertices
+ );
+ break;
+
+ case GRAPHICS_FILL_ELLIPSE:
+ fill_ellipse_raw(
+ dst,
+ req.ellipse.color,
+ req.ellipse.area
+ );
+ break;
+
+ case GRAPHICS_FILL_LINE:
+ fill_line_raw(
+ dst,
+ req.line.color,
+ req.line.vertices,
+ req.line.width
+ );
+ break;
+
+ case GRAPHICS_DRAW_TEXT_AREA:
+ draw_text_area_raw(
+ dst,
+ req.text_area.color,
+ req.text_area.area,
+ req.text_area.max_size,
+ req.text_area.num_chars,
+ req.text_area.text
+ );
+ break;
+
+ case GRAPHICS_DRAW_TEXT_CURSOR:
+ draw_text_cursor_raw(
+ dst,
+ req.text_cursor.color,
+ req.text_cursor.area,
+ req.text_cursor.max_size,
+ req.text_cursor.num_chars,
+ req.text_cursor.text,
+ req.text_cursor.cursor,
+ req.text_cursor.selection
+ );
+ break;
+
+ default:;
+ }
+}
+
+static b8 mem_eq_(i64 size, void *x, void *y) {
+ for (i64 i = 0; i < size; ++i)
+ if (((u8 *) x)[i] != ((u8 *) y)[i])
+ return 0;
+ return 1;
+}
+
+static void copy_pixels_quick_(Pixel_Buffer dst, i64 x, i64 y, Pixel_Buffer src) {
+ for (i64 j = 0; j < src.height; ++j) {
+ i64 src_n = j * src.stride;
+ i64 src_n1 = src_n + src.width;
+ i64 dst_n = (y + j) * dst.stride + x;
+
+ while (src_n < src_n1) {
+ vec4_f32 *d = dst.pixels + dst_n;
+ vec4_f32 *s = src.pixels + src_n;
+
+ d->x = d->x * (1.f - s->w) + s->x * s->w;
+ d->y = d->y * (1.f - s->w) + s->y * s->w;
+ d->z = d->z * (1.f - s->w) + s->z * s->w;
+
+ ++dst_n;
+ ++src_n;
+ }
+ }
+}
+
+static void downscale_pixels_(vec4_f32 *dst, i64 width, i64 height, vec4_f32 *src) {
+ f32 k = 1.f / (ANTIALIASING_SCALE * ANTIALIASING_SCALE);
+ for (i64 j = 0; j < height; ++j) {
+ for (i64 i = 0; i < width; ++i) {
+ vec4_f32 color = {0};
+ for (i64 jj = 0; jj < ANTIALIASING_SCALE; ++jj)
+ for (i64 ii = 0; ii < ANTIALIASING_SCALE; ++ii) {
+ i64 n = (j * ANTIALIASING_SCALE + jj) * width + (i * ANTIALIASING_SCALE + ii);
+ color.x += gamma_re_(src[n].x);
+ color.y += gamma_re_(src[n].y);
+ color.z += gamma_re_(src[n].z);
+ color.w += gamma_re_(src[n].w);
+ }
+ dst[j * width + i].x = gamma_(color.x * k);
+ dst[j * width + i].y = gamma_(color.y * k);
+ dst[j * width + i].z = gamma_(color.z * k);
+ dst[j * width + i].w = gamma_(color.w * k);
+ }
+ }
+}
+
+static void perform_graphics_request_cached_(Pixel_Buffer dst, Graphics_Request req) {
+ Graphics_Request_Norm_ norm = normalize_graphics_request_(req);
+ Graphics_Request_Id_ id = graphics_request_id_(norm.req);
+
+ b8 reset_slot = 1;
+ i64 slot = -1;
+
+ i64 n = (i64) (id.index % (GRAPHICS_CACHE_SIZE / GRAPHICS_CACHE_DEPTH)) * GRAPHICS_CACHE_DEPTH;
+
+ for (i64 i = 0; i < GRAPHICS_CACHE_DEPTH; ++i) {
+ if (!_graphics_cache[n + i].occupied)
+ continue;
+ if (!mem_eq_(BLAKE2B_OUTBYTES, _graphics_cache[n + i].id.hash, id.hash))
+ continue;
+ if (!mem_eq_(sizeof req, &_graphics_cache[n + i].req, &norm.req))
+ continue;
+ reset_slot = 0;
+ slot = n + i;
+ break;
+ }
+
+ if (slot == -1)
+ for (i64 i = 0; i < GRAPHICS_CACHE_DEPTH; ++i)
+ if (!_graphics_cache[n + i].occupied) {
+ slot = n + i;
+ break;
+ }
+
+ if (slot == -1) {
+ LOG_ERROR("Graphics cache collision.");
+ slot = n;
+ }
+
+ Graphics_Cache_Slot_ *s = _graphics_cache + slot;
+
+ i64 num_pixels = norm.width * norm.height;
+ i64 upscaled_len = num_pixels * (ANTIALIASING_SCALE * ANTIALIASING_SCALE);
+
+
+ if (reset_slot) {
+ s->occupied = 1;
+ s->done_rendering = 0;
+ s->done_downscaling = 0;
+ s->id = id;
+ s->req = norm.req;
+ s->width = norm.width;
+ s->height = norm.height;
+
+ if (num_pixels > s->buffer_len)
+ resize_dynamic_array_exact(&s->buffer_len, (void **) &s->buffer, sizeof *s->buffer, num_pixels);
+
+ if (upscaled_len > s->upscaled_buffer_len)
+ resize_dynamic_array_exact(&s->upscaled_buffer_len, (void **) &s->upscaled_buffer, sizeof *s->upscaled_buffer, upscaled_len);
+
+ if (s->buffer_len < num_pixels || s->upscaled_buffer_len < upscaled_len)
+ // Out of memory
+ s->occupied = 0;
+ }
+
+ if (_graphics_rendering_budget > 0 && !s->done_rendering) {
+ if (s->upscaled_buffer_len >= upscaled_len) {
+ Pixel_Buffer dst = {
+ .width = s->width * ANTIALIASING_SCALE,
+ .height = s->height * ANTIALIASING_SCALE,
+ .stride = s->width * ANTIALIASING_SCALE,
+ .pixels = s->upscaled_buffer,
+ };
+ Graphics_Request upscaled = graphics_request_scaled_(s->req, (vec2) { ANTIALIASING_SCALE, ANTIALIASING_SCALE });
+ perform_raw_graphics_request_(dst, upscaled);
+ s->done_rendering = 1;
+ _graphics_rendering_budget -= num_pixels * GRAPHICS_RENDER_COST * _graphics_costs[req.op];
+ } else {
+ LOG_ERROR("Sanity");
+ s->occupied = 0;
+ }
+ }
+
+ if (_graphics_rendering_budget > 0 && s->done_rendering && !s->done_downscaling) {
+ if (s->buffer_len >= num_pixels && s->upscaled_buffer_len >= upscaled_len) {
+ downscale_pixels_(s->buffer, s->width, s->height, s->upscaled_buffer);
+ s->done_downscaling = 1;
+ _graphics_rendering_budget -= num_pixels * GRAPHICS_DOWNSCALE_COST;
+ } else {
+ LOG_ERROR("Sanity");
+ s->occupied = 0;
+ }
+ }
+
+ if (s->done_downscaling)
+ copy_pixels_quick_(dst, norm.x, norm.y, (Pixel_Buffer) {
+ .width = s->width,
+ .height = s->height,
+ .stride = s->width,
+ .pixels = s->buffer,
+ });
+ else
+ perform_raw_graphics_request_(dst, req);
+}
+
+static void scale_and_perform_graphics_request_(Graphics_Context context, Graphics_Request req) {
+ Graphics_Request scaled = graphics_request_scaled_(req, context.scale);
+
+ if (context.disable_antialiasing)
+ perform_raw_graphics_request_(context.dst, scaled);
+ else
+ perform_graphics_request_cached_(context.dst, scaled);
+}
+
+static Graphics_Context graphics_context_defaults_(Graphics_Context context) {
if (context.dst.pixels == NULL) {
context.dst.width = g_platform.frame_width;
context.dst.height = g_platform.frame_height;
@@ -1552,7 +1951,29 @@ void perform_graphics_request(Graphics_Context context, Graphics_Request req) {
};
}
- scale_and_perform_graphics_request_(context, req);
+ return context;
+}
+
+// ================================================================
+
+void perform_graphics_request(Graphics_Context context, Graphics_Request req) {
+ scale_and_perform_graphics_request_(graphics_context_defaults_(context), req);
+}
+
+void graphics_refresh_frame(void) {
+ _graphics_rendering_budget = GRAPHICS_FRAME_BUDGET;
+
+ Graphics_Cache_Slot_ *s = _graphics_cache + _graphics_cache_gc_index;
+
+ i64 required_len = !s->occupied ? 0 : s->width * s->height;
+ i64 required_upscaled_len = !s->occupied ? 0 : required_len * (ANTIALIASING_SCALE * ANTIALIASING_SCALE);
+
+ if (s->buffer_len > required_len)
+ resize_dynamic_array_exact(&s->buffer_len, (void **) &s->buffer, sizeof *s->buffer, required_len);
+ if (s->upscaled_buffer_len > required_upscaled_len)
+ resize_dynamic_array_exact(&s->upscaled_buffer_len, (void **) &s->upscaled_buffer, sizeof *s->buffer, required_upscaled_len);
+
+ _graphics_cache_gc_index = (_graphics_cache_gc_index + 1) % GRAPHICS_CACHE_SIZE;
}
// ================================================================
diff --git a/reduced_system_layer.c b/reduced_system_layer.c
index 461155e..ad5e990 100755
--- a/reduced_system_layer.c
+++ b/reduced_system_layer.c
@@ -51,8 +51,7 @@
#/ + [x] Graphics requests
#/ + [x] Memory buffer allocator
#/ + [x] Blake2 hash
-#/ + [ ] Requests cache
-#/ + [ ] Incremental rendering
+#/ + [x] Requests cache
#/ - Examples
#/ - Conway's Game of Life
#/ - Julia Set
@@ -68,8 +67,8 @@
#/ - Improve microbenchmarks library
#/ - System
#/ - Window
-#/ - Wayland
#/ - Windows graphics
+#/ - Wayland
#/ - Sound
#/ - Windows audio
#/ - Recording
@@ -3745,6 +3744,10 @@ i64 network_send(u16 slot, IP_Address address, i64 size, u8 *data, u16 *local_po
i64 current_utc_time_in_milliseconds(void);
+void yield_thread_execution(void) {
+ // Do nothing
+}
+
void suspend_thread_for_milliseconds(i64 duration) {
if (duration <= 0)
return;
@@ -4087,7 +4090,5 @@ i32 main(i32 argc, c8 **argv) {
// ================================================================
-#undef LOG_ERROR
-
#endif // REDUCED_SYSTEM_LAYER_IMPL_GUARD_
#endif // REDUCED_SYSTEM_LAYER_HEADER