diff options
Diffstat (limited to 'graphics.c')
-rwxr-xr-x | graphics.c | 749 |
1 files changed, 585 insertions, 164 deletions
@@ -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; } // ================================================================ |