From 55c6833e5994b1b37919a4a7366fc7e7bec47bd2 Mon Sep 17 00:00:00 2001 From: Constantin Piber Date: Sun, 19 Oct 2025 12:03:44 +0200 Subject: [PATCH 1/3] POC coroutine impl --- nob.c | 3 +- plugs/cpp_async/interpolators.h | 83 +++++++++++++++++ plugs/cpp_async/plug.cpp | 157 ++++++++++++++++++++++++++++++++ 3 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 plugs/cpp_async/interpolators.h create mode 100644 plugs/cpp_async/plug.cpp diff --git a/nob.c b/nob.c index 37ebbe1..16d164d 100644 --- a/nob.c +++ b/nob.c @@ -75,7 +75,7 @@ bool build_plug_cxx(bool force, Nob_Cmd *cmd, const char *source_path, const cha if (force || rebuild_is_needed) { cxx(cmd); - nob_cmd_append(cmd, "-fPIC", "-shared", "-Wl,--no-undefined"); + nob_cmd_append(cmd, "-fPIC", "-shared", "-Wl,--no-undefined", "-fcoroutines", "-std=c++20"); nob_cmd_append(cmd, "-o", output_path); nob_cmd_append(cmd, source_path); libs(cmd); @@ -129,6 +129,7 @@ int main(int argc, char **argv) if (!build_plug_c(force, &cmd, PLUGS_DIR"squares/plug.c", BUILD_DIR"libsquare.so")) return 1; if (!build_plug_c(force, &cmd, PLUGS_DIR"bezier/plug.c", BUILD_DIR"libbezier.so")) return 1; if (!build_plug_cxx(force, &cmd, PLUGS_DIR"cpp/plug.cpp", BUILD_DIR"libcpp.so")) return 1; + if (!build_plug_cxx(force, &cmd, PLUGS_DIR"cpp_async/plug.cpp", BUILD_DIR"libcpp_async.so")) return 1; { const char *output_path = BUILD_DIR"libc3"; const char *source_paths[] = { diff --git a/plugs/cpp_async/interpolators.h b/plugs/cpp_async/interpolators.h new file mode 100644 index 0000000..96efb56 --- /dev/null +++ b/plugs/cpp_async/interpolators.h @@ -0,0 +1,83 @@ +#ifndef INTERPOLATORS_H_ +#define INTERPOLATORS_H_ + +#include +#include +#include + +typedef enum { + FUNC_ID, + FUNC_SINSTEP, + FUNC_SMOOTHSTEP, + FUNC_SQR, + FUNC_SQRT, + FUNC_SINPULSE, +} Interp_Func; + +static inline float smoothstep(float x) +{ + if (x < 0.0) return 0.0; + if (x >= 1.0) return 1.0; + return 3*x*x - 2*x*x*x; +} + +static inline float sinstep(float t) +{ + if (t < 0.0) return 0.0; + if (t >= 1.0) return 1.0; + return (sinf(PI*t - PI*0.5) + 1)*0.5; +} + +static inline float sinpulse(float t) +{ + if (t < 0.0) return 0.0; + if (t >= 1.0) return 0.0; + return sinf(PI*t); +} + +static inline Vector2 cubic_bezier(float t, Vector2 nodes[4]) +{ + float it = 1 - t; + Vector2 b = Vector2Scale(nodes[0], it*it*it); + b = Vector2Add(b, Vector2Scale(nodes[1], 3*it*it*t)); + b = Vector2Add(b, Vector2Scale(nodes[2], 3*it*t*t)); + b = Vector2Add(b, Vector2Scale(nodes[3], t*t*t)); + return b; +} + +static inline Vector2 cubic_bezier_der(float t, Vector2 nodes[4]) +{ + float it = 1 - t; + Vector2 b = Vector2Scale(nodes[0], -3*it*it); + b = Vector2Add(b, Vector2Scale(nodes[1], 3*it*it)); + b = Vector2Add(b, Vector2Scale(nodes[1], -6*it*t)); + b = Vector2Add(b, Vector2Scale(nodes[2], 6*it*t)); + b = Vector2Add(b, Vector2Scale(nodes[2], -3*t*t)); + b = Vector2Add(b, Vector2Scale(nodes[3], 3*t*t)); + return b; +} + +static inline float cuber_bezier_newton(float x, Vector2 nodes[4], size_t n) +{ + float t = 0; + for (size_t i = 0; i < n; ++i) { + t = t - (cubic_bezier(t, nodes).x - x)/cubic_bezier_der(t, nodes).x; + } + return t; +} + +static inline float interp_func(Interp_Func func, float t) +{ + switch (func) { + case FUNC_ID: return t; + case FUNC_SQR: return t*t; + case FUNC_SQRT: return sqrtf(t); + case FUNC_SINSTEP: return sinstep(t); + case FUNC_SMOOTHSTEP: return smoothstep(t); + case FUNC_SINPULSE: return sinpulse(t); + } + assert(0 && "UNREACHABLE"); + return 0.0f; +} + +#endif // INTERPOLATORS_H_ diff --git a/plugs/cpp_async/plug.cpp b/plugs/cpp_async/plug.cpp new file mode 100644 index 0000000..da01d7c --- /dev/null +++ b/plugs/cpp_async/plug.cpp @@ -0,0 +1,157 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include "env.h" +#include "interpolators.h" +#include "plug.h" + +#define SQUARE_SIZE 20 + +struct promise; + +struct Task : std::coroutine_handle +{ + using promise_type = ::promise; +}; + +struct promise +{ + Task get_return_object() { return {Task::from_promise(*this)}; } + std::suspend_always initial_suspend() noexcept { return {}; } + std::suspend_always final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception() {} + std::suspend_always yield_value(int /* dummy */) + { + return {}; + } +}; + +#define FONT_SIZE 68 + +typedef struct { + size_t size; + Task task; + Vector2 position1; + Vector2 position2; +} Plug; + +static Plug *p; + +static void load_assets(void) +{ +} + +static void unload_assets(void) +{ +} + +static Task move(Vector2 &position, Vector2 direction, size_t n) +{ + for (size_t i = 0; i < n; ++i) + { + position = Vector2Add(position, direction); + co_yield 0; + } +} + +static Task combine(std::vector tasks) +{ + while (true) + { + bool ran = false; + for (auto &task : tasks) + { + if (!task.done()) + { + ran = true; + task(); + } + } + if (!ran) + break; + co_yield 0; + } +} + +extern "C" { + +#define PLUG(name, ret, ...) ret name(__VA_ARGS__); +LIST_OF_PLUGS +#undef PLUG + +void plug_reset(void) +{ + p->position1 = {0, 0}; + p->position2 = {300, 0}; + p->task = combine({ move(p->position1, {0.1, 0}, 300), move(p->position2, {0, 0.1}, 300) }); +} + +void plug_init(void) +{ + p = (Plug*)malloc(sizeof(*p)); + assert(p != NULL); + memset(p, 0, sizeof(*p)); + p->size = sizeof(*p); + + load_assets(); + plug_reset(); +} + +void *plug_pre_reload(void) +{ + unload_assets(); + return p; +} + +void plug_post_reload(void *state) +{ + p = (Plug*)state; + if (p->size < sizeof(*p)) { + TraceLog(LOG_INFO, "Migrating plug state schema %zu bytes -> %zu bytes", p->size, sizeof(*p)); + p = (Plug*)realloc(p, sizeof(*p)); + p->size = sizeof(*p); + } + + load_assets(); +} + +void plug_update(Env env) +{ + if (!p->task.done()) + p->task(); + + Color background_color = ColorFromHSV(0, 0, 0.05); + Color foreground_color = ColorFromHSV(0, 0, 0.95); + + ClearBackground(background_color); + + Rectangle boundary = { + .x = p->position1.x, + .y = p->position1.y, + .width = SQUARE_SIZE, + .height = SQUARE_SIZE, + }; + DrawRectangleRec(boundary, foreground_color); + boundary = { + .x = p->position2.x, + .y = p->position2.y, + .width = SQUARE_SIZE, + .height = SQUARE_SIZE, + }; + DrawRectangleRec(boundary, foreground_color); +} + +bool plug_finished(void) +{ + return p->task.done(); +} + +} From 852677db6b4d65c84d62dd00befc0faacbecc607 Mon Sep 17 00:00:00 2001 From: Constantin Piber Date: Sun, 19 Oct 2025 13:06:30 +0200 Subject: [PATCH 2/3] Macro-based await --- plugs/cpp_async/plug.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/plugs/cpp_async/plug.cpp b/plugs/cpp_async/plug.cpp index da01d7c..a700384 100644 --- a/plugs/cpp_async/plug.cpp +++ b/plugs/cpp_async/plug.cpp @@ -14,6 +14,11 @@ #define SQUARE_SIZE 20 +#define AWAIT(task) do { \ + auto __t = (task); \ + while (!__t.done()) { __t(); co_yield 0; } \ + } while (0) + struct promise; struct Task : std::coroutine_handle @@ -41,6 +46,7 @@ typedef struct { Task task; Vector2 position1; Vector2 position2; + Vector2 position3; } Plug; static Plug *p; @@ -62,6 +68,13 @@ static Task move(Vector2 &position, Vector2 direction, size_t n) } } +static Task move2(Vector2 &position, Vector2 direction1, size_t n1, Vector2 direction2, size_t n2) +{ + AWAIT(move(position, direction1, n1)); + printf("move 1 done\n"); + AWAIT(move(position, direction2, n2)); +} + static Task combine(std::vector tasks) { while (true) @@ -91,7 +104,8 @@ void plug_reset(void) { p->position1 = {0, 0}; p->position2 = {300, 0}; - p->task = combine({ move(p->position1, {0.1, 0}, 300), move(p->position2, {0, 0.1}, 300) }); + p->position3 = {100, 300}; + p->task = combine({ move(p->position1, {0.1, 0}, 300), move(p->position2, {0, 0.1}, 300), move2(p->position3, {0, 0.5}, 100, {0.5, 0}, 200) }); } void plug_init(void) @@ -147,6 +161,13 @@ void plug_update(Env env) .height = SQUARE_SIZE, }; DrawRectangleRec(boundary, foreground_color); + boundary = { + .x = p->position3.x, + .y = p->position3.y, + .width = SQUARE_SIZE, + .height = SQUARE_SIZE, + }; + DrawRectangleRec(boundary, foreground_color); } bool plug_finished(void) From b2c31bbeecfc47d7a502194a536981f26263793f Mon Sep 17 00:00:00 2001 From: Constantin Piber Date: Sun, 19 Oct 2025 13:11:07 +0200 Subject: [PATCH 3/3] Remove unused file --- plugs/cpp_async/interpolators.h | 83 --------------------------------- 1 file changed, 83 deletions(-) delete mode 100644 plugs/cpp_async/interpolators.h diff --git a/plugs/cpp_async/interpolators.h b/plugs/cpp_async/interpolators.h deleted file mode 100644 index 96efb56..0000000 --- a/plugs/cpp_async/interpolators.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef INTERPOLATORS_H_ -#define INTERPOLATORS_H_ - -#include -#include -#include - -typedef enum { - FUNC_ID, - FUNC_SINSTEP, - FUNC_SMOOTHSTEP, - FUNC_SQR, - FUNC_SQRT, - FUNC_SINPULSE, -} Interp_Func; - -static inline float smoothstep(float x) -{ - if (x < 0.0) return 0.0; - if (x >= 1.0) return 1.0; - return 3*x*x - 2*x*x*x; -} - -static inline float sinstep(float t) -{ - if (t < 0.0) return 0.0; - if (t >= 1.0) return 1.0; - return (sinf(PI*t - PI*0.5) + 1)*0.5; -} - -static inline float sinpulse(float t) -{ - if (t < 0.0) return 0.0; - if (t >= 1.0) return 0.0; - return sinf(PI*t); -} - -static inline Vector2 cubic_bezier(float t, Vector2 nodes[4]) -{ - float it = 1 - t; - Vector2 b = Vector2Scale(nodes[0], it*it*it); - b = Vector2Add(b, Vector2Scale(nodes[1], 3*it*it*t)); - b = Vector2Add(b, Vector2Scale(nodes[2], 3*it*t*t)); - b = Vector2Add(b, Vector2Scale(nodes[3], t*t*t)); - return b; -} - -static inline Vector2 cubic_bezier_der(float t, Vector2 nodes[4]) -{ - float it = 1 - t; - Vector2 b = Vector2Scale(nodes[0], -3*it*it); - b = Vector2Add(b, Vector2Scale(nodes[1], 3*it*it)); - b = Vector2Add(b, Vector2Scale(nodes[1], -6*it*t)); - b = Vector2Add(b, Vector2Scale(nodes[2], 6*it*t)); - b = Vector2Add(b, Vector2Scale(nodes[2], -3*t*t)); - b = Vector2Add(b, Vector2Scale(nodes[3], 3*t*t)); - return b; -} - -static inline float cuber_bezier_newton(float x, Vector2 nodes[4], size_t n) -{ - float t = 0; - for (size_t i = 0; i < n; ++i) { - t = t - (cubic_bezier(t, nodes).x - x)/cubic_bezier_der(t, nodes).x; - } - return t; -} - -static inline float interp_func(Interp_Func func, float t) -{ - switch (func) { - case FUNC_ID: return t; - case FUNC_SQR: return t*t; - case FUNC_SQRT: return sqrtf(t); - case FUNC_SINSTEP: return sinstep(t); - case FUNC_SMOOTHSTEP: return smoothstep(t); - case FUNC_SINPULSE: return sinpulse(t); - } - assert(0 && "UNREACHABLE"); - return 0.0f; -} - -#endif // INTERPOLATORS_H_