diff options
Diffstat (limited to 'reduced_system_layer.c')
-rwxr-xr-x | reduced_system_layer.c | 323 |
1 files changed, 216 insertions, 107 deletions
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) { |