diff options
author | Mitya Selivanov <automainint@guattari.tech> | 2025-02-13 13:13:03 +0100 |
---|---|---|
committer | Mitya Selivanov <automainint@guattari.tech> | 2025-02-13 13:13:03 +0100 |
commit | cf25a8b0c9cb58baf8dccfe353a733777071fe4e (patch) | |
tree | 7a788e66505aa7b4b8544bd2ec12f5399472a062 | |
parent | b187d63d434e5fa5f18ab4b09db7358e36cb58f5 (diff) | |
download | reduced_system_layer-cf25a8b0c9cb58baf8dccfe353a733777071fe4e.zip |
Global anti-aliasing
-rwxr-xr-x | graphics.c | 285 | ||||
-rwxr-xr-x | reduced_system_layer.c | 323 |
2 files changed, 270 insertions, 338 deletions
@@ -56,10 +56,6 @@ exit $STATUS # */ #define EPSILON (1e-9) #endif -#ifndef ANTIALIASING_SCALE -#define ANTIALIASING_SCALE 3 -#endif - #ifndef GRAPHICS_CACHE_DEPTH #define GRAPHICS_CACHE_DEPTH 4 #endif @@ -68,18 +64,6 @@ exit $STATUS # */ #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 @@ -298,32 +282,17 @@ static b8 same_sign_(f64 a, f64 b) { return 1; } -static f64 gamma_(f64 x) { - if (x >= 0.0031308) - return 1.055 * pow(x, 1.0 / 2.4) - 0.055; - return 12.92 * x; -} - -static f64 gamma_re_(f64 x) { - if (x >= 0.04045) - return pow((x + 0.055) / 1.055, 2.4); - return x / 12.92; -} - -static void put_pixel_(Pixel_Buffer dst, vec4_f32 color, i64 x, i64 y) { - i64 n = y * dst.stride + x; - +static void put_pixel_(vec4_f32 *dst, vec4_f32 color) { if (color.w == 1.f) - dst.pixels[n] = color; + *dst = color; else { - vec4_f32 dst_color = dst.pixels[n]; - f32 dst_amount = 1. - color.w; - - dst.pixels[n] = (vec4_f32) { - .x = dst_color.x * dst_amount + color.x * color.w, - .y = dst_color.y * dst_amount + color.y * color.w, - .z = dst_color.z * dst_amount + color.z * color.w, - .w = max2_f32_(dst_color.w, color.w), + f32 dst_amount = 1. - color.w; + + *dst = (vec4_f32) { + .x = dst->x * dst_amount + color.x * color.w, + .y = dst->y * dst_amount + color.y * color.w, + .z = dst->z * dst_amount + color.z * color.w, + .w = max2_f32_(dst->w, color.w), }; } } @@ -342,43 +311,28 @@ void draw_pixels_to_buffer(Pixel_Buffer dst, Box area, Pixel_Buffer src) { if (j0 < 0) j0 = 0; if (j1 > dst.height) j1 = dst.height; - f64 w_inv = 1. / area.width; - f64 h_inv = 1. / area.height; + f64 w_coeff = src.width / area.width; + f64 h_coeff = src.height / area.height; for (i64 j = j0; j < j1; ++j) { - i64 src_j = (i64) floor(((j - area.y + .5) * src.height) * h_inv); + i64 src_j = (i64) floor((j - area.y + .5) * h_coeff); if (src_j < 0 || src_j >= src.height) continue; vec4_f32 *q = src.pixels + src_j * src.stride; vec4_f32 *p = dst.pixels + j * dst.stride + i0; for (i64 i = i0; i < i1; ++i) { - i64 src_i = (i64) floor(((i - area.x + .5) * src.width) * w_inv); + i64 src_i = (i64) floor((i - area.x + .5) * w_coeff); if (src_i < 0 || src_i >= src.width) continue; - vec4_f32 color = q[src_i]; - if (color.w == 1.f) - *p = color; - else { - vec4_f32 dst_color = *p; - f32 dst_amount = 1. - color.w; - *p = (vec4_f32) { - .x = dst_color.x * dst_amount + color.x * color.w, - .y = dst_color.y * dst_amount + color.y * color.w, - .z = dst_color.z * dst_amount + color.z * color.w, - .w = max2_f32_(dst_color.w, color.w), - }; - } + put_pixel_(p, q[src_i]); ++p; } } } void fill_rectangle_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area) { - f64 x1 = area.x + area.width; - f64 y1 = area.y + area.height; - - i64 i0 = (i64) ceil (area.x); - i64 i1 = (i64) floor(x1); - i64 j0 = (i64) ceil (area.y); - i64 j1 = (i64) floor(y1); + i64 i0 = (i64) floor(area.x); + i64 i1 = (i64) floor(area.x + area.width); + i64 j0 = (i64) floor(area.y); + i64 j1 = (i64) floor(area.y + area.height); i64 i00 = i0; i64 j00 = j0; @@ -390,73 +344,11 @@ void fill_rectangle_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area) { if (j0 < 0) j0 = 0; if (j1 > dst.height) j1 = dst.height; - f64 kx0 = i00 - area.x; - f64 kx1 = x1 - i10; - f64 ky0 = j00 - area.y; - f64 ky1 = y1 - j10; - - for (i64 j = j0; j < j1; ++j) - for (i64 i = i0; i < i1; ++i) - put_pixel_(dst, color, i, j); - - if (i00 > i10) { - kx0 *= kx1; - kx1 = 0.; - } - - if (j00 > j10) { - ky0 *= ky1; - ky1 = 0.; - } - - f64 alpha = color.w; - - if (i00 - 1 >= 0 && i00 - 1 < dst.width) { - color.w = gamma_(gamma_re_(alpha) * kx0); - for (i64 j = j0; j < j1; ++j) - put_pixel_(dst, color, i00 - 1, j); - } - - if (i10 >= 0 && i10 < dst.width) { - color.w = gamma_(gamma_re_(alpha) * kx1); - for (i64 j = j0; j < j1; ++j) - put_pixel_(dst, color, i10, j); - } - - if (j00 - 1 >= 0 && j00 - 1 < dst.height) { - color.w = gamma_(gamma_re_(alpha) * ky0); - for (i64 i = i0; i < i1; ++i) - put_pixel_(dst, color, i, j00 - 1); - } - - if (j10 >= 0 && j10 < dst.height) { - color.w = gamma_(gamma_re_(alpha) * ky1); - for (i64 i = i0; i < i1; ++i) - put_pixel_(dst, color, i, j10); - } - - if ( i00 - 1 >= 0 && i00 - 1 < dst.width - && j00 - 1 >= 0 && j00 - 1 < dst.height) { - color.w = gamma_(gamma_re_(alpha) * kx0 * ky0); - put_pixel_(dst, color, i00 - 1, j00 - 1); - } - - if ( i10 >= 0 && i10 < dst.width - && j00 - 1 >= 0 && j00 - 1 < dst.height) { - color.w = gamma_(gamma_re_(alpha) * kx1 * ky0); - put_pixel_(dst, color, i10, j00 - 1); - } - - if ( i00 - 1 >= 0 && i00 - 1 < dst.width - && j10 >= 0 && j10 < dst.height) { - color.w = gamma_(gamma_re_(alpha) * kx0 * ky1); - put_pixel_(dst, color, i00 - 1, j10); - } - - if ( i10 >= 0 && i10 < dst.width - && j10 >= 0 && j10 < dst.height) { - color.w = gamma_(gamma_re_(alpha) * kx1 * ky1); - put_pixel_(dst, color, i10, j10); + for (i64 j = j0; j < j1; ++j) { + vec4_f32 *p = dst.pixels + j * dst.stride + i0; + vec4_f32 *p_end = dst.pixels + j * dst.stride + i1; + for (; p < p_end; ++p) + put_pixel_(p, color); } } @@ -496,8 +388,10 @@ void fill_triangle_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[3]) break; } - for (i64 i = left; i <= right; ++i) - put_pixel_(dst, color, i, j); + vec4_f32 *p = dst.pixels + j * dst.stride + left; + vec4_f32 *p_end = dst.pixels + j * dst.stride + right + 1; + for (; p < p_end; ++p) + put_pixel_(p, color); } } @@ -539,8 +433,10 @@ void fill_quad_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[4]) { break; } - for (i64 i = left; i <= right; ++i) - put_pixel_(dst, color, i, j); + vec4_f32 *p = dst.pixels + j * dst.stride + left; + vec4_f32 *p_end = dst.pixels + j * dst.stride + right + 1; + for (; p < p_end; ++p) + put_pixel_(p, color); } } @@ -576,8 +472,10 @@ void fill_ellipse_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area) { if (left < 0) left = 0; if (right >= dst.width) right = dst.width - 1; - for (i64 i = left; i <= right; ++i) - put_pixel_(dst, color, i, j); + vec4_f32 *p = dst.pixels + j * dst.stride + left; + vec4_f32 *p_end = dst.pixels + j * dst.stride + right + 1; + for (; p < p_end; ++p) + put_pixel_(p, color); } } @@ -810,13 +708,13 @@ static void draw_text_(Pixel_Buffer dst, vec4_f32 color, f64 x0, f64 y0, f64 sca f64 h_inv = 1. / h; for (i64 i = i0; i <= i1; ++i) { - i64 column = (i64) floor(((i - x) * num_cols) * w_inv + .5); + i64 column = (i64) floor(((i - x + .5) * num_cols) * w_inv); i64 offset = char_column_offset_(text[n], column); for (i64 j = j0; j <= j1; ++j) { - i64 row = (i64) floor(((j - y) * CHAR_NUM_BITS_Y_) * h_inv + .5); + i64 row = (i64) floor(((j - y + .5) * CHAR_NUM_BITS_Y_) * h_inv); if (char_bit_(offset, row)) - put_pixel_(dst, color, i, j); + put_pixel_(dst.pixels + j * dst.stride + i, color); } } } @@ -1433,33 +1331,17 @@ typedef union { 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., -}; +static i64 _graphics_cache_gc_index = 0; static void graphics_request_hash_(Blake2b_State *S, Graphics_Request req) { switch (req.op) { @@ -1817,41 +1699,14 @@ static b8 mem_eq_(i64 size, void *x, void *y) { 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; + vec4_f32 *s = src.pixels + j * src.stride; + vec4_f32 *d = dst.pixels + (y + j) * dst.stride + x; + vec4_f32 *d_end = d + src.width; + for (; d < d_end; ++d, ++s) { 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); } } } @@ -1891,14 +1746,10 @@ static void perform_graphics_request_cached_(Pixel_Buffer dst, Graphics_Request Graphics_Cache_Slot_ *s = _graphics_cache + slot; - i64 num_pixels = norm.width * norm.height; - i64 upscaled_len = num_pixels * (ANTIALIASING_SCALE * ANTIALIASING_SCALE); - + i64 num_pixels = norm.width * norm.height; 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; @@ -1907,44 +1758,21 @@ static void perform_graphics_request_cached_(Pixel_Buffer dst, Graphics_Request 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) + if (s->buffer_len < num_pixels) // 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_graphics_request_to_buffer_(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; - } - } + Pixel_Buffer dst = { + .width = s->width, + .height = s->height, + .stride = s->width, + .pixels = s->buffer, + }; - 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; - } + perform_graphics_request_to_buffer_(dst, s->req); } - if (s->done_downscaling) + if (s->occupied) copy_pixels_quick_(dst, norm.x, norm.y, (Pixel_Buffer) { .width = s->width, .height = s->height, @@ -1990,17 +1818,12 @@ void perform_graphics_request(Graphics_Context context, Graphics_Request 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); + i64 required_len = !s->occupied ? 0 : s->width * s->height; 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 1ca54e5..5079f4d 100755 --- a/reduced_system_layer.c +++ b/reduced_system_layer.c @@ -52,6 +52,7 @@ #/ + [x] Memory buffer allocator #/ + [x] Blake2 hash #/ + [x] Requests cache +#/ + [x] Global anti-aliasing #/ - Examples #/ - Conway's Game of Life #/ - Julia Set @@ -259,12 +260,16 @@ i32 main(i32 argc, c8 **argv); #define MEMORY_CHUNK_SIZE 1024 #endif +#ifndef DEFAULT_ANTIALIASING_SCALE +#define DEFAULT_ANTIALIASING_SCALE 4 +#endif + #ifndef MIN_PIXEL_SIZE #define MIN_PIXEL_SIZE (1.0) #endif #ifndef MAX_PIXEL_SIZE -#define MAX_PIXEL_SIZE (16.0) +#define MAX_PIXEL_SIZE (40.0) #endif #ifndef DEFAULT_PIXEL_SIZE @@ -593,13 +598,14 @@ typedef struct { u8 v4_address[4]; u8 v6_address[16]; }; -} IP_Address; +} Network_Address; typedef struct { c8 *title; i32 frame_width; i32 frame_height; f64 pixel_size; + i32 antialiasing_scale; // TODO: Global anti-aliasing. b8 exact_resolution : 1; b8 graceful_shutdown : 1; @@ -651,6 +657,7 @@ i8 utf8_write(c32 c, c8 *buffer); // Time and sleep i64 current_utc_time_in_milliseconds(void); +void current_utc_time_in_sec_and_nsec(i64 *seconds, i64 *nanoseconds); void yield_thread_execution(void); void suspend_thread_for_milliseconds(i64 duration); @@ -676,8 +683,8 @@ void handle_primary_sound(void); void queue_primary_sound(i64 delay_in_samples, i64 num_samples, f32 *frames); // Networking -i64 network_recv(u16 slot, IP_Address address, i64 size, u8 *data, u16 *local_port, IP_Address *remote_address); -i64 network_send(u16 slot, IP_Address address, i64 size, u8 *data, u16 *local_port); +i64 network_recv(u16 slot, Network_Address address, i64 size, u8 *data, u16 *local_port, Network_Address *remote_address); +i64 network_send(u16 slot, Network_Address address, i64 size, u8 *data, u16 *local_port); // NOTE: This will shutdown main window, networking and audio. // If g_platform.graceful_shutdown is 0, this does nothing. @@ -890,6 +897,9 @@ void resize_dynamic_array_exact(i64 *num, void **data, i64 element_size, i64 new return; } + if (*num == new_num) + return; + void *new_data = memory_buffer_allocate( new_num * element_size, element_size, @@ -911,6 +921,9 @@ void resize_dynamic_array_capacity(i64 *num, i64 *capacity, void **data, i64 ele return; } + if (*num == new_num) + return; + if (new_num > *capacity) resize_dynamic_array_exact(capacity, data, element_size, new_num); @@ -1357,6 +1370,17 @@ static vec3_f32 rgb_f32_from_u32_(u32 c) { }; } +// ================================================================ +// +// Platform +// +// ================================================================ + +static i64 _internal_pixels_len = 0; +static i32 _internal_width = 0; +static i32 _internal_height = 0; +static u32 *_internal_pixels = NULL; + static i64 average_frame_duration_(i64 duration) { _frame_duration[_frame_index] = duration; _frame_index = (_frame_index + 1) % NUM_FRAMES_AVERAGED; @@ -1455,20 +1479,24 @@ static void drop_files_set_data_(i64 index, i64 data_size) { resize_dynamic_array_exact(&f->data_size, (void **) &f->data, 1, data_size); } +static i32 min2_i32_(i32 x, i32 y) { + return x < y ? x : y; +} + +static i32 max2_i32_(i32 x, i32 y) { + return x > y ? x : y; +} + static i8 pixel_size_update_(i32 real_width, i32 real_height) { i8 size_changed = 0; - i32 width = real_width; - i32 height = real_height; + if (g_platform.antialiasing_scale <= 0) g_platform.antialiasing_scale = DEFAULT_ANTIALIASING_SCALE; + if (g_platform.pixel_size <= .0) g_platform.pixel_size = DEFAULT_PIXEL_SIZE; + if (g_platform.pixel_size < MIN_PIXEL_SIZE) g_platform.pixel_size = MIN_PIXEL_SIZE; + if (g_platform.pixel_size > MAX_PIXEL_SIZE) g_platform.pixel_size = MAX_PIXEL_SIZE; - if (g_platform.pixel_size <= 0.) g_platform.pixel_size = DEFAULT_PIXEL_SIZE; - if (g_platform.pixel_size < MIN_PIXEL_SIZE) g_platform.pixel_size = MIN_PIXEL_SIZE; - if (g_platform.pixel_size > MAX_PIXEL_SIZE) g_platform.pixel_size = MAX_PIXEL_SIZE; - - if (g_platform.pixel_size > 1.) { - width = (i32) floor(((f64) real_width) / g_platform.pixel_size + .5); - height = (i32) floor(((f64) real_height) / g_platform.pixel_size + .5); - } + i32 width = (i32) floor(((f64) real_width) / g_platform.pixel_size + .5) * g_platform.antialiasing_scale; + i32 height = (i32) floor(((f64) real_height) / g_platform.pixel_size + .5) * g_platform.antialiasing_scale; if (g_platform.real_width != real_width || g_platform.real_height != real_height) { size_changed = 1; @@ -1476,16 +1504,125 @@ static i8 pixel_size_update_(i32 real_width, i32 real_height) { g_platform.real_height = real_height; } + resize_dynamic_array_exact(&g_platform.num_pixels, (void **) &g_platform.pixels, sizeof *g_platform.pixels, width * height); + height = g_platform.num_pixels / width; + if (g_platform.frame_width != width || g_platform.frame_height != height) { size_changed = 1; g_platform.frame_width = width; g_platform.frame_height = height; } + i32 internal_width = max2_i32_(real_width, width / g_platform.antialiasing_scale); + i32 internal_height = max2_i32_(real_height, height / g_platform.antialiasing_scale); + + resize_dynamic_array_exact(&_internal_pixels_len, (void **) &_internal_pixels, sizeof *_internal_pixels, internal_width * internal_height); + _internal_width = real_width; + _internal_height = min2_i32_(_internal_pixels_len / real_width, real_height); + return size_changed; } -static void pixel_size_calubrate_(i64 current_frame_duration) { +static f64 gamma_(f64 x) { + if (x >= 0.0031308) + return 1.055 * pow(x, 1.0 / 2.4) - 0.055; + return 12.92 * x; +} + +static f64 gamma_re_(f64 x) { + if (x >= 0.04045) + return pow((x + 0.055) / 1.055, 2.4); + return x / 12.92; +} + +static void convert_pixels_for_window_(void) { + i32 aa_scale = g_platform.antialiasing_scale; + + if (aa_scale <= 0) { + LOG_ERROR("Sanity"); + return; + } + + // Downscale pixels from vec4_f32 pixel buffer to internal u32 pixel buffer. + { + i64 dst_width = g_platform.frame_width / aa_scale; + i64 dst_height = g_platform.frame_height / aa_scale; + i64 src_width = g_platform.frame_width; + i64 src_stride = g_platform.frame_width * aa_scale; + + if (g_platform.num_pixels < src_width * g_platform.frame_height) { + LOG_ERROR("Sanity"); + return; + } + + if (_internal_pixels_len < dst_width * dst_height) { + LOG_ERROR("Sanity"); + return; + } + + f32 k = 1.f / (aa_scale * aa_scale); + + for (i64 j = 0; j < dst_width; ++j) { + vec4_f32 *s = g_platform.pixels + j * src_stride; + u32 *d = _internal_pixels + j * dst_width; + u32 *d_end = d + dst_width; + + for (; d < d_end; ++d, s += aa_scale) { + vec3_f32 color = {0}; + + for (i32 jj = 0; jj < aa_scale; ++jj) { + vec4_f32 *ss = s + jj * src_width; + vec4_f32 *ss_end = ss + aa_scale; + + for (; ss < ss_end; ++ss) { + color.x += gamma_re_(ss->z); + color.y += gamma_re_(ss->y); + color.z += gamma_re_(ss->x); + } + } + + color.x = gamma_(color.x * k); + color.y = gamma_(color.y * k); + color.z = gamma_(color.z * k); + + *d = rgb_u32_from_f32_(color) | 0xff000000; + } + } + } + + // Resize internal pixel buffer in-place. + { + i64 src_width = g_platform.frame_width / aa_scale; + i64 src_height = g_platform.frame_height / aa_scale; + i64 src_len = src_width * src_height; + i64 dst_width = _internal_width; + i64 dst_height = _internal_height; + i64 dst_len = dst_width * dst_height; + + if (_internal_pixels_len < src_len) { + LOG_ERROR("Sanity"); + return; + } + + if (_internal_pixels_len < dst_len) { + LOG_ERROR("Sanity"); + return; + } + + i64 half_w = dst_width / 2; + i64 half_h = dst_height / 2; + + // FIXME, PERF: Use pointers, check if src_len is less than dst_len. + for (i64 j = dst_height - 1; j >= 0; --j) + for (i64 i = dst_width - 1; i >= 0; --i) { + i64 n = j * dst_width + i; + i64 nn = ((j * src_height) / dst_height) * src_width + (i * src_width) / dst_width; + _internal_pixels[n] = _internal_pixels[nn]; + } + } +} + +static void pixel_size_calibrate_(i64 current_frame_duration) { if (g_platform.exact_resolution) return; @@ -1499,6 +1636,11 @@ static void pixel_size_calubrate_(i64 current_frame_duration) { g_platform.pixel_size -= PIXEL_SIZE_DELTA * (MIN_FRAME_DURATION - frame_duration); } +static void cleanup_pixel_buffers_(void) { + resize_dynamic_array_exact(&_internal_pixels_len, (void **) &_internal_pixels, sizeof *_internal_pixels, 0); + resize_dynamic_array_exact(&g_platform.num_pixels, (void **) &g_platform.pixels, sizeof *g_platform.pixels, 0); +} + // ================================================================ // // Unix @@ -1511,6 +1653,13 @@ static void pixel_size_calubrate_(i64 current_frame_duration) { #include <sched.h> #include <unistd.h> +void current_utc_time_in_sec_and_nsec(i64 *seconds, i64 *nanoseconds) { + struct timespec t; + timespec_get(&t, TIME_UTC); + if (seconds != NULL) *seconds = t.tv_sec; + if (nanoseconds != NULL) *nanoseconds = t.tv_nsec; +} + i64 current_utc_time_in_milliseconds(void) { struct timespec t; timespec_get(&t, TIME_UTC); @@ -1553,10 +1702,10 @@ void suspend_thread_for_milliseconds(i64 duration) { #include <sys/socket.h> typedef struct { - b8 ready; - i32 socket; - u16 local_port; - IP_Address address; + b8 ready; + i32 socket; + u16 local_port; + Network_Address address; } Socket_Slot; static b8 _network_ready = 0; @@ -1588,7 +1737,7 @@ static void network_cleanup_(void) { resize_dynamic_array_exact(&_num_sockets, (void **) &_sockets, sizeof *_sockets, 0); } -static b8 network_open_(u16 slot, IP_Address address, u16 *local_port) { +static b8 network_open_(u16 slot, Network_Address address, u16 *local_port) { network_init_(slot); b8 change_address = @@ -1664,9 +1813,9 @@ static b8 network_open_(u16 slot, IP_Address address, u16 *local_port) { return 1; } -i64 network_recv(u16 slot, IP_Address address, i64 size, u8 *data, u16 *local_port, IP_Address *remote_address) { +i64 network_recv(u16 slot, Network_Address address, i64 size, u8 *data, u16 *local_port, Network_Address *remote_address) { if (address.protocol != IPv4_UDP && address.protocol != IPv6_UDP) { - LOG_ERROR("Invalid address protocol %d.", (i32) (u32) address.protocol); + LOG_ERROR("Invalid address protocol: %d", (i32) (u32) address.protocol); return 0; } @@ -1722,13 +1871,13 @@ i64 network_recv(u16 slot, IP_Address address, i64 size, u8 *data, u16 *local_po return received; } -i64 network_send(u16 slot, IP_Address address, i64 size, u8 *data, u16 *local_port) { +i64 network_send(u16 slot, Network_Address address, i64 size, u8 *data, u16 *local_port) { if (address.protocol != IPv4_UDP && address.protocol != IPv6_UDP) { - LOG_ERROR("Invalid address protocol %d.", (i32) (u32) address.protocol); + LOG_ERROR("Invalid address protocol: %d", (i32) (u32) address.protocol); return 0; } - IP_Address local_address = address; + Network_Address local_address = address; local_address.port = 0; if (!network_open_(slot, local_address, local_port)) @@ -2545,11 +2694,6 @@ static Atom _text_uri_list = 0; static Window _dnd_source = 0; static b8 _mapped = 0; static b8 _requested_clipboard = 0; -// TODO: Use the same buffer for both scaled and non-scaled images. -static i64 _pixels_scaled_len = 0; -static u32 * _pixels_scaled = NULL; -static i64 _pixels_internal_len = 0; -static u32 * _pixels_internal = NULL; static i16 _key_table [MAX_NUM_KEYS] = {0}; static b8 _key_repeat [MAX_NUM_KEYS] = {0}; @@ -2568,17 +2712,12 @@ static i32 x11_error_handler_(Display *display, XErrorEvent *event) { return 0; } -void update_main_window_frame_(void) { - i64 num_pixels = g_platform.frame_width * g_platform.frame_height; - - if (g_platform.num_pixels < num_pixels) - resize_dynamic_array_exact(&g_platform.num_pixels, (void **) &g_platform.pixels, sizeof *g_platform.pixels, num_pixels); - if (_pixels_scaled_len < num_pixels) - resize_dynamic_array_exact(&_pixels_scaled_len, (void **) &_pixels_scaled, sizeof *_pixels_scaled, num_pixels); - if (_pixels_internal_len < num_pixels) - resize_dynamic_array_exact(&_pixels_internal_len, (void **) &_pixels_internal, sizeof *_pixels_internal, num_pixels); +void put_image_to_main_window_(void) { + if (_image.width <= 0 || _image.height <= 0) + return; - _image.data = (c8 *) _pixels_internal; + XPutImage(_display, _window, _gc, &_image, 0, 0, 0, 0, _image.width, _image.height); + XFlush(_display); } void init_main_window(void) { @@ -2752,13 +2891,16 @@ void init_main_window(void) { if (_ic == NULL) { LOG_ERROR("XCreateIC failed."); return; - } + } + + pixel_size_update_(g_platform.frame_width, g_platform.frame_height); _image = (XImage) { - .width = g_platform.frame_width, - .height = g_platform.frame_height, + .width = _internal_width, + .height = _internal_height, .depth = depth, .format = ZPixmap, + .data = (c8 *) _internal_pixels, .byte_order = LSBFirst, .bitmap_unit = 32, .bitmap_bit_order = LSBFirst, @@ -2770,8 +2912,6 @@ void init_main_window(void) { .blue_mask = 0x0000ff, }; - update_main_window_frame_(); - XInitImage(&_image); _wm_delete_window = XInternAtom(_display, "WM_DELETE_WINDOW", False); @@ -2802,10 +2942,6 @@ void init_main_window(void) { XChangeProperty(_display, _window, _dnd_aware, 4, 32, PropModeReplace, &(u8) {5}, 1); - // FIXME: Check if _image.data is successfully allocated. - XPutImage(_display, _window, _gc, &_image, 0, 0, 0, 0, _image.width, _image.height); - XFlush(_display); - _mapped = 0; } @@ -2824,16 +2960,13 @@ void shutdown_all_systems(void) { _display = NULL; drop_files_clean_(); - g_platform.input_len = 0; - - resize_dynamic_array_exact(&_pixels_scaled_len, (void **) &_pixels_scaled, sizeof *_pixels_scaled, 0); - resize_dynamic_array_exact(&_pixels_internal_len, (void **) &_pixels_internal, sizeof *_pixels_internal, 0); - resize_dynamic_array_exact(&g_platform.num_pixels, (void **) &g_platform.pixels, sizeof *g_platform.pixels, 0); - resize_dynamic_array_exact(&g_platform.input_capacity, (void **) &g_platform.input, sizeof *g_platform.input, 0); - resize_dynamic_array_exact(&g_platform.clipboard_text_len, (void **) &g_platform.clipboard_text, 1, 0); - + cleanup_pixel_buffers_(); network_cleanup_(); sound_cleanup_(); + + g_platform.input_len = 0; + resize_dynamic_array_exact(&g_platform.input_capacity, (void **) &g_platform.input, sizeof *g_platform.input, 0); + resize_dynamic_array_exact(&g_platform.clipboard_text_len, (void **) &g_platform.clipboard_text, 1, 0); } i32 handle_main_window_events(void) { @@ -2980,7 +3113,7 @@ i32 handle_main_window_events(void) { g_platform.cursor_y = ev.xkey.y; g_platform.key_down[k] = 0; - _key_repeat[k] = 1; + _key_repeat[k] = 1; g_platform.key_down[MOD_CTRL] = !!(ev.xkey.state & ControlMask); g_platform.key_down[MOD_SHIFT] = !!(ev.xkey.state & ShiftMask); @@ -3350,13 +3483,13 @@ i32 handle_main_window_events(void) { XWindowAttributes attrs; XGetWindowAttributes(_display, _window, &attrs); - if (attrs.width > 0 && attrs.height > 0) { - _image.width = attrs.width; - _image.height = attrs.height; - _image.bytes_per_line = _image.width * 4; - } + num_events += pixel_size_update_(attrs.width, attrs.height); - num_events += pixel_size_update_(_image.width, _image.height); + _image.width = _internal_width; + _image.height = _internal_height; + _image.data = (c8 *) _internal_pixels; + + _image.bytes_per_line = _image.width * 4; return num_events; } @@ -3383,41 +3516,11 @@ void render_main_window_frame(void) { _mapped = 1; } - update_main_window_frame_(); - - i64 size = g_platform.frame_width * g_platform.frame_height; - if (size > g_platform.num_pixels) - size = g_platform.num_pixels; - - if (g_platform.frame_width == _image.width && g_platform.frame_height == _image.height) { - if (size > _pixels_internal_len) - size = _pixels_internal_len; - for (i64 i = 0; i < size; ++i) - _pixels_internal[i] = rgb_u32_from_f32_((vec3_f32) { g_platform.pixels[i].x, g_platform.pixels[i].y, g_platform.pixels[i].z }); - } else { - if (size > _pixels_scaled_len) - size = _pixels_scaled_len; - for (i64 i = 0; i < size; ++i) - _pixels_scaled[i] = rgb_u32_from_f32_((vec3_f32) { g_platform.pixels[i].x, g_platform.pixels[i].y, g_platform.pixels[i].z }); - - for (i64 j = 0; j < _image.height; ++j) { - if ((j + 1) * _image.width > _pixels_internal_len) - break; - i64 j0 = (j * g_platform.frame_height) / _image.height; - if ((j0 + 1) * g_platform.frame_width > _pixels_scaled_len) - break; - for (i64 i = 0; i < _image.width; ++i) { - i64 i0 = (i * g_platform.frame_width) / _image.width; - _pixels_internal[j * _image.width + i] = _pixels_scaled[j0 * g_platform.frame_width + i0]; - } - } - } + convert_pixels_for_window_(); - // FIXME: Check if _image.data is successfully allocated. - XPutImage(_display, _window, _gc, &_image, 0, 0, 0, 0, _image.width, _image.height); - XFlush(_display); + put_image_to_main_window_(); - pixel_size_calubrate_(current_utc_time_in_milliseconds() - _frame_time); + pixel_size_calibrate_(current_utc_time_in_milliseconds() - _frame_time); } void write_clipboard_text(i64 size, c8 *data) { @@ -3647,7 +3750,7 @@ static b8 _files_dropped = 0; static c8 _href [4096] = {0}; static u32 _pixels_scaled [MAX_NUM_PIXELS] = {0}; -static u32 _pixels_internal [MAX_NUM_PIXELS] = {0}; +static u32 _internal_pixels [MAX_NUM_PIXELS] = {0}; static b8 _key_pressed [MAX_NUM_KEYS] = {0}; static f32 _sound_buffer [MAX_NUM_PRIMARY_SOUND_FRAMES] = {0}; static u8 _drop_buffer [DROP_FILES_BUFFER_SIZE] = {0}; @@ -3656,18 +3759,24 @@ void shutdown_all_systems(void) { g_platform.done = 1; } -i64 network_recv(u16 slot, IP_Address address, i64 size, u8 *data, u16 *local_port, IP_Address *remote_address) { +i64 network_recv(u16 slot, Network_Address address, i64 size, u8 *data, u16 *local_port, Network_Address *remote_address) { LOG_ERROR("Web sockets are not implemented."); return 0; } -i64 network_send(u16 slot, IP_Address address, i64 size, u8 *data, u16 *local_port) { +i64 network_send(u16 slot, Network_Address address, i64 size, u8 *data, u16 *local_port) { LOG_ERROR("Web sockets are not implemented."); return 0; } i64 current_utc_time_in_milliseconds(void); +void current_utc_time_in_sec_and_nsec(i64 *seconds, i64 *nanoseconds) { + i64 time = current_utc_time_in_milliseconds(); + if (seconds != NULL) *seconds = time / 1000ll; + if (nanoseconds != NULL) *nanoseconds = 1000000ll * (time % 1000ll); +} + void yield_thread_execution(void) { // Do nothing } @@ -3741,11 +3850,11 @@ void render_main_window_frame(void) { i64 size = g_platform.frame_width * g_platform.frame_height; if (size > g_platform.num_pixels) size = g_platform.num_pixels; - if (size > _pixels_internal_len) - size = _pixels_internal_len; + if (size > _internal_pixels_len) + size = _internal_pixels_len; for (i64 i = 0; i < size; ++i) - _pixels_internal[i] = 0xff000000u | rgb_u32_from_f32_((vec3_f32) { g_platform.pixels[i].z, g_platform.pixels[i].y, g_platform.pixels[i].x }); + _internal_pixels[i] = 0xff000000u | rgb_u32_from_f32_((vec3_f32) { g_platform.pixels[i].z, g_platform.pixels[i].y, g_platform.pixels[i].x }); } else { i64 size = g_platform.frame_width * g_platform.frame_height; if (size > g_platform.num_pixels) @@ -3758,12 +3867,12 @@ void render_main_window_frame(void) { for (i64 j = 0; j < _frame_height; ++j) { i64 j0 = (j * g_platform.frame_height) / _frame_height; - if ((j0 + 1) * _frame_width > _pixels_internal_len) break; + if ((j0 + 1) * _frame_width > _internal_pixels_len) break; for (i64 i = 0; i < _frame_width; ++i) { i64 i0 = (i * g_platform.frame_width) / _frame_width; i64 n0 = j0 * g_platform.frame_width + i0; if (n0 > _pixels_scaled_len) break; - _pixels_internal[j * _frame_width + i] = 0xff000000u | _pixels_scaled[n0]; + _internal_pixels[j * _frame_width + i] = 0xff000000u | _pixels_scaled[n0]; } } } @@ -3859,7 +3968,7 @@ __attribute__((export_name("js_title"))) void *js_title(void) { } __attribute__((export_name("js_pixels"))) void *js_pixels(void) { - return _pixels_internal; + return _internal_pixels; } __attribute__((export_name("js_frame"))) void js_frame(i32 frame_width, i32 frame_height, i32 num_samples) { @@ -3892,7 +4001,7 @@ __attribute__((export_name("js_frame"))) void js_frame(i32 frame_width, i32 fram _sound_read = (_sound_read + num_samples * NUM_PRIMARY_SOUND_CHANNELS) % MAX_NUM_PRIMARY_SOUND_FRAMES; if (do_render) - pixel_size_calubrate_(current_utc_time_in_milliseconds() - frame_time); + pixel_size_calibrate_(current_utc_time_in_milliseconds() - frame_time); } __attribute__((export_name("js_mousemove"))) void js_mousemove(i32 x, i32 y) { |