diff options
| author | Vasco <[email protected]> | 2026-08-24 03:53:16 +0100 |
|---|---|---|
| committer | Vasco <[email protected]> | 2026-08-24 03:53:16 +0100 |
| commit | ad5248a3e1bafc1b7dec4c8af82d56ed324ef314 (patch) | |
| tree | b575c3b85d6ad314fa9768855bed9aed8642917e | |
| parent | 5dc2716abd43166cd65133625f408ad36a6623b3 (diff) | |
Slimed out podium. Updated math. Rend kinda works but still bugged.
| -rw-r--r-- | Draw/draw.h | 307 | ||||
| -rw-r--r-- | Fuse/demos/fuse.slang | 31 | ||||
| -rw-r--r-- | Fuse/demos/fuse_demo.c | 280 | ||||
| -rw-r--r-- | Math/math.h | 343 | ||||
| -rw-r--r-- | Peak/build.c | 39 | ||||
| -rwxr-xr-x | Peak/demos/demo | bin | 43544 -> 0 bytes | |||
| -rwxr-xr-x | Peak/demos/demo.wasm | bin | 30983 -> 0 bytes | |||
| -rwxr-xr-x | Peak/demos/demo_run | bin | 43912 -> 0 bytes | |||
| -rw-r--r-- | Peak/peak.h | 1037 | ||||
| -rw-r--r-- | Peak/src/p_emscripten.c | 118 | ||||
| -rw-r--r-- | Peak/src/p_linux.c | 187 | ||||
| -rw-r--r-- | Peak/src/p_log.c | 153 | ||||
| -rw-r--r-- | Peak/src/p_win32.c | 391 | ||||
| -rw-r--r-- | Peak/src/peak.c | 80 | ||||
| -rw-r--r-- | Peak/src/peak_header.h | 103 | ||||
| -rw-r--r-- | Peak/src/peak_header.h.out | 1037 | ||||
| -rw-r--r-- | README.md | 10 | ||||
| -rw-r--r-- | Rend/demos/build.c | 85 | ||||
| -rwxr-xr-x | Rend/demos/compute/rend_compute_demo | bin | 424800 -> 0 bytes | |||
| -rwxr-xr-x | Rend/demos/compute/rend_compute_fast | bin | 450512 -> 0 bytes | |||
| -rw-r--r-- | Rend/demos/poof.h | 874 | ||||
| -rw-r--r-- | Rend/demos/rend_teapot.c | 172 | ||||
| -rw-r--r-- | Rend/podium.h | 2282 | ||||
| -rw-r--r-- | Rend/rend.c | 11 | ||||
| -rw-r--r-- | Rend/rend.h | 20 | ||||
| -rw-r--r-- | Rend/rend_internal.h | 20 | ||||
| -rw-r--r-- | Rend/rend_vk.c | 198 | ||||
| -rw-r--r-- | Rend/rend_vk_device.c | 18 | ||||
| -rw-r--r-- | Rend/rend_vk_internal.h | 4 | ||||
| -rw-r--r-- | archive/Podium/README.md | 9 | ||||
| -rw-r--r-- | archive/Podium/p_allocators.c | 301 | ||||
| -rw-r--r-- | archive/Podium/p_allocators.h | 44 | ||||
| -rw-r--r-- | archive/Podium/p_ds.h | 52 | ||||
| -rw-r--r-- | archive/Podium/p_log.h | 186 | ||||
| -rw-r--r-- | archive/Podium/p_math.c | 255 | ||||
| -rw-r--r-- | archive/Podium/p_math.h | 33 | ||||
| -rw-r--r-- | archive/Podium/p_platform_linux.c | 404 | ||||
| -rw-r--r-- | archive/Podium/p_platform_win32.c | 402 | ||||
| -rw-r--r-- | archive/Podium/p_string.c | 201 | ||||
| -rw-r--r-- | archive/Podium/p_string.h | 38 | ||||
| -rw-r--r-- | archive/Podium/p_types.h | 88 | ||||
| -rw-r--r-- | archive/Podium/podium.c | 1 | ||||
| -rw-r--r-- | archive/Podium/podium.h | 2269 | ||||
| -rw-r--r-- | archive/Podium/podium_header.h | 258 | ||||
| -rw-r--r-- | build.c | 128 | ||||
| -rw-r--r-- | demos/compute/frag.spv (renamed from Rend/demos/compute/frag.spv) | bin | 376 -> 376 bytes | |||
| -rw-r--r-- | demos/compute/particle.spv (renamed from Rend/demos/compute/particle.spv) | bin | 1876 -> 1876 bytes | |||
| -rwxr-xr-x | demos/compute/rend_compute_demo | bin | 0 -> 446416 bytes | |||
| -rwxr-xr-x | demos/compute/rend_compute_fast | bin | 0 -> 474256 bytes | |||
| -rw-r--r-- | demos/compute/sand.spv (renamed from Rend/demos/compute/sand.spv) | bin | 5548 -> 5548 bytes | |||
| -rw-r--r-- | demos/compute/vert.spv (renamed from Rend/demos/compute/vert.spv) | bin | 3704 -> 3704 bytes | |||
| -rwxr-xr-x | demos/demo | bin | 0 -> 60352 bytes | |||
| -rw-r--r-- | demos/demo.c (renamed from Peak/demos/demo.c) | 2 | ||||
| -rw-r--r-- | demos/demo.html (renamed from Peak/demos/demo.html) | 0 | ||||
| -rw-r--r-- | demos/demo.js (renamed from Peak/demos/demo.js) | 66 | ||||
| -rwxr-xr-x | demos/demo.wasm | bin | 0 -> 31763 bytes | |||
| -rwxr-xr-x | demos/demo_run | bin | 0 -> 60592 bytes | |||
| -rw-r--r-- | demos/demo_run.c (renamed from Peak/demos/demo_run.c) | 2 | ||||
| -rw-r--r-- | demos/rend_compute.c (renamed from Rend/demos/rend_compute.c) | 67 | ||||
| -rw-r--r-- | demos/rend_compute.slang (renamed from Rend/demos/rend_compute.slang) | 0 | ||||
| -rw-r--r-- | demos/rend_teapot.c | 242 | ||||
| -rw-r--r-- | demos/teapot.h (renamed from Rend/demos/teapot.h) | 0 | ||||
| -rw-r--r-- | demos/teapot/basic.frag.spv | bin | 0 -> 764 bytes | |||
| -rw-r--r-- | demos/teapot/basic.vert.spv | bin | 0 -> 1008 bytes | |||
| -rwxr-xr-x | demos/teapot/rend_teapot | bin | 0 -> 849944 bytes | |||
| -rw-r--r-- | demos/teapot/shaders/basic.slang (renamed from Rend/demos/teapot/shaders/basic.slang) | 0 | ||||
| -rw-r--r-- | demos/teapot/shaders/texture.slang (renamed from Rend/demos/teapot/shaders/texture.slang) | 0 | ||||
| -rw-r--r-- | demos/teapot/stb_image.h (renamed from Rend/demos/teapot/stb_image.h) | 0 | ||||
| -rw-r--r-- | demos/teapot/texture.frag.spv (renamed from Rend/demos/teapot/texture.frag.spv) | bin | 684 -> 680 bytes | |||
| -rw-r--r-- | demos/teapot/texture.vert.spv (renamed from Rend/demos/teapot/texture.vert.spv) | bin | 844 -> 844 bytes | |||
| -rw-r--r-- | demos/teapot/wal.jpg (renamed from Rend/demos/teapot/wal.jpg) | bin | 21110 -> 21110 bytes |
71 files changed, 4021 insertions, 8827 deletions
diff --git a/Draw/draw.h b/Draw/draw.h index fded875..73b2b3e 100644 --- a/Draw/draw.h +++ b/Draw/draw.h @@ -5,178 +5,239 @@ #ifndef DRAW_H #define DRAW_H +/* CHANGE LOG + * 0.0.0 - @vasco - prototyping + */ + +#include <math.h> #include <stdint.h> -#include <string.h> #include <stdlib.h> -#include <math.h> +#include <string.h> + +#define DRAW__MIN(a, b) ((a) < (b) ? (a) : (b)) +#define DRAW__MAX(a, b) ((a) > (b) ? (a) : (b)) typedef struct Draw { - uint32_t *px; - int w, h; - int cx, cy, cw, ch; + uint32_t *px; + int w, h; + int cx, cy, cw, ch; } Draw; -#define DRAW__MIN(a, b) ((a) < (b) ? (a) : (b)) -#define DRAW__MAX(a, b) ((a) > (b) ? (a) : (b)) +static void draw_begin(Draw *d, uint32_t *px, int w, int h); +static void draw_clip(Draw *d, int x, int y, int w, int h); +static void draw_clip_reset(Draw *d); +static void draw_pixel(Draw *d, int x, int y, uint32_t color); +static uint32_t draw__lerp(uint32_t c0, uint32_t c1, float t); +static float draw__cross(float ax, float ay, float bx, float by, float cx, float cy); +static void draw_blit(Draw *d, int x, int y, const uint32_t *src, int sw, int sh); +static void draw_rect(Draw *d, int x, int y, int w, int h, uint32_t color); +static void draw_rect_gradient(Draw *d, int x, int y, int w, int h, uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3); +static void draw_tri(Draw *d, int x0, int y0, int x1, int y1, int x2, int y2, uint32_t c0, uint32_t c1, uint32_t c2); +static void draw_line(Draw *d, int x0, int y0, int x1, int y1, int width, uint32_t c0, uint32_t c1); -static inline void +void draw_begin(Draw *d, uint32_t *px, int w, int h) { - d->px = px; - d->w = w; - d->h = h; - d->cx = 0; - d->cy = 0; - d->cw = w; - d->ch = h; + d->px = px; + d->w = w; + d->h = h; + d->cx = 0; + d->cy = 0; + d->cw = w; + d->ch = h; } -static inline void +void draw_clip(Draw *d, int x, int y, int w, int h) { - d->cx = x; - d->cy = y; - d->cw = w; - d->ch = h; + d->cx = x; + d->cy = y; + d->cw = w; + d->ch = h; } -static inline void +void draw_clip_reset(Draw *d) { - d->cx = 0; - d->cy = 0; - d->cw = d->w; - d->ch = d->h; + d->cx = 0; + d->cy = 0; + d->cw = d->w; + d->ch = d->h; } -static inline void +void draw_pixel(Draw *d, int x, int y, uint32_t color) { - if (x < d->cx || x >= d->cx + d->cw || y < d->cy || y >= d->cy + d->ch) return; - if (x < 0 || x >= d->w || y < 0 || y >= d->h) return; - d->px[y * d->w + x] = color; + if (x < d->cx || x >= d->cx + d->cw || y < d->cy || y >= d->cy + d->ch) + return; + if (x < 0 || x >= d->w || y < 0 || y >= d->h) + return; + d->px[y * d->w + x] = color; } -static inline uint32_t +uint32_t draw__lerp(uint32_t c0, uint32_t c1, float t) { - if (t <= 0.0f) return c0; - if (t >= 1.0f) return c1; - uint32_t a0 = (c0 >> 24) & 0xFF, a1 = (c1 >> 24) & 0xFF; - uint32_t r0 = (c0 >> 16) & 0xFF, r1 = (c1 >> 16) & 0xFF; - uint32_t g0 = (c0 >> 8) & 0xFF, g1 = (c1 >> 8) & 0xFF; - uint32_t b0 = c0 & 0xFF, b1 = c1 & 0xFF; - uint32_t a = (uint32_t)(a0 + t * ((float)a1 - (float)a0)); - uint32_t r = (uint32_t)(r0 + t * ((float)r1 - (float)r0)); - uint32_t g = (uint32_t)(g0 + t * ((float)g1 - (float)g0)); - uint32_t b = (uint32_t)(b0 + t * ((float)b1 - (float)b0)); - return (a << 24) | (r << 16) | (g << 8) | b; + uint32_t a0, a1, r0, r1, g0, g1, b0, b1; + uint32_t a, r, g, b; + if (t <= 0.0f) + return c0; + if (t >= 1.0f) + return c1; + a0 = (c0 >> 24) & 0xFF; + a1 = (c1 >> 24) & 0xFF; + r0 = (c0 >> 16) & 0xFF; + r1 = (c1 >> 16) & 0xFF; + g0 = (c0 >> 8) & 0xFF; + g1 = (c1 >> 8) & 0xFF; + b0 = c0 & 0xFF; + b1 = c1 & 0xFF; + a = (uint32_t)(a0 + t * ((float)a1 - (float)a0)); + r = (uint32_t)(r0 + t * ((float)r1 - (float)r0)); + g = (uint32_t)(g0 + t * ((float)g1 - (float)g0)); + b = (uint32_t)(b0 + t * ((float)b1 - (float)b0)); + return (a << 24) | (r << 16) | (g << 8) | b; } -static inline float +float draw__cross(float ax, float ay, float bx, float by, float cx, float cy) { - return (cx - ax) * (by - ay) - (cy - ay) * (bx - ax); + return (cx - ax) * (by - ay) - (cy - ay) * (bx - ax); } -static inline void +void draw_blit(Draw *d, int x, int y, const uint32_t *src, int sw, int sh) { - for (int row = 0; row < sh; ++row) { - int ty = y + row; - if (ty < d->cy || ty >= d->cy + d->ch || ty < 0 || ty >= d->h) continue; - int x0 = DRAW__MAX(x, d->cx); - int x1 = DRAW__MIN(x + sw, d->cx + d->cw); - if (x0 >= x1) continue; - memcpy(&d->px[ty * d->w + x0], &src[row * sw + (x0 - x)], (size_t)(x1 - x0) * sizeof(uint32_t)); - } + int row, ty, x0, x1; + for (row = 0; row < sh; ++row) { + ty = y + row; + if (ty < d->cy || ty >= d->cy + d->ch || ty < 0 || ty >= d->h) + continue; + x0 = DRAW__MAX(x, d->cx); + x1 = DRAW__MIN(x + sw, d->cx + d->cw); + if (x0 >= x1) + continue; + memcpy(&d->px[ty * d->w + x0], &src[row * sw + (x0 - x)], (size_t)(x1 - x0) * sizeof(uint32_t)); + } } -static inline void +void draw_rect(Draw *d, int x, int y, int w, int h, uint32_t color) { - int x0 = DRAW__MAX(x, d->cx); - int y0 = DRAW__MAX(y, d->cy); - int x1 = DRAW__MIN(x + w, d->cx + d->cw); - int y1 = DRAW__MIN(y + h, d->cy + d->ch); - for (int py = y0; py < y1; ++py) { - if (py < 0 || py >= d->h) continue; - for (int px = x0; px < x1; ++px) { - if (px < 0 || px >= d->w) continue; - d->px[py * d->w + px] = color; - } - } + int x0, y0, x1, y1; + int py, px; + x0 = DRAW__MAX(x, d->cx); + y0 = DRAW__MAX(y, d->cy); + x1 = DRAW__MIN(x + w, d->cx + d->cw); + y1 = DRAW__MIN(y + h, d->cy + d->ch); + for (py = y0; py < y1; ++py) { + if (py < 0 || py >= d->h) + continue; + for (px = x0; px < x1; ++px) { + if (px < 0 || px >= d->w) + continue; + d->px[py * d->w + px] = color; + } + } } -static inline void +void draw_rect_gradient(Draw *d, int x, int y, int w, int h, uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3) { - for (int ry = 0; ry < h; ++ry) { - float ty = (h > 1) ? (float)ry / (float)(h - 1) : 0.0f; - uint32_t left = draw__lerp(c0, c3, ty); - uint32_t right = draw__lerp(c1, c2, ty); - for (int rx = 0; rx < w; ++rx) { - float tx = (w > 1) ? (float)rx / (float)(w - 1) : 0.0f; - draw_pixel(d, x + rx, y + ry, draw__lerp(left, right, tx)); - } - } + int ry, rx; + float ty, tx; + uint32_t left, right; + for (ry = 0; ry < h; ++ry) { + ty = (h > 1) ? (float)ry / (float)(h - 1) : 0.0f; + left = draw__lerp(c0, c3, ty); + right = draw__lerp(c1, c2, ty); + for (rx = 0; rx < w; ++rx) { + tx = (w > 1) ? (float)rx / (float)(w - 1) : 0.0f; + draw_pixel(d, x + rx, y + ry, draw__lerp(left, right, tx)); + } + } } -static inline void +void draw_tri(Draw *d, int x0, int y0, int x1, int y1, int x2, int y2, uint32_t c0, uint32_t c1, uint32_t c2) { - int min_x = DRAW__MIN(x0, DRAW__MIN(x1, x2)); - int max_x = DRAW__MAX(x0, DRAW__MAX(x1, x2)); - int min_y = DRAW__MIN(y0, DRAW__MIN(y1, y2)); - int max_y = DRAW__MAX(y0, DRAW__MAX(y1, y2)); - float area = draw__cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2); - if (area == 0.0f) return; - int solid = (c0 == c1 && c1 == c2); - for (int y = min_y; y <= max_y; ++y) { - for (int x = min_x; x <= max_x; ++x) { - float w0 = draw__cross((float)x1, (float)y1, (float)x2, (float)y2, (float)x, (float)y); - float w1 = draw__cross((float)x2, (float)y2, (float)x0, (float)y0, (float)x, (float)y); - float w2 = draw__cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x, (float)y); - if ((w0 >= 0 && w1 >= 0 && w2 >= 0) || (w0 <= 0 && w1 <= 0 && w2 <= 0)) { - uint32_t c = c0; - if (!solid) { - w0 /= area; - w1 /= area; - w2 /= area; - c = draw__lerp(c0, c1, w1 / (w0 + w1 + 1e-6f)); - c = draw__lerp(c, c2, w2); - } - draw_pixel(d, x, y, c); - } - } - } + int min_x, max_x, min_y, max_y; + int solid; + int x, y; + float area; + float w0, w1, w2; + uint32_t c; + min_x = DRAW__MIN(x0, DRAW__MIN(x1, x2)); + max_x = DRAW__MAX(x0, DRAW__MAX(x1, x2)); + min_y = DRAW__MIN(y0, DRAW__MIN(y1, y2)); + max_y = DRAW__MAX(y0, DRAW__MAX(y1, y2)); + area = draw__cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2); + if (area == 0.0f) + return; + solid = (c0 == c1 && c1 == c2); + for (y = min_y; y <= max_y; ++y) { + for (x = min_x; x <= max_x; ++x) { + w0 = draw__cross((float)x1, (float)y1, (float)x2, (float)y2, (float)x, (float)y); + w1 = draw__cross((float)x2, (float)y2, (float)x0, (float)y0, (float)x, (float)y); + w2 = draw__cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x, (float)y); + if ((w0 >= 0 && w1 >= 0 && w2 >= 0) || (w0 <= 0 && w1 <= 0 && w2 <= 0)) { + c = c0; + if (!solid) { + w0 /= area; + w1 /= area; + w2 /= area; + c = draw__lerp(c0, c1, w1 / (w0 + w1 + 1e-6f)); + c = draw__lerp(c, c2, w2); + } + draw_pixel(d, x, y, c); + } + } + } } -static inline void +void draw_line(Draw *d, int x0, int y0, int x1, int y1, int width, uint32_t c0, uint32_t c1) { - float total = 0.0f; - int grad = (c0 != c1); - if (grad) total = sqrtf((float)((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0))); - int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; - int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; - int err = dx + dy; - int ox0 = x0, oy0 = y0; - int hw = width / 2; - for (;;) { - uint32_t c = c0; - if (grad) { - float cd = sqrtf((float)((x0 - ox0) * (x0 - ox0) + (y0 - oy0) * (y0 - oy0))); - c = draw__lerp(c0, c1, total > 0.0f ? cd / total : 0.0f); - } - for (int ox = -hw; ox <= hw; ++ox) - for (int oy = -hw; oy <= hw; ++oy) - draw_pixel(d, x0 + ox, y0 + oy, c); - if (x0 == x1 && y0 == y1) break; - int e2 = 2 * err; - if (e2 >= dy) { err += dy; x0 += sx; } - if (e2 <= dx) { err += dx; y0 += sy; } - } + float total; + int grad; + int dx, sx, dy, sy, err; + int ox0, oy0, hw; + int ox, oy, e2; + uint32_t c; + total = 0.0f; + grad = (c0 != c1); + if (grad) + total = sqrtf((float)((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0))); + dx = abs(x1 - x0); + sx = x0 < x1 ? 1 : -1; + dy = -abs(y1 - y0); + sy = y0 < y1 ? 1 : -1; + err = dx + dy; + ox0 = x0; + oy0 = y0; + hw = width / 2; + for (;;) { + c = c0; + if (grad) { + float cd; + cd = sqrtf((float)((x0 - ox0) * (x0 - ox0) + (y0 - oy0) * (y0 - oy0))); + c = draw__lerp(c0, c1, total > 0.0f ? cd / total : 0.0f); + } + for (ox = -hw; ox <= hw; ++ox) + for (oy = -hw; oy <= hw; ++oy) + draw_pixel(d, x0 + ox, y0 + oy, c); + if (x0 == x1 && y0 == y1) + break; + e2 = 2 * err; + if (e2 >= dy) { + err += dy; + x0 += sx; + } + if (e2 <= dx) { + err += dx; + y0 += sy; + } + } } #endif /* DRAW_H */ diff --git a/Fuse/demos/fuse.slang b/Fuse/demos/fuse.slang deleted file mode 100644 index 837a3dc..0000000 --- a/Fuse/demos/fuse.slang +++ /dev/null @@ -1,31 +0,0 @@ -struct VertexInput { - [[vk::location(0)]] float2 pos : POSITION; - [[vk::location(1)]] float4 col : COLOR; -}; - -struct VertexOutput { - float4 col : COLOR; - float4 pos : SV_Position; -}; - -struct UBO { - float2 viewport; -}; - -[[vk::binding(0, 0)]] -ConstantBuffer<UBO> ubo; - -[shader("vertex")] -VertexOutput vertMain(VertexInput input) -{ - VertexOutput output; - output.col = input.col; - output.pos = float4(input.pos*2.0/ubo.viewport - 1.0, 0.0, 1.0); - return output; -} - -[shader("fragment")] -float4 fragMain(VertexOutput input) : SV_Target -{ - return input.col; -} diff --git a/Fuse/demos/fuse_demo.c b/Fuse/demos/fuse_demo.c deleted file mode 100644 index 60128bd..0000000 --- a/Fuse/demos/fuse_demo.c +++ /dev/null @@ -1,280 +0,0 @@ -#include "rend.h" -#include "rend.c" -#include "fuse.h" -#include "fuse.c" -#include <stddef.h> -#include <stdio.h> - -/* has to be included last because rend will ask for certain modules */ -#define PODIUM_IMPLEMENTATION -#include "podium.h" - -typedef struct { float x, y; } FuseVec2; -typedef struct { float x, y, z, w; } FuseVec4; - -#pragma pack(push, 1) -typedef struct { - FuseVec2 pos; - FuseVec4 color; -} FuseVertex; -#pragma pack(pop) - -static FuseVertex fuse_renderer_data[1024]; -static size_t fuse_renderer_idx = 0; - -struct data_t { float offset_x; float padding = 0 }; -struct data_t button_data = { .offset_x = 0, .padding = 0}; - -void stretch_button_op(void *data) { - struct data_t* ptr = (struct data_t*) data; - data->offset_x += 10; - data->padding += 10; -} - - -static inline FuseVec4 -fuse__unpack_color(uint32_t c) -{ - FuseVec4 v; - v.x = (float) ((c >> 24) & 0xFF) / 255.0f; // Red - v.y = (float) ((c >> 16) & 0xFF) / 255.0f; // Green - v.z = (float) ((c >> 8) & 0xFF) / 255.0f; // Blue - v.w = (float) ( c & 0xFF) / 255.0f; // Alpha - return v; -} - -void fuse_handler(FuseCanvas canvas, FuseCmd *cmd, void *data) { - switch (cmd->type) { - case FUSE_CMD_RECT: - FuseRect rect = cmd->rect; - FuseVec4 color = fuse__unpack_color(rect.color); - FuseVertex c0 = { - .pos = { rect.x, rect.y }, - .color = color - }; - - FuseVertex c1 = { - .pos = { rect.x + rect.w, rect.y }, - .color = color - }; - - FuseVertex c2 = { - .pos = { rect.x + rect.w, rect.y + rect.h }, - .color = color - }; - - FuseVertex c3 = { - .pos = { rect.x, rect.y + rect.h }, - .color = color - }; - - fuse_renderer_data[fuse_renderer_idx++] = c0; - fuse_renderer_data[fuse_renderer_idx++] = c1; - fuse_renderer_data[fuse_renderer_idx++] = c2; - - fuse_renderer_data[fuse_renderer_idx++] = c2; - fuse_renderer_data[fuse_renderer_idx++] = c3; - fuse_renderer_data[fuse_renderer_idx++] = c0; - break; - } -} - -#define SCREEN_WIDTH 800 -#define SCREEN_HEIGHT 600 - -int main() { - - P_Window window; - if (!p_window_open(&window, 800, 600, "fuse_demo")) { - PFATAL("Failed to open window!"); - return 1; - } - - if (!rend_init(REND_BACKEND_AUTO)) { - PFATAL("Failed to initialize backend!"); - return 1; - } - - RendRenderer renderer = rend_renderer_create(&window, true); - RendShader vert = rend_shader_from_spv_path(REND_SHADER_VERTEX, "fuse.vert.spv"); - RendShader frag = rend_shader_from_spv_path(REND_SHADER_FRAG, "fuse.frag.spv"); - - RendPipelineConfig ui_cfg = rend_pipeline_config_create( - REND_TOPOLOGY_TRIANGLE_LIST, - REND_POLYGON_MODE_FILL, - REND_CULL_MODE_BACK); - rend_pipeline_config_depth_test(ui_cfg, false); - - /* configure vertex input */ - int b0 = rend_pipeline_config_vertex_binding_add(ui_cfg, sizeof (FuseVertex), REND_INPUT_RATE_VERTEX); - rend_pipeline_config_vertex_attribute_add(ui_cfg, b0, 0, REND_FORMAT_R32G32_SFLOAT, offsetof(FuseVertex, pos)); - rend_pipeline_config_vertex_attribute_add(ui_cfg, b0, 1, REND_FORMAT_R32G32B32A32_SFLOAT, offsetof(FuseVertex, color)); - rend_pipeline_config_resource_binding_add(ui_cfg, 0, 0, REND_BUFFER_UNIFORM, REND_SHADER_VERTEX, 1); - - /* add the shaders */ - rend_pipeline_config_shader_add(ui_cfg, vert); - rend_pipeline_config_shader_add(ui_cfg, frag); - - RendPipeline ui_pipeline = rend_pipeline_create(renderer, ui_cfg); - - RendResourceSet ui_resources = rend_resource_set_create(renderer, ui_pipeline, 0); - - FuseCanvas canvas = fuse_canvas_create(1024, NULL); - - u64 frame_t = NANOS_PER_SEC / 60; - - bool running = true; - P_Event ev; - float pev = 0, px = 0, py = 0; - - float nob = 0.3; - - float target_screen[2] = { SCREEN_WIDTH, SCREEN_HEIGHT }; - rend_resource_set_write_buffer(ui_resources, 0, REND_BUFFER_UNIFORM, target_screen, 2 * sizeof *target_screen, 0); - - float real_screen_width = 0; - float real_screen_heigth = 0; - - enum UI_State { - UI_START_MENU, - UI_OPTIONS, - }; - - int ui_state = UI_START_MENU; - - while (running) { - - u64 start = p_get_time(); - - pev = 0; - /* event loop */ - while (p_window_poll_event(&window, &ev)) { - if (ev.type == P_EVENT_WINDOW_CLOSE) { - running = false; - break; - } - - switch (ev.type) { - case P_EVENT_WINDOW_RESIZE: - real_screen_width = ev.resize.width; - real_screen_heigth = ev.resize.height; - break; - case P_EVENT_POINTER: - px = ev.pointer.x; - py = ev.pointer.y; - switch (ev.pointer.state) { - case P_POINTER_PRESSED: - if (ev.pointer.button == 1 || ev.pointer.button == 0) - pev = FUSE_POINTER_PRESSED; - break; - case P_POINTER_RELEASED: - if (ev.pointer.button == 1 || ev.pointer.button == 0) - pev = FUSE_POINTER_RELEASED; - break; - default: - case P_POINTER_MOVED: - break; - } - - fuse_canvas_pointer(pev, px * SCREEN_WIDTH / real_screen_width, py * SCREEN_HEIGHT / real_screen_heigth); - break; - } - } - - /* Clears the Screen */ - fuse_canvas_clear(canvas, 0x000000); - - /* Inside a div, children no longer have fixed positions, - * instead x, y represent an offset so that they can still be animated! - * Width and height represent min width and min height instead! */ - if (ui_state == UI_START_MENU) { - // NOTE: brackets for legibility only - FUSE_LAYOUT_DIRECTION(FUSE_DIRECTION_COLUMN); - fuse_div_begin(0, 0, SCREEN_WIDTH / 4, SCREEN_HEIGHT, 0xFFFFFFaa); - { - - fuse_hovered(stretch_button_op, data); - - if (fuse_button_text("Start New Game", button_data.offset_x, 0, 100, 0, 0xFF0000FF, 0x000000FF)) { - printf("start new game"); - } - - - if (fuse_button_text("Options", 0, 0, 100, 0, 0xFF0000FF, 0x000000FF)) { - ui_state = UI_OPTIONS; - } - - - if (fuse_button_text("Credits", 0, 0, 100, 0, 0xFF0000FF, 0x000000FF)) { - printf("awesome credits"); - } - - if (fuse_button_text("Quit Game")) { - running = false; - break; - } - - fuse_hovered_clear(); - - } - fuse_div_end(); - - /* back to global layouting */ - fuse_3d_object(); - fuse_3d_point_at(); - - - } else if (ui_state == UI_OPTIONS) { - - /* Inside a flex the same x,y as offsets and width and height as minimums rules apply - * The difference is that a flex is flexible, by default it does not have a fixed size - * but you may specify minimums and maximus for it to work around. - */ - - FUSE( - .max_width = 300, - .max_height = 600, - .direction = FUSE_DIRECTION_COLUMN, - .gap = 32 - ); - - fuse_flex_begin(0, 0, SCREEN_WIDTH / 4, SCREEN_HEIGHT, 0xFFFFFFaa); - { - if (fuse_checkbox_text("VSYNC", &vsync)) { - vsync = ~vsync; - } - } - } - - fuse_slider(100, 200, 300, 600, 0xFFFF00FF, 0xFF0000FF, &nob); - - fuse_canvas_draw(canvas, fuse_handler, NULL); - - /* call renderer */ - if (rend_renderer_frame_begin(renderer)) { - rend_pipeline_bind(ui_pipeline); - rend_resource_set_bind(ui_pipeline, 0, ui_resources); - - rend_pipeline_push_data(ui_pipeline, fuse_renderer_data, fuse_renderer_idx, sizeof *fuse_renderer_data); - rend_pipeline_draw(ui_pipeline); - - rend_renderer_frame_end(renderer, NULL); - } - - fuse_renderer_idx = 0; - - u64 elapsed = p_get_time() - start; - - /* the renderer should already be forcing vsync */ - if (elapsed < frame_t) - p_sleep_ns(frame_t - elapsed); - } - - - fuse_canvas_destroy(canvas); - - rend_quit(); - - p_window_close(&window); - return 0; -} diff --git a/Math/math.h b/Math/math.h new file mode 100644 index 0000000..ce46b12 --- /dev/null +++ b/Math/math.h @@ -0,0 +1,343 @@ +/* =========================================================================== + * MATH - Copyright @ Vasco Alves - See LICENSE at the end of file. + * + * Linear algebra and math for games. + * No types. I'm tired of everyone having a Vec3 type. + * Ops write in place. + * Speed over accuracy. + * + * TYPES: + * - vec2 = float[2] + * - vec3 = float[3] + * - mat4 = float[16] column-major + * A pointer that is not backed correctly but the correct amount of memory + * is undefined behaviour and misuse of the library. + * + * =========================================================================== */ + +#ifndef GUNSTACK_MATH_H +#define GUNSTACK_MATH_H + +#define MATH_MAJOR "0" +#define MATH_MINOR "0" +#define MATH_PATCH "0" + +/* CHANGE LOG + * 0.0.0 - @vasco - prototyping + */ + +#define MATH_PI 3.14159265358979323846f +#define MATH_PI2 (2.0f * MATH_PI) +#define MATH_PI_HALF (MATH_PI * 0.5f) +#define MATH_PI_POW2 (MATH_PI * MATH_PI) + +#define MATH static inline + +MATH void math_ceil(float *x); +MATH void math_sinf(float *x); +MATH void math_cosf(float *x); + +MATH void math_vec2f(float *v, float x, float y); +MATH void math_vec2f_add(float *a, const float *b); +MATH void math_vec2f_sub(float *a, const float *b); +MATH void math_vec2f_mult(float *a, float s); +MATH void math_vec2f_dot(float *out, const float *a, const float *b); +MATH void math_vec2f_cross(float *out, const float *a, const float *b); + +MATH void math_vec3f(float *v, float x, float y, float z); +MATH void math_vec3f_add(float *a, const float *b); +MATH void math_vec3f_sub(float *a, const float *b); +MATH void math_vec3f_mult(float *a, float s); +MATH void math_vec3f_dot(float *out, const float *a, const float *b); +MATH void math_vec3f_cross(float *a, const float *b); + +MATH void math_mat4_identity(float *m); +MATH void math_mat4_mul(float *a, const float *b); +MATH void math_mat4_translate(float *m, const float *v); +MATH void math_mat4_translate_by(float *m, const float *v); +MATH void math_mat4_rotate_x(float *m, float rad); +MATH void math_mat4_rotate_x_by(float *m, float rad); +MATH void math_mat4_rotate_y(float *m, float rad); +MATH void math_mat4_rotate_y_by(float *m, float rad); +MATH void math_mat4_rotate_z(float *m, float rad); +MATH void math_mat4_perspective(float *m, float fov_rad, float aspect, float near_z, float far_z); + +void +math_ceil(float *x) +{ + int base = (int)*x; + *x = (*x > (float)base) ? (float)(base + 1) : (float)base; +} + +void +math_sinf(float *x) +{ + float y, B, C, P; + while (*x > MATH_PI) + *x -= MATH_PI2; + while (*x < -MATH_PI) + *x += MATH_PI2; + B = 4.0f / MATH_PI; + C = -4.0f / MATH_PI_POW2; + y = B * *x + C * *x * (*x < 0 ? -*x : *x); + P = 0.225f; + *x = P * (y * (y < 0 ? -y : y) - y) + y; +} + +void +math_cosf(float *x) +{ + *x += MATH_PI_HALF; + math_sinf(x); +} + +void +math_vec2f(float *v, float x, float y) +{ + v[0] = x; + v[1] = y; +} + +void +math_vec2f_add(float *a, const float *b) +{ + a[0] += b[0]; + a[1] += b[1]; +} + +void +math_vec2f_sub(float *a, const float *b) +{ + a[0] -= b[0]; + a[1] -= b[1]; +} + +void +math_vec2f_mult(float *a, float s) +{ + a[0] *= s; + a[1] *= s; +} + +void +math_vec2f_dot(float *out, const float *a, const float *b) +{ + *out = a[0] * b[0] + a[1] * b[1]; +} + +void +math_vec2f_cross(float *out, const float *a, const float *b) +{ + *out = a[0] * b[1] - a[1] * b[0]; +} + +void +math_vec3f(float *v, float x, float y, float z) +{ + v[0] = x; + v[1] = y; + v[2] = z; +} + +void +math_vec3f_add(float *a, const float *b) +{ + a[0] += b[0]; + a[1] += b[1]; + a[2] += b[2]; +} + +void +math_vec3f_sub(float *a, const float *b) +{ + a[0] -= b[0]; + a[1] -= b[1]; + a[2] -= b[2]; +} + +void +math_vec3f_mult(float *a, float s) +{ + a[0] *= s; + a[1] *= s; + a[2] *= s; +} + +void +math_vec3f_dot(float *out, const float *a, const float *b) +{ + *out = a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} + +void +math_vec3f_cross(float *a, const float *b) +{ + float x = a[1] * b[2] - a[2] * b[1]; + float y = a[2] * b[0] - a[0] * b[2]; + float z = a[0] * b[1] - a[1] * b[0]; + a[0] = x; + a[1] = y; + a[2] = z; +} + +void +math_mat4_identity(float *m) +{ + int i; + for (i = 0; i < 16; i++) + m[i] = 0.0f; + m[0] = m[5] = m[10] = m[15] = 1.0f; +} + +void +math_mat4_mul(float *a, const float *b) +{ + float res[16]; + int col, row, k; + for (col = 0; col < 4; ++col) { + for (row = 0; row < 4; ++row) { + float sum = 0.0f; + for (k = 0; k < 4; ++k) + sum += a[k * 4 + row] * b[col * 4 + k]; + res[col * 4 + row] = sum; + } + } + for (k = 0; k < 16; k++) + a[k] = res[k]; +} + +void +math_mat4_translate(float *m, const float *v) +{ + math_mat4_identity(m); + m[12] = v[0]; + m[13] = v[1]; + m[14] = v[2]; +} + +void +math_mat4_translate_by(float *m, const float *v) +{ + m[12] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12]; + m[13] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13]; + m[14] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14]; + m[15] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15]; +} + +void +math_mat4_rotate_x(float *m, float rad) +{ + float c = rad, s = rad; + math_cosf(&c); + math_sinf(&s); + math_mat4_identity(m); + m[5] = c; + m[6] = s; + m[9] = -s; + m[10] = c; +} + +void +math_mat4_rotate_x_by(float *m, float rad) +{ + float r[16]; + math_mat4_rotate_x(r, rad); + math_mat4_mul(m, r); +} + +void +math_mat4_rotate_y(float *m, float rad) +{ + float c = rad, s = rad; + math_cosf(&c); + math_sinf(&s); + math_mat4_identity(m); + m[0] = c; + m[2] = -s; + m[8] = s; + m[10] = c; +} + +void +math_mat4_rotate_y_by(float *m, float rad) +{ + float r[16]; + math_mat4_rotate_y(r, rad); + math_mat4_mul(m, r); +} + +void +math_mat4_rotate_z(float *m, float rad) +{ + float c = rad, s = rad; + math_cosf(&c); + math_sinf(&s); + math_mat4_identity(m); + m[0] = c; + m[1] = s; + m[4] = -s; + m[5] = c; +} + +void +math_mat4_perspective(float *m, float fov_rad, float aspect, float near_z, float far_z) +{ + float half = fov_rad * 0.5f; + float c = half, s = half; + float tan_half_fov; + int i; + math_sinf(&s); + math_cosf(&c); + tan_half_fov = s / c; + for (i = 0; i < 16; i++) + m[i] = 0.0f; + m[0] = 1.0f / (aspect * tan_half_fov); + m[5] = 1.0f / tan_half_fov; + m[10] = far_z / (near_z - far_z); + m[11] = -1.0f; + m[14] = (near_z * far_z) / (near_z - far_z); +} + +#endif /* GUNSTACK_MATH_H */ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2026 Vasco Alves +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/Peak/build.c b/Peak/build.c deleted file mode 100644 index c3b20b9..0000000 --- a/Peak/build.c +++ /dev/null @@ -1,39 +0,0 @@ -#define POOF_IMPLEMENTATION -#include "../Poof/poof.h" - -int main(int argc, char **argv) { - POOF_GO_REBUILD_URSELF(argc, argv); - - Poof_Cmd cmd = {0}; - poof_cmd_append(&cmd, "python", "../bundle_header.py", "src/peak_header.h"); - if (!poof_cmd_run(&cmd)) return 1; - - poof_copy_file("src/peak_header.h.out", "peak.h"); - - Poof_CC cc = {0}; - poof_cc_init(&cc, POOF_CC_GCC | POOF_CC_CLANG, POOF_TARGET_HOST); - cc.debug_mode = true; - cc.optimization = POOF_O0; - cc.output = "demos/demo"; - poof_cc_append(&cc.inputs, "demos/demo.c"); - poof_cc_append(&cc.extra_flags, "-std=c99", "-Wall", "-Wno-deprecated-declarations"); - poof_cc_append(&cc.libs, "m"); - if (!poof_cc_run(&cc)) return 1; - - cc.debug_mode = true; - cc.optimization = POOF_O0; - cc.output = "demos/demo_run"; - poof_cc_append(&cc.inputs, "demos/demo_run.c"); - poof_cc_append(&cc.extra_flags, "-std=c99", "-Wall", "-Wno-deprecated-declarations"); - poof_cc_append(&cc.libs, "m"); - if (!poof_cc_run(&cc)) return 1; - - poof_cmd_clear(&cmd); - poof_cmd_append(&cmd, "emcc", "demos/demo_run.c", "-o", "demos/demo.js", - "-std=c99", "-Wall", "-Wno-deprecated-declarations", - "-sALLOW_MEMORY_GROWTH=1", "-sENVIRONMENT=web"); - if (!poof_cmd_run(&cmd)) return 1; - - return 0; -} - diff --git a/Peak/demos/demo b/Peak/demos/demo Binary files differdeleted file mode 100755 index 8f72b0a..0000000 --- a/Peak/demos/demo +++ /dev/null diff --git a/Peak/demos/demo.wasm b/Peak/demos/demo.wasm Binary files differdeleted file mode 100755 index e7e428b..0000000 --- a/Peak/demos/demo.wasm +++ /dev/null diff --git a/Peak/demos/demo_run b/Peak/demos/demo_run Binary files differdeleted file mode 100755 index 7a526c1..0000000 --- a/Peak/demos/demo_run +++ /dev/null diff --git a/Peak/peak.h b/Peak/peak.h index 97bb0a3..f5cc83b 100644 --- a/Peak/peak.h +++ b/Peak/peak.h @@ -10,17 +10,24 @@ #define PEAK_H #define PEAK_MAJOR "0" -#define PEAK_MINOR "1" -#define PEAK_PATCH "1" +#define PEAK_MINOR "5" +#define PEAK_PATCH "2" /* CHANGE LOG * 0.0.0 - @vasco - prototyping * 0.1.0 - @vasco - linux x11 that automagically loads X11 DLL * 0.1.1 - @vasco - fixed event handling on linux * 0.1.2 - @vasco - better handling of windows, if multiple windows becomes necessary in the future - * 0.2.0 - @vasco - win32 + * 0.2.0 - @vasco - multiple windows, major-ish API changes. + * 0.3.0 - @vasco - web via emscripten + * 0.4.0 - @vasco - win32 + * 0.5.0 - @vasco - audio start/stop, s16le pull callback + * 0.5.1 - @vasco - log, file, time, vulkan extensions + * 0.5.2 - @vasco - vulkan surface from window */ +#define NANOS_PER_SEC 1000000000ull + #include <assert.h> #include <stdint.h> #include <stddef.h> @@ -73,6 +80,62 @@ #define PEAK extern #endif +typedef enum PeakLogLevel { + P_LOG_LEVEL_FATAL = 0, + P_LOG_LEVEL_ERROR, + P_LOG_LEVEL_WARN, + P_LOG_LEVEL_INFO, + P_LOG_LEVEL_DEBUG, + P_LOG_LEVEL_TRACE, + P_COUNT_LOG_LEVEL +} PeakLogLevel; + +#define P_PREFIX_LEN 7 +static const char *p_prefix[P_COUNT_LOG_LEVEL] = { + [P_LOG_LEVEL_FATAL] = "[FATAL]", + [P_LOG_LEVEL_ERROR] = "[ERROR]", + [P_LOG_LEVEL_WARN] = "[WARNI]", + [P_LOG_LEVEL_INFO] = "[INFOR]", + [P_LOG_LEVEL_DEBUG] = "[DEBUG]", + [P_LOG_LEVEL_TRACE] = "[TRACE]", +}; + +#ifndef P_LOG_WARN_ENABLED +#define P_LOG_WARN_ENABLED 1 +#endif +#ifndef P_LOG_INFO_ENABLED +#define P_LOG_INFO_ENABLED 1 +#endif +#ifndef P_LOG_DEBUG_ENABLED +#define P_LOG_DEBUG_ENABLED 0 +#endif +#ifndef P_LOG_TRACE_ENABLED +#define P_LOG_TRACE_ENABLED 0 +#endif + +#define PFATAL(message, ...) peak_log_printf(P_LOG_LEVEL_FATAL, message, ##__VA_ARGS__) +#define PERROR(message, ...) peak_log_printf(P_LOG_LEVEL_ERROR, message, ##__VA_ARGS__) +#if P_LOG_WARN_ENABLED == 1 +#define PWARN(message, ...) peak_log_printf(P_LOG_LEVEL_WARN, message, ##__VA_ARGS__) +#else +#define PWARN(message, ...) +#endif +#if P_LOG_INFO_ENABLED == 1 +#define PINFO(message, ...) peak_log_printf(P_LOG_LEVEL_INFO, message, ##__VA_ARGS__) +#else +#define PINFO(message, ...) +#endif +#if P_LOG_DEBUG_ENABLED == 1 +#define PDEBUG(message, ...) peak_log_printf(P_LOG_LEVEL_DEBUG, message, ##__VA_ARGS__) +#else +#define PDEBUG(message, ...) +#endif +#if P_LOG_TRACE_ENABLED == 1 +#define PTRACE(message, ...) peak_log_printf(P_LOG_LEVEL_TRACE, message, ##__VA_ARGS__) +#else +#define PTRACE(message, ...) +#endif + typedef enum { PEAK_KEYMOD_NONE = 0, PEAK_KEYMOD_ALT, @@ -128,22 +191,49 @@ typedef struct { typedef struct peak_window_internal_t PeakWindowInternal; typedef struct PeakWindow PeakWindow; -/* Public API */ +/* Initialize the platform. */ PEAK int peak_init(void); // Initialize platform context and load necessary DLLs. PEAK void peak_quit(void); // Close platform context. + +/* Do sexy stuff with the windows. */ PEAK PeakWindow peak_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags); // Open a window. PEAK void peak_window_close(PeakWindow *window); // Close a window. PEAK void peak_window_run(PeakWindow *win, int (*peak_tick)(PeakWindow *win, void *userdata), void *userdata); // Hijacking the main loop makes life easier on platforms like web PEAK int peak_window_epoll(PeakWindow *win, PeakEvent *ev); // Poll a window for events. PEAK uint32_t* peak_window_backbuffer(PeakWindow *win, size_t *width, size_t *height); // Get the windows backbuffer. -PEAK void peak_window_clear(PeakWindow *win, float r, float g, float b, float a); -PEAK void peak_window_present(PeakWindow *win); +PEAK void peak_window_clear(PeakWindow *win, float r, float g, float b, float a); // Clear window to color. +PEAK void peak_window_present(PeakWindow *win); // Present window backbuffer. + +/* Play some tunes. */ +PEAK int peak_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata); // Device pulls interleaved s16le +PEAK void peak_audio_stop(void); // Stop audio. + +/* Time */ +PEAK uint64_t peak_get_time(void); // Get time in nanoseconds. +PEAK void peak_sleep_ns(int64_t ns); // Sleep for nanoseconds!!! + +/* File */ +PEAK int peak_file_exists(const char *path); // Does this file exist? +PEAK void *peak_file_alloc(const char *path, unsigned long *buf_size); // Allocate an entire file. + +/* Graphics API bull... */ +PEAK const char **peak_vulkan_get_extensions(uint32_t *count); // Get vulkan extensions. +PEAK int peak_vulkan_create_surface(PeakWindow *win, void *instance, const void *allocator, void *out_surface); // Create a vulkan surface for a window. Needs PEAK_VULKAN. + +/* Debug your amazing code. */ +PEAK void peak_log_printf(PeakLogLevel level, const char *src, ...); // Printf with log level. +PEAK void *peak_debug_malloc_impl(size_t size, const char *file, int line, const char *func); // Malloc for debuggin. +PEAK void peak_debug_free_impl(void *ptr, const char *file, int line, const char *func); // Free for debugging. +PEAK void *peak_debug_realloc_impl(void *ptr, size_t size, const char *file, int line, const char *func); // Realloc for debugging. +PEAK void peak_debug_memory_report(void); // Report memory. #endif // PEAK_H #ifdef PEAK_IMPLEMENTATION #undef PEAK_IMPLEMENTATION +/* NOTE(vasco): Some platforms require an event queue + * while others have it built in. */ #if defined(PEAK_WIN32) || defined(PEAK_WEB) #define PEAK_Q 64 @@ -174,43 +264,68 @@ peak_q_pop(PeakQ *q, PeakEvent *ev) #if defined(PEAK_WIN32) /* --- Start of p_win32.c --- */ +/* + * Win32 window, input, StretchDIBits present, and waveOut audio. + * user32.dll, gdi32.dll, and winmm.dll are loaded at runtime. + * + * * 0.4.0 - @vasco - win32 + * * 0.5.0 - @vasco - audio start/stop, s16le pull callback + */ + #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +/* windows.h must precede mmsystem.h */ #include <windows.h> +#include <mmsystem.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#ifdef PEAK_VULKAN +#define VK_USE_PLATFORM_WIN32_KHR +#include <vulkan/vulkan.h> +#endif #define PEAK_WIN32_USER32 "user32.dll" #define PEAK_WIN32_GDI32 "gdi32.dll" +#define PEAK_WIN32_WINMM "winmm.dll" #define PEAK_WIN32_CLASS "PeakWindow" #define PEAK_WIN32_PROP "Peak" +#define PEAK_AUDIO_FRAMES 256 +#define PEAK_AUDIO_BUFFERS 2 #define PEAK_USER32_API(X) \ - X(RegisterClassExA, ATOM, WINAPI, (const WNDCLASSEXA *)) \ - X(UnregisterClassA, BOOL, WINAPI, (LPCSTR, HINSTANCE)) \ - X(CreateWindowExA, HWND, WINAPI, (DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)) \ - X(DestroyWindow, BOOL, WINAPI, (HWND)) \ - X(ShowWindow, BOOL, WINAPI, (HWND, int)) \ - X(GetDC, HDC, WINAPI, (HWND)) \ - X(ReleaseDC, int, WINAPI, (HWND, HDC)) \ - X(PeekMessageA, BOOL, WINAPI, (LPMSG, HWND, UINT, UINT, UINT)) \ - X(TranslateMessage, BOOL, WINAPI, (const MSG *)) \ - X(DispatchMessageA, LRESULT, WINAPI, (const MSG *)) \ - X(DefWindowProcA, LRESULT, WINAPI, (HWND, UINT, WPARAM, LPARAM)) \ - X(SetPropA, BOOL, WINAPI, (HWND, LPCSTR, HANDLE)) \ - X(GetPropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ - X(RemovePropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ - X(BeginPaint, HDC, WINAPI, (HWND, LPPAINTSTRUCT)) \ - X(EndPaint, BOOL, WINAPI, (HWND, const PAINTSTRUCT *)) \ - X(AdjustWindowRectEx, BOOL, WINAPI, (LPRECT, DWORD, BOOL, DWORD)) \ - X(LoadCursorA, HCURSOR, WINAPI, (HINSTANCE, LPCSTR)) \ - X(GetKeyState, SHORT, WINAPI, (int)) + X(RegisterClassExA, ATOM, WINAPI, (const WNDCLASSEXA *)) \ + X(UnregisterClassA, BOOL, WINAPI, (LPCSTR, HINSTANCE)) \ + X(CreateWindowExA, HWND, WINAPI, (DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)) \ + X(DestroyWindow, BOOL, WINAPI, (HWND)) \ + X(ShowWindow, BOOL, WINAPI, (HWND, int)) \ + X(GetDC, HDC, WINAPI, (HWND)) \ + X(ReleaseDC, int, WINAPI, (HWND, HDC)) \ + X(PeekMessageA, BOOL, WINAPI, (LPMSG, HWND, UINT, UINT, UINT)) \ + X(TranslateMessage, BOOL, WINAPI, (const MSG *)) \ + X(DispatchMessageA, LRESULT, WINAPI, (const MSG *)) \ + X(DefWindowProcA, LRESULT, WINAPI, (HWND, UINT, WPARAM, LPARAM)) \ + X(SetPropA, BOOL, WINAPI, (HWND, LPCSTR, HANDLE)) \ + X(GetPropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(RemovePropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(BeginPaint, HDC, WINAPI, (HWND, LPPAINTSTRUCT)) \ + X(EndPaint, BOOL, WINAPI, (HWND, const PAINTSTRUCT *)) \ + X(AdjustWindowRectEx, BOOL, WINAPI, (LPRECT, DWORD, BOOL, DWORD)) \ + X(LoadCursorA, HCURSOR, WINAPI, (HINSTANCE, LPCSTR)) \ + X(GetKeyState, SHORT, WINAPI, (int)) #define PEAK_GDI32_API(X) \ - X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD)) + X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD)) + +#define PEAK_WINMM_API(X) \ + X(waveOutOpen, MMRESULT, WINAPI, (LPHWAVEOUT, UINT, LPCWAVEFORMATEX, DWORD_PTR, DWORD_PTR, DWORD)) \ + X(waveOutClose, MMRESULT, WINAPI, (HWAVEOUT)) \ + X(waveOutPrepareHeader, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \ + X(waveOutUnprepareHeader, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \ + X(waveOutWrite, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \ + X(waveOutReset, MMRESULT, WINAPI, (HWAVEOUT)) typedef struct { #define X(name, ret, conv, args) ret (conv *name) args; @@ -225,6 +340,25 @@ typedef struct { } PeakGdi32Api; typedef struct { +#define X(name, ret, conv, args) ret (conv *name) args; + PEAK_WINMM_API(X) +#undef X +} PeakWinmmApi; + +typedef struct { + volatile int run; + HANDLE thread; + HANDLE event; + HWAVEOUT out; + WAVEHDR hdr[PEAK_AUDIO_BUFFERS]; + int16_t *pcm[PEAK_AUDIO_BUFFERS]; + uint32_t channels; + uint32_t bytes; + void (*fill)(int16_t *out, size_t frames, void *userdata); + void *userdata; +} PeakAudio; + +typedef struct { HMODULE user32; HMODULE gdi32; int class_reg; @@ -243,9 +377,28 @@ struct peak_window_internal_t { struct peak_win32_win *w; }; +static int peak_internal_user32_load(HMODULE handle); +static int peak_internal_gdi32_load(HMODULE handle); +static PeakKeyCode peak_internal_win32_key_map(WPARAM vk); +static PeakKeyMod peak_internal_win32_mod_map(void); +static int peak_internal_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height); +static LRESULT CALLBACK peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); +static int peak_platform_init(void); +static void peak_platform_quit(void); +static PeakWindowInternal peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags); +static void peak_platform_window_close(PeakWindowInternal *intern); +static uint32_t *peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height); +static void peak_platform_window_present(PeakWindowInternal *intern); +static bool peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev); +static int peak_internal_winmm_load(void); +static void peak_internal_win32_audio_fill(int i); +static DWORD WINAPI peak_internal_win32_audio_thread(LPVOID arg); + static PeakWin32 peak_win32; static PeakUser32Api peak_user32; static PeakGdi32Api peak_gdi32; +static PeakWinmmApi peak_winmm; +static PeakAudio peak_audio; static int peak_internal_user32_load(HMODULE handle) @@ -279,34 +432,45 @@ peak_internal_win32_key_map(WPARAM vk) if (vk >= 'A' && vk <= 'Z') return (PeakKeyCode)(PEAK_KEY_A + (int)(vk - 'A')); switch (vk) { - case VK_UP: return PEAK_KEY_UP; - case VK_DOWN: return PEAK_KEY_DOWN; - case VK_LEFT: return PEAK_KEY_LEFT; - case VK_RIGHT: return PEAK_KEY_RIGHT; - case VK_SPACE: return PEAK_KEY_SPACE; - case VK_ESCAPE: return PEAK_KEY_ESCAPE; - case VK_RETURN: return PEAK_KEY_ENTER; - default: return PEAK_KEY_UNKNOWN; + case VK_UP: + return PEAK_KEY_UP; + case VK_DOWN: + return PEAK_KEY_DOWN; + case VK_LEFT: + return PEAK_KEY_LEFT; + case VK_RIGHT: + return PEAK_KEY_RIGHT; + case VK_SPACE: + return PEAK_KEY_SPACE; + case VK_ESCAPE: + return PEAK_KEY_ESCAPE; + case VK_RETURN: + return PEAK_KEY_ENTER; + default: + return PEAK_KEY_UNKNOWN; } } static PeakKeyMod peak_internal_win32_mod_map(void) { - if (peak_user32.GetKeyState(VK_CONTROL) & 0x8000) return PEAK_KEYMOD_CTRL; - if (peak_user32.GetKeyState(VK_MENU) & 0x8000) return PEAK_KEYMOD_ALT; - if (peak_user32.GetKeyState(VK_SHIFT) & 0x8000) return PEAK_KEYMOD_SHIFT; - if (peak_user32.GetKeyState(VK_CAPITAL) & 1) return PEAK_KEYMOD_CAPS; + if (peak_user32.GetKeyState(VK_CONTROL) & 0x8000) + return PEAK_KEYMOD_CTRL; + if (peak_user32.GetKeyState(VK_MENU) & 0x8000) + return PEAK_KEYMOD_ALT; + if (peak_user32.GetKeyState(VK_SHIFT) & 0x8000) + return PEAK_KEYMOD_SHIFT; + if (peak_user32.GetKeyState(VK_CAPITAL) & 1) + return PEAK_KEYMOD_CAPS; return (PeakKeyMod)0; } static int -peak_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height) +peak_internal_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height) { uint32_t *buffer; - buffer = calloc((size_t)width * height, sizeof *buffer); - if (!buffer) + if (!(buffer = calloc((size_t)width * height, sizeof(*buffer)))) return 0; free(w->buffer); w->buffer = buffer; @@ -320,9 +484,10 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { struct peak_win32_win *w; PeakEvent ev; + CREATESTRUCTA *cs; if (msg == WM_NCCREATE) { - CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam; + cs = (CREATESTRUCTA *)lparam; peak_user32.SetPropA(hwnd, PEAK_WIN32_PROP, cs->lpCreateParams); } @@ -332,45 +497,46 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) switch (msg) { case WM_CLOSE: - memset(&ev, 0, sizeof ev); + memset(&ev, 0, sizeof(ev)); ev.type = PEAK_EVENT_WINDOW_CLOSE; peak_q_push(&w->q, ev); return 0; case WM_SIZE: { uint32_t width, height; + if (wparam == SIZE_MINIMIZED) return 0; width = (uint32_t)LOWORD(lparam); height = (uint32_t)HIWORD(lparam); if (!width || !height || (width == w->width && height == w->height)) return 0; - if (!peak_win32_buffer(w, width, height)) + if (!peak_internal_win32_buffer(w, width, height)) return 0; - memset(&ev, 0, sizeof ev); + memset(&ev, 0, sizeof(ev)); ev.type = PEAK_EVENT_WINDOW_RESIZE; ev.resize.width = w->width; ev.resize.height = w->height; peak_q_push(&w->q, ev); return 0; } - case WM_KEYDOWN: - case WM_KEYUP: - case WM_SYSKEYDOWN: + case WM_KEYDOWN: /* FALLTHROUGH */ + case WM_KEYUP: /* FALLTHROUGH */ + case WM_SYSKEYDOWN: /* FALLTHROUGH */ case WM_SYSKEYUP: - memset(&ev, 0, sizeof ev); + memset(&ev, 0, sizeof(ev)); ev.type = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; ev.key.key = peak_internal_win32_key_map(wparam); ev.key.mod = peak_internal_win32_mod_map(); peak_q_push(&w->q, ev); return 0; - case WM_MOUSEMOVE: - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_MBUTTONDOWN: + case WM_MOUSEMOVE: /* FALLTHROUGH */ + case WM_LBUTTONDOWN: /* FALLTHROUGH */ + case WM_LBUTTONUP: /* FALLTHROUGH */ + case WM_RBUTTONDOWN: /* FALLTHROUGH */ + case WM_RBUTTONUP: /* FALLTHROUGH */ + case WM_MBUTTONDOWN: /* FALLTHROUGH */ case WM_MBUTTONUP: - memset(&ev, 0, sizeof ev); + memset(&ev, 0, sizeof(ev)); ev.type = PEAK_EVENT_POINTER; ev.pointer.x = (float)(short)LOWORD(lparam); ev.pointer.y = (float)(short)HIWORD(lparam); @@ -391,6 +557,7 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) return 0; case WM_PAINT: { PAINTSTRUCT ps; + peak_user32.BeginPaint(hwnd, &ps); peak_user32.EndPaint(hwnd, &ps); return 0; @@ -431,8 +598,8 @@ peak_platform_init(void) } } if (!peak_win32.class_reg) { - memset(&wc, 0, sizeof wc); - wc.cbSize = sizeof wc; + memset(&wc, 0, sizeof(wc)); + wc.cbSize = sizeof(wc); wc.style = CS_OWNDC; wc.lpfnWndProc = peak_internal_win32_wndproc; wc.hInstance = GetModuleHandleA(NULL); @@ -469,10 +636,9 @@ peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uin if (!peak_user32.CreateWindowExA && !peak_platform_init()) return intern; - w = calloc(1, sizeof *w); - if (!w) + if (!(w = calloc(1, sizeof(*w)))) return intern; - if (!peak_win32_buffer(w, width, height)) { + if (!peak_internal_win32_buffer(w, width, height)) { free(w); return intern; } @@ -511,6 +677,7 @@ static void peak_platform_window_close(PeakWindowInternal *intern) { struct peak_win32_win *w; + if (!intern || !intern->w) return; w = intern->w; @@ -528,7 +695,9 @@ peak_platform_window_close(PeakWindowInternal *intern) static uint32_t * peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height) { - struct peak_win32_win *w = intern ? intern->w : NULL; + struct peak_win32_win *w; + + w = intern ? intern->w : NULL; if (!w) { *width = 0; *height = 0; @@ -542,13 +711,14 @@ peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *h static void peak_platform_window_present(PeakWindowInternal *intern) { - struct peak_win32_win *w = intern ? intern->w : NULL; + struct peak_win32_win *w; BITMAPINFO bmi; + w = intern ? intern->w : NULL; if (!w || !w->hwnd || !w->hdc || !w->buffer) return; - memset(&bmi, 0, sizeof bmi); + memset(&bmi, 0, sizeof(bmi)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = (LONG)w->width; bmi.bmiHeader.biHeight = -(LONG)w->height; @@ -565,8 +735,9 @@ static bool peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) { MSG msg; - struct peak_win32_win *w = intern ? intern->w : NULL; + struct peak_win32_win *w; + w = intern ? intern->w : NULL; if (!w || !w->hwnd) return 0; @@ -578,6 +749,198 @@ peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) } return peak_q_pop(&w->q, ev); } + +static int +peak_internal_winmm_load(void) +{ + HMODULE handle; + + if (peak_winmm.waveOutOpen) + return 1; + if (!(handle = LoadLibraryA(PEAK_WIN32_WINMM))) { + fputs("Failed to load winmm.dll. What system are you fucking using and abusing?", stderr); + return 0; + } +#define X(name, ret, conv, args) peak_winmm.name = (ret (conv *) args)(void *)GetProcAddress(handle, #name); + PEAK_WINMM_API(X) +#undef X +#define X(name, ret, conv, args) || !peak_winmm.name + if (0 PEAK_WINMM_API(X)) { + fputs("Failed to load winmm symbols", stderr); + return 0; + } +#undef X + return 1; +} + +static void +peak_internal_win32_audio_fill(int i) +{ + memset(peak_audio.pcm[i], 0, peak_audio.bytes); + if (peak_audio.fill) + peak_audio.fill(peak_audio.pcm[i], PEAK_AUDIO_FRAMES, peak_audio.userdata); + peak_winmm.waveOutWrite(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR)); +} + +static DWORD WINAPI +peak_internal_win32_audio_thread(LPVOID arg) +{ + int i, did; + + (void)arg; + for (;;) { + did = 0; + if (!peak_audio.run) + break; + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) { + if (peak_audio.hdr[i].dwFlags & WHDR_DONE) { + peak_internal_win32_audio_fill(i); + did = 1; + } + } + if (did) + continue; + WaitForSingleObject(peak_audio.event, INFINITE); + ResetEvent(peak_audio.event); + } + return 0; +} + +static int +peak_platform_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata) +{ + WAVEFORMATEX fmt; + int i; + + if (channels > 32) + return 0; + if (!peak_internal_winmm_load()) + return 0; + + memset(&fmt, 0, sizeof(fmt)); + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.nChannels = (WORD)channels; + fmt.nSamplesPerSec = rate; + fmt.wBitsPerSample = 16; + fmt.nBlockAlign = (WORD)(channels * 2); + fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign; + + peak_audio.event = CreateEventA(NULL, TRUE, FALSE, NULL); + if (!peak_audio.event) + return 0; + if (peak_winmm.waveOutOpen(&peak_audio.out, WAVE_MAPPER, &fmt, (DWORD_PTR)peak_audio.event, 0, CALLBACK_EVENT) != MMSYSERR_NOERROR) { + CloseHandle(peak_audio.event); + peak_audio.event = NULL; + fputs("Failed to open waveOut device", stderr); + return 0; + } + + peak_audio.channels = channels; + peak_audio.bytes = (uint32_t)channels * PEAK_AUDIO_FRAMES * sizeof(int16_t); + peak_audio.fill = fill; + peak_audio.userdata = userdata; + peak_audio.run = 1; + + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) { + if (!(peak_audio.pcm[i] = calloc(1, peak_audio.bytes))) + goto fail; + memset(&peak_audio.hdr[i], 0, sizeof(peak_audio.hdr[i])); + peak_audio.hdr[i].lpData = (LPSTR)peak_audio.pcm[i]; + peak_audio.hdr[i].dwBufferLength = peak_audio.bytes; + if (peak_winmm.waveOutPrepareHeader(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) + goto fail; + peak_audio.hdr[i].dwFlags |= WHDR_DONE; + } + + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) + peak_internal_win32_audio_fill(i); + + peak_audio.thread = CreateThread(NULL, 0, peak_internal_win32_audio_thread, NULL, 0, NULL); + if (!peak_audio.thread) + goto fail; + return 1; + +fail: + peak_platform_audio_stop(); + return 0; +} + +static uint64_t +peak_platform_get_time(void) +{ + LARGE_INTEGER count, freq; + QueryPerformanceCounter(&count); + QueryPerformanceFrequency(&freq); + return (uint64_t)((count.QuadPart * 1000000000ull) / freq.QuadPart); +} + +static void +peak_platform_sleep_ns(int64_t ns) +{ + if (ns <= 0) return; + Sleep((DWORD)(ns / 1000000)); +} + +static const char ** +peak_platform_vulkan_get_extensions(uint32_t *count) +{ + static const char *exts[] = { + "VK_KHR_surface", + "VK_KHR_win32_surface", + }; + if (count) *count = 2; + return exts; +} + +static int +peak_platform_vulkan_create_surface(PeakWindowInternal *intern, void *instance, const void *allocator, void *out_surface) +{ +#ifdef PEAK_VULKAN + VkWin32SurfaceCreateInfoKHR ci; + if (!intern || !intern->w || !intern->w->hwnd) return 0; + memset(&ci, 0, sizeof ci); + ci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + ci.hwnd = intern->w->hwnd; + ci.hinstance = GetModuleHandleA(NULL); + return vkCreateWin32SurfaceKHR((VkInstance)instance, &ci, (const VkAllocationCallbacks *)allocator, (VkSurfaceKHR *)out_surface) == VK_SUCCESS; +#else + (void)intern; (void)instance; (void)allocator; (void)out_surface; + return 0; +#endif +} + +static void +peak_platform_audio_stop(void) +{ + int i; + + peak_audio.run = 0; + if (peak_audio.event) + SetEvent(peak_audio.event); + if (peak_audio.thread) { + WaitForSingleObject(peak_audio.thread, INFINITE); + CloseHandle(peak_audio.thread); + peak_audio.thread = NULL; + } + if (peak_audio.out) { + peak_winmm.waveOutReset(peak_audio.out); + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) { + if (peak_audio.hdr[i].dwFlags & WHDR_PREPARED) + peak_winmm.waveOutUnprepareHeader(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR)); + free(peak_audio.pcm[i]); + peak_audio.pcm[i] = NULL; + memset(&peak_audio.hdr[i], 0, sizeof(peak_audio.hdr[i])); + } + peak_winmm.waveOutClose(peak_audio.out); + peak_audio.out = NULL; + } + if (peak_audio.event) { + CloseHandle(peak_audio.event); + peak_audio.event = NULL; + } + peak_audio.fill = NULL; + peak_audio.userdata = NULL; +} /* --- End of p_win32.c --- */ #elif defined(PEAK_LINUX) /* --- Start of p_linux.c --- */ @@ -590,10 +953,24 @@ peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) #include <X11/Xutil.h> #include <X11/keysym.h> #include <dlfcn.h> +#include <pthread.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <time.h> +#ifdef PEAK_VULKAN +#define VK_USE_PLATFORM_XLIB_KHR +#include <vulkan/vulkan.h> +#endif #define PEAK_X11_LINUX "libX11.so.6" +#define PEAK_PULSE_LINUX "libpulse-simple.so.0" +#define PEAK_AUDIO_FRAMES 256 + +#define PEAK_PULSE_API(X) \ + X(pa_simple_new, void *, (const char *, const char *, int, const char *, const char *, const void *, const void *, const void *, int *)) \ + X(pa_simple_free, void, (void *)) \ + X(pa_simple_write, int, (void *, const void *, size_t, int *)) #define PEAK_X11_API(X) \ X(XOpenDisplay, Display *, (const char *)) \ @@ -620,10 +997,33 @@ typedef struct { } PeakX11Api; typedef struct { +#define X(name, ret, args) ret (*name) args; + PEAK_PULSE_API(X) +#undef X +} PeakPulseApi; + +typedef struct { + int format; + uint32_t rate; + uint8_t channels; +} PeakPaSampleSpec; + +typedef struct { Display *display; Atom wm_delete_window; } PeakLinux; +typedef struct { + volatile int run; + int thread_on; + pthread_t thread; + void *stream; + int16_t *buf; + uint32_t channels; + void (*fill)(int16_t *out, size_t frames, void *userdata); + void *userdata; +} PeakAudio; + struct peak_window_internal_t { Window window; GC gfx_ctx; @@ -635,6 +1035,8 @@ struct peak_window_internal_t { static PeakLinux peak_linux; static PeakX11Api peak_x11; +static PeakPulseApi peak_pulse; +static PeakAudio peak_audio; static int peak_internal_x11_load(void *handle) @@ -877,6 +1279,154 @@ peak_platform_epoll(PeakWindowInternal *w, PeakEvent *ev) } return 0; } + +static int +peak_internal_pulse_load(void) +{ + void *handle; + + if (peak_pulse.pa_simple_new) + return 1; + if (!(handle = dlopen(PEAK_PULSE_LINUX, RTLD_LOCAL | RTLD_NOW))) { + fputs("Failed to load PulseAudio library. What system are you fucking using and abusing?", stderr); + return 0; + } +#define X(name, ret, args) peak_pulse.name = (ret (*) args)dlsym(handle, #name); + PEAK_PULSE_API(X) +#undef X +#define X(name, ret, args) || !peak_pulse.name + if (0 PEAK_PULSE_API(X)) { + fputs("Failed to load PulseAudio symbols", stderr); + return 0; + } +#undef X + return 1; +} + +static void * +peak_internal_audio_thread(void *arg) +{ + size_t n; + int error; + + (void)arg; + n = (size_t)peak_audio.channels * PEAK_AUDIO_FRAMES; + while (peak_audio.run) { + memset(peak_audio.buf, 0, n * sizeof(int16_t)); + if (peak_audio.fill) + peak_audio.fill(peak_audio.buf, PEAK_AUDIO_FRAMES, peak_audio.userdata); + if (peak_pulse.pa_simple_write(peak_audio.stream, peak_audio.buf, n * sizeof(int16_t), &error) < 0) + break; + } + return NULL; +} + +static int +peak_platform_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata) +{ + PeakPaSampleSpec ss; + int error; + + if (channels > 32) + return 0; + if (!peak_internal_pulse_load()) + return 0; + + ss.format = 3; /* PA_SAMPLE_S16LE */ + ss.rate = rate; + ss.channels = (uint8_t)channels; + peak_audio.stream = peak_pulse.pa_simple_new(NULL, "Peak", 1, NULL, "playback", &ss, NULL, NULL, &error); + if (!peak_audio.stream) { + fputs("Failed to open PulseAudio stream", stderr); + return 0; + } + if (!(peak_audio.buf = calloc((size_t)channels * PEAK_AUDIO_FRAMES, sizeof(int16_t)))) { + peak_pulse.pa_simple_free(peak_audio.stream); + peak_audio.stream = NULL; + return 0; + } + peak_audio.fill = fill; + peak_audio.userdata = userdata; + peak_audio.channels = channels; + peak_audio.run = 1; + if (pthread_create(&peak_audio.thread, NULL, peak_internal_audio_thread, NULL) != 0) { + peak_audio.thread_on = 0; + peak_audio.run = 0; + free(peak_audio.buf); + peak_audio.buf = NULL; + peak_pulse.pa_simple_free(peak_audio.stream); + peak_audio.stream = NULL; + return 0; + } + peak_audio.thread_on = 1; + return 1; +} + +static uint64_t +peak_platform_get_time(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * NANOS_PER_SEC + (uint64_t)ts.tv_nsec; +} + +static void +peak_platform_sleep_ns(int64_t ns) +{ + struct timespec ts; + if (ns <= 0) return; + ts.tv_sec = ns / 1000000000ll; + ts.tv_nsec = ns % 1000000000ll; + nanosleep(&ts, NULL); +} + +static const char ** +peak_platform_vulkan_get_extensions(uint32_t *count) +{ + static const char *exts[] = { + "VK_KHR_surface", + "VK_KHR_xlib_surface", + }; + if (count) *count = 2; + return exts; +} + +static int +peak_platform_vulkan_create_surface(PeakWindowInternal *w, void *instance, const void *allocator, void *out_surface) +{ +#ifdef PEAK_VULKAN + VkXlibSurfaceCreateInfoKHR ci; + if (!w || !peak_linux.display || !w->window) return 0; + memset(&ci, 0, sizeof ci); + ci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + ci.dpy = peak_linux.display; + ci.window = w->window; + return vkCreateXlibSurfaceKHR((VkInstance)instance, &ci, (const VkAllocationCallbacks *)allocator, (VkSurfaceKHR *)out_surface) == VK_SUCCESS; +#else + (void)w; (void)instance; (void)allocator; (void)out_surface; + return 0; +#endif +} + +static void +peak_platform_audio_stop(void) +{ + if (!peak_audio.run && !peak_audio.stream) + return; + peak_audio.run = 0; + if (peak_audio.thread_on) { + pthread_join(peak_audio.thread, NULL); + peak_audio.thread_on = 0; + } + if (peak_audio.stream) { + peak_pulse.pa_simple_free(peak_audio.stream); + peak_audio.stream = NULL; + } + free(peak_audio.buf); + peak_audio.buf = NULL; + peak_audio.fill = NULL; + peak_audio.userdata = NULL; +} /* --- End of p_linux.c --- */ #elif defined(PEAK_WEB) /* --- Start of p_emscripten.c --- */ @@ -1068,10 +1618,129 @@ peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) struct peak_web_win *w = intern ? intern->w : NULL; return w ? peak_q_pop(&w->q, ev) : 0; } + +#define PEAK_AUDIO_FRAMES 1024 + +static struct { + int run; + uint32_t channels; + int16_t *buf; + void (*fill)(int16_t *out, size_t frames, void *userdata); + void *userdata; +} peak_web_audio; + +void EMSCRIPTEN_KEEPALIVE +peak_internal_web_audio_fill(int16_t *out, int frames) +{ + size_t n; + + if (!peak_web_audio.run || !out || frames <= 0) + return; + n = (size_t)frames * peak_web_audio.channels; + memset(out, 0, n * sizeof(int16_t)); + if (peak_web_audio.fill) + peak_web_audio.fill(out, (size_t)frames, peak_web_audio.userdata); +} + +EM_JS(int, peak_web_audio_dom_start, (int channels, int rate, int frames, uintptr_t ptr), { + var AC = window.AudioContext || window.webkitAudioContext; + var ctx, proc, i, c, heap, off, ch; + if (!AC || Module._peak_web_audio) + return 0; + ctx = new AC({ sampleRate: rate }); + if (ctx.sampleRate !== rate) { + ctx.close(); + return 0; + } + proc = ctx.createScriptProcessor(frames, 0, channels); + proc.onaudioprocess = function(e) { + Module._peak_internal_web_audio_fill(ptr, frames); + heap = Module.HEAP16; + off = ptr >> 1; + for (c = 0; c < channels; c++) { + ch = e.outputBuffer.getChannelData(c); + for (i = 0; i < frames; i++) + ch[i] = heap[off + i * channels + c] / 32768.0; + } + }; + proc.connect(ctx.destination); + ctx.resume(); + Module._peak_web_audio = { ctx: ctx, proc: proc }; + return 1; +}); + +EM_JS(void, peak_web_audio_dom_stop, (void), { + var a = Module._peak_web_audio; + if (!a) + return; + a.proc.disconnect(); + a.ctx.close(); + Module._peak_web_audio = null; +}); + +static int +peak_platform_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata) +{ + if (channels > 32) + return 0; + if (!(peak_web_audio.buf = calloc((size_t)channels * PEAK_AUDIO_FRAMES, sizeof(int16_t)))) + return 0; + peak_web_audio.fill = fill; + peak_web_audio.userdata = userdata; + peak_web_audio.channels = channels; + peak_web_audio.run = 1; + if (!peak_web_audio_dom_start((int)channels, (int)rate, PEAK_AUDIO_FRAMES, (uintptr_t)peak_web_audio.buf)) { + free(peak_web_audio.buf); + peak_web_audio.buf = NULL; + peak_web_audio.run = 0; + peak_web_audio.fill = NULL; + return 0; + } + return 1; +} + +static uint64_t +peak_platform_get_time(void) +{ + return (uint64_t)(emscripten_get_now() * 1000000.0); +} + +static void +peak_platform_sleep_ns(int64_t ns) +{ + if (ns <= 0) return; + emscripten_sleep((unsigned)(ns / 1000000)); +} + +static const char ** +peak_platform_vulkan_get_extensions(uint32_t *count) +{ + if (count) *count = 0; + return NULL; +} + +static int +peak_platform_vulkan_create_surface(PeakWindowInternal *w, void *instance, const void *allocator, void *out_surface) +{ + (void)w; (void)instance; (void)allocator; (void)out_surface; + return 0; +} + +static void +peak_platform_audio_stop(void) +{ + peak_web_audio.run = 0; + peak_web_audio_dom_stop(); + free(peak_web_audio.buf); + peak_web_audio.buf = NULL; + peak_web_audio.fill = NULL; + peak_web_audio.userdata = NULL; +} /* --- End of p_emscripten.c --- */ #endif -/* We define PeakWindow only when we know the type of peak_window_internal_t */ +/* NOTE(vasco): We define PeakWindow only when + * we know the type of peak_window_internal_t */ struct PeakWindow { PeakWindowInternal internal; int (*tick)(struct PeakWindow *win, void *userdata); @@ -1080,11 +1749,170 @@ struct PeakWindow { uint32_t width; uint32_t height; uint32_t bufsize; + uint16_t *audio; // LRLRLR int running; }; +/* --- Start of p_log.c --- */ +/* + * Logging! + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> + +#define PEAK_MAX_PRINTF 1024 +#define PEAK_MAX_ALLOCS 512 + +typedef struct { + void *ptr; + size_t size; + const char *file; + const char *func; + int line; +} PeakDebugMemoryInfo; + +static uint64_t peak_alloc_count = 0; +static PeakDebugMemoryInfo peak_ptr_array[PEAK_MAX_ALLOCS]; + +void +peak_log_printf(PeakLogLevel level, const char *src, ...) +{ + char out[PEAK_MAX_PRINTF]; + va_list ap; + int len; + size_t offset = P_PREFIX_LEN + 1; + + if (level < 0 || level >= P_COUNT_LOG_LEVEL) + level = P_LOG_LEVEL_ERROR; + memcpy(out, p_prefix[level], P_PREFIX_LEN); + out[P_PREFIX_LEN] = ' '; + va_start(ap, src); + len = vsnprintf(out + offset, PEAK_MAX_PRINTF - offset, src, ap); + va_end(ap); + if (len < 0) len = 0; + if (offset + (size_t)len >= PEAK_MAX_PRINTF) + len = (int)(PEAK_MAX_PRINTF - offset - 1); + out[offset + (size_t)len] = '\n'; + fwrite(out, 1, offset + (size_t)len + 1, (level <= P_LOG_LEVEL_ERROR) ? stderr : stdout); +} + +void * +peak_debug_malloc_impl(size_t size, const char *file, int line, const char *func) +{ + void *ptr = malloc(size); + printf("[ALLOC] %p (%zu bytes) -> %s:%d %s()\n", ptr, size, file, line, func); + + if (ptr) { + if (peak_alloc_count < PEAK_MAX_ALLOCS) { + peak_ptr_array[peak_alloc_count++] = (PeakDebugMemoryInfo){ + .ptr = ptr, + .size = size, + .file = file, + .func = func, + .line = line + }; + } else { + fprintf(stderr, "[ERROR] Debug allocator tracking capacity (%d) exceeded!\n", PEAK_MAX_ALLOCS); + } + } + return ptr; +} + +void +peak_debug_free_impl(void *ptr, const char *file, int line, const char *func) +{ + printf("[FREE] %p -> %s:%d %s()\n", ptr, file, line, func); + + if (!ptr) return; + + bool found = false; + uint64_t index = 0; + + for (index = 0; index < peak_alloc_count; ++index) { + if (peak_ptr_array[index].ptr == ptr) { + found = true; + break; + } + } + + if (found) { + for (uint64_t j = index; j < peak_alloc_count - 1; ++j) { + peak_ptr_array[j] = peak_ptr_array[j + 1]; + } + peak_alloc_count--; + } else { + fprintf(stderr, "[WARNING] Attempted to free untracked/double-freed pointer %p at %s:%d %s()\n", + ptr, file, line, func); + } + + free(ptr); +} + +void * +peak_debug_realloc_impl(void *ptr, size_t size, const char *file, int line, const char *func) +{ + if (!ptr) { + return peak_debug_malloc_impl(size, file, line, func); + } + if (size == 0) { + peak_debug_free_impl(ptr, file, line, func); + return NULL; + } + + uintptr_t old_addr = (uintptr_t)ptr; + void *new_ptr = realloc(ptr, size); + printf("[REALLOC] %p -> %p (%zu bytes) -> %s:%d %s()\n", (void *)old_addr, new_ptr, size, file, line, func); + + if (new_ptr) { + bool found = false; + for (uint64_t i = 0; i < peak_alloc_count; ++i) { + if (peak_ptr_array[i].ptr == (void *)old_addr) { + peak_ptr_array[i].ptr = new_ptr; + peak_ptr_array[i].size = size; + peak_ptr_array[i].file = file; + peak_ptr_array[i].line = line; + peak_ptr_array[i].func = func; + found = true; + break; + } + } + if (!found) { + if (peak_alloc_count < PEAK_MAX_ALLOCS) { + peak_ptr_array[peak_alloc_count++] = (PeakDebugMemoryInfo){ + .ptr = new_ptr, + .size = size, + .file = file, + .func = func, + .line = line + }; + } + } + } + return new_ptr; +} + +void +peak_debug_memory_report(void) +{ + printf("\n==================== MEMORY REPORT ====================\n"); + printf("Remaining unfreed allocations: %lu\n", (unsigned long)peak_alloc_count); + + for (uint64_t i = 0; i < peak_alloc_count; ++i) { + PeakDebugMemoryInfo *info = &peak_ptr_array[i]; + printf("[LEAK] %p (%zu bytes) allocated at %s:%d in %s()\n", + info->ptr, info->size, info->file, info->line, info->func); + } + printf("=======================================================\n"); +} +/* --- End of p_log.c --- */ /* --- Start of peak.c --- */ +#include <stdio.h> +#include <stdlib.h> + static uint32_t * peak_window_sync(PeakWindow *win, size_t *width, size_t *height) { @@ -1107,6 +1935,7 @@ peak_init(void) void peak_quit(void) { + peak_audio_stop(); peak_platform_quit(); } @@ -1203,6 +2032,82 @@ peak_window_run(PeakWindow *win, int (*peak_tick)(PeakWindow *win, void *userdat while (win->running && win->tick(win, win->userdata)); #endif } + +int +peak_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata) +{ + if (!fill || !channels || !rate) + return 0; + peak_audio_stop(); + return peak_platform_audio_start(channels, rate, fill, userdata); +} + +void +peak_audio_stop(void) +{ + peak_platform_audio_stop(); +} + +uint64_t +peak_get_time(void) +{ + return peak_platform_get_time(); +} + +void +peak_sleep_ns(int64_t ns) +{ + peak_platform_sleep_ns(ns); +} + +int +peak_file_exists(const char *path) +{ + FILE *f; + if (!path) return 0; + f = fopen(path, "rb"); + if (!f) return 0; + fclose(f); + return 1; +} + +void * +peak_file_alloc(const char *path, unsigned long *buf_size) +{ + FILE *f; + long n; + void *p; + if (!path) return NULL; + f = fopen(path, "rb"); + if (!f) return NULL; + if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; } + n = ftell(f); + if (n < 0) { fclose(f); return NULL; } + rewind(f); + p = malloc((size_t)n + (n == 0)); + if (!p) { fclose(f); return NULL; } + if (n && fread(p, 1, (size_t)n, f) != (size_t)n) { + free(p); + fclose(f); + return NULL; + } + fclose(f); + if (buf_size) *buf_size = (unsigned long)n; + return p; +} + +const char ** +peak_vulkan_get_extensions(uint32_t *count) +{ + return peak_platform_vulkan_get_extensions(count); +} + +int +peak_vulkan_create_surface(PeakWindow *win, void *instance, const void *allocator, void *out_surface) +{ + if (!win || !instance || !out_surface) return 0; + return peak_platform_vulkan_create_surface(&win->internal, instance, allocator, out_surface); +} /* --- End of peak.c --- */ #endif // PEAK_IMPLEMENTATION diff --git a/Peak/src/p_emscripten.c b/Peak/src/p_emscripten.c index f79f575..6da25a8 100644 --- a/Peak/src/p_emscripten.c +++ b/Peak/src/p_emscripten.c @@ -186,3 +186,121 @@ peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) struct peak_web_win *w = intern ? intern->w : NULL; return w ? peak_q_pop(&w->q, ev) : 0; } + +#define PEAK_AUDIO_FRAMES 1024 + +static struct { + int run; + uint32_t channels; + int16_t *buf; + void (*fill)(int16_t *out, size_t frames, void *userdata); + void *userdata; +} peak_web_audio; + +void EMSCRIPTEN_KEEPALIVE +peak_internal_web_audio_fill(int16_t *out, int frames) +{ + size_t n; + + if (!peak_web_audio.run || !out || frames <= 0) + return; + n = (size_t)frames * peak_web_audio.channels; + memset(out, 0, n * sizeof(int16_t)); + if (peak_web_audio.fill) + peak_web_audio.fill(out, (size_t)frames, peak_web_audio.userdata); +} + +EM_JS(int, peak_web_audio_dom_start, (int channels, int rate, int frames, uintptr_t ptr), { + var AC = window.AudioContext || window.webkitAudioContext; + var ctx, proc, i, c, heap, off, ch; + if (!AC || Module._peak_web_audio) + return 0; + ctx = new AC({ sampleRate: rate }); + if (ctx.sampleRate !== rate) { + ctx.close(); + return 0; + } + proc = ctx.createScriptProcessor(frames, 0, channels); + proc.onaudioprocess = function(e) { + Module._peak_internal_web_audio_fill(ptr, frames); + heap = Module.HEAP16; + off = ptr >> 1; + for (c = 0; c < channels; c++) { + ch = e.outputBuffer.getChannelData(c); + for (i = 0; i < frames; i++) + ch[i] = heap[off + i * channels + c] / 32768.0; + } + }; + proc.connect(ctx.destination); + ctx.resume(); + Module._peak_web_audio = { ctx: ctx, proc: proc }; + return 1; +}); + +EM_JS(void, peak_web_audio_dom_stop, (void), { + var a = Module._peak_web_audio; + if (!a) + return; + a.proc.disconnect(); + a.ctx.close(); + Module._peak_web_audio = null; +}); + +static int +peak_platform_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata) +{ + if (channels > 32) + return 0; + if (!(peak_web_audio.buf = calloc((size_t)channels * PEAK_AUDIO_FRAMES, sizeof(int16_t)))) + return 0; + peak_web_audio.fill = fill; + peak_web_audio.userdata = userdata; + peak_web_audio.channels = channels; + peak_web_audio.run = 1; + if (!peak_web_audio_dom_start((int)channels, (int)rate, PEAK_AUDIO_FRAMES, (uintptr_t)peak_web_audio.buf)) { + free(peak_web_audio.buf); + peak_web_audio.buf = NULL; + peak_web_audio.run = 0; + peak_web_audio.fill = NULL; + return 0; + } + return 1; +} + +static uint64_t +peak_platform_get_time(void) +{ + return (uint64_t)(emscripten_get_now() * 1000000.0); +} + +static void +peak_platform_sleep_ns(int64_t ns) +{ + if (ns <= 0) return; + emscripten_sleep((unsigned)(ns / 1000000)); +} + +static const char ** +peak_platform_vulkan_get_extensions(uint32_t *count) +{ + if (count) *count = 0; + return NULL; +} + +static int +peak_platform_vulkan_create_surface(PeakWindowInternal *w, void *instance, const void *allocator, void *out_surface) +{ + (void)w; (void)instance; (void)allocator; (void)out_surface; + return 0; +} + +static void +peak_platform_audio_stop(void) +{ + peak_web_audio.run = 0; + peak_web_audio_dom_stop(); + free(peak_web_audio.buf); + peak_web_audio.buf = NULL; + peak_web_audio.fill = NULL; + peak_web_audio.userdata = NULL; +} diff --git a/Peak/src/p_linux.c b/Peak/src/p_linux.c index 0dc5e2f..9090b46 100644 --- a/Peak/src/p_linux.c +++ b/Peak/src/p_linux.c @@ -7,10 +7,24 @@ #include <X11/Xutil.h> #include <X11/keysym.h> #include <dlfcn.h> +#include <pthread.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <time.h> +#ifdef PEAK_VULKAN +#define VK_USE_PLATFORM_XLIB_KHR +#include <vulkan/vulkan.h> +#endif #define PEAK_X11_LINUX "libX11.so.6" +#define PEAK_PULSE_LINUX "libpulse-simple.so.0" +#define PEAK_AUDIO_FRAMES 256 + +#define PEAK_PULSE_API(X) \ + X(pa_simple_new, void *, (const char *, const char *, int, const char *, const char *, const void *, const void *, const void *, int *)) \ + X(pa_simple_free, void, (void *)) \ + X(pa_simple_write, int, (void *, const void *, size_t, int *)) #define PEAK_X11_API(X) \ X(XOpenDisplay, Display *, (const char *)) \ @@ -37,10 +51,33 @@ typedef struct { } PeakX11Api; typedef struct { +#define X(name, ret, args) ret (*name) args; + PEAK_PULSE_API(X) +#undef X +} PeakPulseApi; + +typedef struct { + int format; + uint32_t rate; + uint8_t channels; +} PeakPaSampleSpec; + +typedef struct { Display *display; Atom wm_delete_window; } PeakLinux; +typedef struct { + volatile int run; + int thread_on; + pthread_t thread; + void *stream; + int16_t *buf; + uint32_t channels; + void (*fill)(int16_t *out, size_t frames, void *userdata); + void *userdata; +} PeakAudio; + struct peak_window_internal_t { Window window; GC gfx_ctx; @@ -52,6 +89,8 @@ struct peak_window_internal_t { static PeakLinux peak_linux; static PeakX11Api peak_x11; +static PeakPulseApi peak_pulse; +static PeakAudio peak_audio; static int peak_internal_x11_load(void *handle) @@ -294,3 +333,151 @@ peak_platform_epoll(PeakWindowInternal *w, PeakEvent *ev) } return 0; } + +static int +peak_internal_pulse_load(void) +{ + void *handle; + + if (peak_pulse.pa_simple_new) + return 1; + if (!(handle = dlopen(PEAK_PULSE_LINUX, RTLD_LOCAL | RTLD_NOW))) { + fputs("Failed to load PulseAudio library. What system are you fucking using and abusing?", stderr); + return 0; + } +#define X(name, ret, args) peak_pulse.name = (ret (*) args)dlsym(handle, #name); + PEAK_PULSE_API(X) +#undef X +#define X(name, ret, args) || !peak_pulse.name + if (0 PEAK_PULSE_API(X)) { + fputs("Failed to load PulseAudio symbols", stderr); + return 0; + } +#undef X + return 1; +} + +static void * +peak_internal_audio_thread(void *arg) +{ + size_t n; + int error; + + (void)arg; + n = (size_t)peak_audio.channels * PEAK_AUDIO_FRAMES; + while (peak_audio.run) { + memset(peak_audio.buf, 0, n * sizeof(int16_t)); + if (peak_audio.fill) + peak_audio.fill(peak_audio.buf, PEAK_AUDIO_FRAMES, peak_audio.userdata); + if (peak_pulse.pa_simple_write(peak_audio.stream, peak_audio.buf, n * sizeof(int16_t), &error) < 0) + break; + } + return NULL; +} + +static int +peak_platform_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata) +{ + PeakPaSampleSpec ss; + int error; + + if (channels > 32) + return 0; + if (!peak_internal_pulse_load()) + return 0; + + ss.format = 3; /* PA_SAMPLE_S16LE */ + ss.rate = rate; + ss.channels = (uint8_t)channels; + peak_audio.stream = peak_pulse.pa_simple_new(NULL, "Peak", 1, NULL, "playback", &ss, NULL, NULL, &error); + if (!peak_audio.stream) { + fputs("Failed to open PulseAudio stream", stderr); + return 0; + } + if (!(peak_audio.buf = calloc((size_t)channels * PEAK_AUDIO_FRAMES, sizeof(int16_t)))) { + peak_pulse.pa_simple_free(peak_audio.stream); + peak_audio.stream = NULL; + return 0; + } + peak_audio.fill = fill; + peak_audio.userdata = userdata; + peak_audio.channels = channels; + peak_audio.run = 1; + if (pthread_create(&peak_audio.thread, NULL, peak_internal_audio_thread, NULL) != 0) { + peak_audio.thread_on = 0; + peak_audio.run = 0; + free(peak_audio.buf); + peak_audio.buf = NULL; + peak_pulse.pa_simple_free(peak_audio.stream); + peak_audio.stream = NULL; + return 0; + } + peak_audio.thread_on = 1; + return 1; +} + +static uint64_t +peak_platform_get_time(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * NANOS_PER_SEC + (uint64_t)ts.tv_nsec; +} + +static void +peak_platform_sleep_ns(int64_t ns) +{ + struct timespec ts; + if (ns <= 0) return; + ts.tv_sec = ns / 1000000000ll; + ts.tv_nsec = ns % 1000000000ll; + nanosleep(&ts, NULL); +} + +static const char ** +peak_platform_vulkan_get_extensions(uint32_t *count) +{ + static const char *exts[] = { + "VK_KHR_surface", + "VK_KHR_xlib_surface", + }; + if (count) *count = 2; + return exts; +} + +static int +peak_platform_vulkan_create_surface(PeakWindowInternal *w, void *instance, const void *allocator, void *out_surface) +{ +#ifdef PEAK_VULKAN + VkXlibSurfaceCreateInfoKHR ci; + if (!w || !peak_linux.display || !w->window) return 0; + memset(&ci, 0, sizeof ci); + ci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + ci.dpy = peak_linux.display; + ci.window = w->window; + return vkCreateXlibSurfaceKHR((VkInstance)instance, &ci, (const VkAllocationCallbacks *)allocator, (VkSurfaceKHR *)out_surface) == VK_SUCCESS; +#else + (void)w; (void)instance; (void)allocator; (void)out_surface; + return 0; +#endif +} + +static void +peak_platform_audio_stop(void) +{ + if (!peak_audio.run && !peak_audio.stream) + return; + peak_audio.run = 0; + if (peak_audio.thread_on) { + pthread_join(peak_audio.thread, NULL); + peak_audio.thread_on = 0; + } + if (peak_audio.stream) { + peak_pulse.pa_simple_free(peak_audio.stream); + peak_audio.stream = NULL; + } + free(peak_audio.buf); + peak_audio.buf = NULL; + peak_audio.fill = NULL; + peak_audio.userdata = NULL; +} diff --git a/Peak/src/p_log.c b/Peak/src/p_log.c new file mode 100644 index 0000000..136214d --- /dev/null +++ b/Peak/src/p_log.c @@ -0,0 +1,153 @@ +/* + * Logging! + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> + +#define PEAK_MAX_PRINTF 1024 +#define PEAK_MAX_ALLOCS 512 + +typedef struct { + void *ptr; + size_t size; + const char *file; + const char *func; + int line; +} PeakDebugMemoryInfo; + +static uint64_t peak_alloc_count = 0; +static PeakDebugMemoryInfo peak_ptr_array[PEAK_MAX_ALLOCS]; + +void +peak_log_printf(PeakLogLevel level, const char *src, ...) +{ + char out[PEAK_MAX_PRINTF]; + va_list ap; + int len; + size_t offset = P_PREFIX_LEN + 1; + + if (level < 0 || level >= P_COUNT_LOG_LEVEL) + level = P_LOG_LEVEL_ERROR; + memcpy(out, p_prefix[level], P_PREFIX_LEN); + out[P_PREFIX_LEN] = ' '; + va_start(ap, src); + len = vsnprintf(out + offset, PEAK_MAX_PRINTF - offset, src, ap); + va_end(ap); + if (len < 0) len = 0; + if (offset + (size_t)len >= PEAK_MAX_PRINTF) + len = (int)(PEAK_MAX_PRINTF - offset - 1); + out[offset + (size_t)len] = '\n'; + fwrite(out, 1, offset + (size_t)len + 1, (level <= P_LOG_LEVEL_ERROR) ? stderr : stdout); +} + +void * +peak_debug_malloc_impl(size_t size, const char *file, int line, const char *func) +{ + void *ptr = malloc(size); + printf("[ALLOC] %p (%zu bytes) -> %s:%d %s()\n", ptr, size, file, line, func); + + if (ptr) { + if (peak_alloc_count < PEAK_MAX_ALLOCS) { + peak_ptr_array[peak_alloc_count++] = (PeakDebugMemoryInfo){ + .ptr = ptr, + .size = size, + .file = file, + .func = func, + .line = line + }; + } else { + fprintf(stderr, "[ERROR] Debug allocator tracking capacity (%d) exceeded!\n", PEAK_MAX_ALLOCS); + } + } + return ptr; +} + +void +peak_debug_free_impl(void *ptr, const char *file, int line, const char *func) +{ + printf("[FREE] %p -> %s:%d %s()\n", ptr, file, line, func); + + if (!ptr) return; + + bool found = false; + uint64_t index = 0; + + for (index = 0; index < peak_alloc_count; ++index) { + if (peak_ptr_array[index].ptr == ptr) { + found = true; + break; + } + } + + if (found) { + for (uint64_t j = index; j < peak_alloc_count - 1; ++j) { + peak_ptr_array[j] = peak_ptr_array[j + 1]; + } + peak_alloc_count--; + } else { + fprintf(stderr, "[WARNING] Attempted to free untracked/double-freed pointer %p at %s:%d %s()\n", + ptr, file, line, func); + } + + free(ptr); +} + +void * +peak_debug_realloc_impl(void *ptr, size_t size, const char *file, int line, const char *func) +{ + if (!ptr) { + return peak_debug_malloc_impl(size, file, line, func); + } + if (size == 0) { + peak_debug_free_impl(ptr, file, line, func); + return NULL; + } + + uintptr_t old_addr = (uintptr_t)ptr; + void *new_ptr = realloc(ptr, size); + printf("[REALLOC] %p -> %p (%zu bytes) -> %s:%d %s()\n", (void *)old_addr, new_ptr, size, file, line, func); + + if (new_ptr) { + bool found = false; + for (uint64_t i = 0; i < peak_alloc_count; ++i) { + if (peak_ptr_array[i].ptr == (void *)old_addr) { + peak_ptr_array[i].ptr = new_ptr; + peak_ptr_array[i].size = size; + peak_ptr_array[i].file = file; + peak_ptr_array[i].line = line; + peak_ptr_array[i].func = func; + found = true; + break; + } + } + if (!found) { + if (peak_alloc_count < PEAK_MAX_ALLOCS) { + peak_ptr_array[peak_alloc_count++] = (PeakDebugMemoryInfo){ + .ptr = new_ptr, + .size = size, + .file = file, + .func = func, + .line = line + }; + } + } + } + return new_ptr; +} + +void +peak_debug_memory_report(void) +{ + printf("\n==================== MEMORY REPORT ====================\n"); + printf("Remaining unfreed allocations: %lu\n", (unsigned long)peak_alloc_count); + + for (uint64_t i = 0; i < peak_alloc_count; ++i) { + PeakDebugMemoryInfo *info = &peak_ptr_array[i]; + printf("[LEAK] %p (%zu bytes) allocated at %s:%d in %s()\n", + info->ptr, info->size, info->file, info->line, info->func); + } + printf("=======================================================\n"); +} diff --git a/Peak/src/p_win32.c b/Peak/src/p_win32.c index c1a289b..cf99b6c 100644 --- a/Peak/src/p_win32.c +++ b/Peak/src/p_win32.c @@ -1,40 +1,65 @@ +/* + * Win32 window, input, StretchDIBits present, and waveOut audio. + * user32.dll, gdi32.dll, and winmm.dll are loaded at runtime. + * + * * 0.4.0 - @vasco - win32 + * * 0.5.0 - @vasco - audio start/stop, s16le pull callback + */ + #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +/* windows.h must precede mmsystem.h */ #include <windows.h> +#include <mmsystem.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#ifdef PEAK_VULKAN +#define VK_USE_PLATFORM_WIN32_KHR +#include <vulkan/vulkan.h> +#endif #define PEAK_WIN32_USER32 "user32.dll" #define PEAK_WIN32_GDI32 "gdi32.dll" +#define PEAK_WIN32_WINMM "winmm.dll" #define PEAK_WIN32_CLASS "PeakWindow" #define PEAK_WIN32_PROP "Peak" +#define PEAK_AUDIO_FRAMES 256 +#define PEAK_AUDIO_BUFFERS 2 #define PEAK_USER32_API(X) \ - X(RegisterClassExA, ATOM, WINAPI, (const WNDCLASSEXA *)) \ - X(UnregisterClassA, BOOL, WINAPI, (LPCSTR, HINSTANCE)) \ - X(CreateWindowExA, HWND, WINAPI, (DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)) \ - X(DestroyWindow, BOOL, WINAPI, (HWND)) \ - X(ShowWindow, BOOL, WINAPI, (HWND, int)) \ - X(GetDC, HDC, WINAPI, (HWND)) \ - X(ReleaseDC, int, WINAPI, (HWND, HDC)) \ - X(PeekMessageA, BOOL, WINAPI, (LPMSG, HWND, UINT, UINT, UINT)) \ - X(TranslateMessage, BOOL, WINAPI, (const MSG *)) \ - X(DispatchMessageA, LRESULT, WINAPI, (const MSG *)) \ - X(DefWindowProcA, LRESULT, WINAPI, (HWND, UINT, WPARAM, LPARAM)) \ - X(SetPropA, BOOL, WINAPI, (HWND, LPCSTR, HANDLE)) \ - X(GetPropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ - X(RemovePropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ - X(BeginPaint, HDC, WINAPI, (HWND, LPPAINTSTRUCT)) \ - X(EndPaint, BOOL, WINAPI, (HWND, const PAINTSTRUCT *)) \ - X(AdjustWindowRectEx, BOOL, WINAPI, (LPRECT, DWORD, BOOL, DWORD)) \ - X(LoadCursorA, HCURSOR, WINAPI, (HINSTANCE, LPCSTR)) \ - X(GetKeyState, SHORT, WINAPI, (int)) + X(RegisterClassExA, ATOM, WINAPI, (const WNDCLASSEXA *)) \ + X(UnregisterClassA, BOOL, WINAPI, (LPCSTR, HINSTANCE)) \ + X(CreateWindowExA, HWND, WINAPI, (DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)) \ + X(DestroyWindow, BOOL, WINAPI, (HWND)) \ + X(ShowWindow, BOOL, WINAPI, (HWND, int)) \ + X(GetDC, HDC, WINAPI, (HWND)) \ + X(ReleaseDC, int, WINAPI, (HWND, HDC)) \ + X(PeekMessageA, BOOL, WINAPI, (LPMSG, HWND, UINT, UINT, UINT)) \ + X(TranslateMessage, BOOL, WINAPI, (const MSG *)) \ + X(DispatchMessageA, LRESULT, WINAPI, (const MSG *)) \ + X(DefWindowProcA, LRESULT, WINAPI, (HWND, UINT, WPARAM, LPARAM)) \ + X(SetPropA, BOOL, WINAPI, (HWND, LPCSTR, HANDLE)) \ + X(GetPropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(RemovePropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(BeginPaint, HDC, WINAPI, (HWND, LPPAINTSTRUCT)) \ + X(EndPaint, BOOL, WINAPI, (HWND, const PAINTSTRUCT *)) \ + X(AdjustWindowRectEx, BOOL, WINAPI, (LPRECT, DWORD, BOOL, DWORD)) \ + X(LoadCursorA, HCURSOR, WINAPI, (HINSTANCE, LPCSTR)) \ + X(GetKeyState, SHORT, WINAPI, (int)) #define PEAK_GDI32_API(X) \ - X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD)) + X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD)) + +#define PEAK_WINMM_API(X) \ + X(waveOutOpen, MMRESULT, WINAPI, (LPHWAVEOUT, UINT, LPCWAVEFORMATEX, DWORD_PTR, DWORD_PTR, DWORD)) \ + X(waveOutClose, MMRESULT, WINAPI, (HWAVEOUT)) \ + X(waveOutPrepareHeader, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \ + X(waveOutUnprepareHeader, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \ + X(waveOutWrite, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \ + X(waveOutReset, MMRESULT, WINAPI, (HWAVEOUT)) typedef struct { #define X(name, ret, conv, args) ret (conv *name) args; @@ -49,6 +74,25 @@ typedef struct { } PeakGdi32Api; typedef struct { +#define X(name, ret, conv, args) ret (conv *name) args; + PEAK_WINMM_API(X) +#undef X +} PeakWinmmApi; + +typedef struct { + volatile int run; + HANDLE thread; + HANDLE event; + HWAVEOUT out; + WAVEHDR hdr[PEAK_AUDIO_BUFFERS]; + int16_t *pcm[PEAK_AUDIO_BUFFERS]; + uint32_t channels; + uint32_t bytes; + void (*fill)(int16_t *out, size_t frames, void *userdata); + void *userdata; +} PeakAudio; + +typedef struct { HMODULE user32; HMODULE gdi32; int class_reg; @@ -67,9 +111,28 @@ struct peak_window_internal_t { struct peak_win32_win *w; }; +static int peak_internal_user32_load(HMODULE handle); +static int peak_internal_gdi32_load(HMODULE handle); +static PeakKeyCode peak_internal_win32_key_map(WPARAM vk); +static PeakKeyMod peak_internal_win32_mod_map(void); +static int peak_internal_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height); +static LRESULT CALLBACK peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); +static int peak_platform_init(void); +static void peak_platform_quit(void); +static PeakWindowInternal peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags); +static void peak_platform_window_close(PeakWindowInternal *intern); +static uint32_t *peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height); +static void peak_platform_window_present(PeakWindowInternal *intern); +static bool peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev); +static int peak_internal_winmm_load(void); +static void peak_internal_win32_audio_fill(int i); +static DWORD WINAPI peak_internal_win32_audio_thread(LPVOID arg); + static PeakWin32 peak_win32; static PeakUser32Api peak_user32; static PeakGdi32Api peak_gdi32; +static PeakWinmmApi peak_winmm; +static PeakAudio peak_audio; static int peak_internal_user32_load(HMODULE handle) @@ -103,34 +166,45 @@ peak_internal_win32_key_map(WPARAM vk) if (vk >= 'A' && vk <= 'Z') return (PeakKeyCode)(PEAK_KEY_A + (int)(vk - 'A')); switch (vk) { - case VK_UP: return PEAK_KEY_UP; - case VK_DOWN: return PEAK_KEY_DOWN; - case VK_LEFT: return PEAK_KEY_LEFT; - case VK_RIGHT: return PEAK_KEY_RIGHT; - case VK_SPACE: return PEAK_KEY_SPACE; - case VK_ESCAPE: return PEAK_KEY_ESCAPE; - case VK_RETURN: return PEAK_KEY_ENTER; - default: return PEAK_KEY_UNKNOWN; + case VK_UP: + return PEAK_KEY_UP; + case VK_DOWN: + return PEAK_KEY_DOWN; + case VK_LEFT: + return PEAK_KEY_LEFT; + case VK_RIGHT: + return PEAK_KEY_RIGHT; + case VK_SPACE: + return PEAK_KEY_SPACE; + case VK_ESCAPE: + return PEAK_KEY_ESCAPE; + case VK_RETURN: + return PEAK_KEY_ENTER; + default: + return PEAK_KEY_UNKNOWN; } } static PeakKeyMod peak_internal_win32_mod_map(void) { - if (peak_user32.GetKeyState(VK_CONTROL) & 0x8000) return PEAK_KEYMOD_CTRL; - if (peak_user32.GetKeyState(VK_MENU) & 0x8000) return PEAK_KEYMOD_ALT; - if (peak_user32.GetKeyState(VK_SHIFT) & 0x8000) return PEAK_KEYMOD_SHIFT; - if (peak_user32.GetKeyState(VK_CAPITAL) & 1) return PEAK_KEYMOD_CAPS; + if (peak_user32.GetKeyState(VK_CONTROL) & 0x8000) + return PEAK_KEYMOD_CTRL; + if (peak_user32.GetKeyState(VK_MENU) & 0x8000) + return PEAK_KEYMOD_ALT; + if (peak_user32.GetKeyState(VK_SHIFT) & 0x8000) + return PEAK_KEYMOD_SHIFT; + if (peak_user32.GetKeyState(VK_CAPITAL) & 1) + return PEAK_KEYMOD_CAPS; return (PeakKeyMod)0; } static int -peak_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height) +peak_internal_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height) { uint32_t *buffer; - buffer = calloc((size_t)width * height, sizeof *buffer); - if (!buffer) + if (!(buffer = calloc((size_t)width * height, sizeof(*buffer)))) return 0; free(w->buffer); w->buffer = buffer; @@ -144,9 +218,10 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { struct peak_win32_win *w; PeakEvent ev; + CREATESTRUCTA *cs; if (msg == WM_NCCREATE) { - CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam; + cs = (CREATESTRUCTA *)lparam; peak_user32.SetPropA(hwnd, PEAK_WIN32_PROP, cs->lpCreateParams); } @@ -156,45 +231,46 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) switch (msg) { case WM_CLOSE: - memset(&ev, 0, sizeof ev); + memset(&ev, 0, sizeof(ev)); ev.type = PEAK_EVENT_WINDOW_CLOSE; peak_q_push(&w->q, ev); return 0; case WM_SIZE: { uint32_t width, height; + if (wparam == SIZE_MINIMIZED) return 0; width = (uint32_t)LOWORD(lparam); height = (uint32_t)HIWORD(lparam); if (!width || !height || (width == w->width && height == w->height)) return 0; - if (!peak_win32_buffer(w, width, height)) + if (!peak_internal_win32_buffer(w, width, height)) return 0; - memset(&ev, 0, sizeof ev); + memset(&ev, 0, sizeof(ev)); ev.type = PEAK_EVENT_WINDOW_RESIZE; ev.resize.width = w->width; ev.resize.height = w->height; peak_q_push(&w->q, ev); return 0; } - case WM_KEYDOWN: - case WM_KEYUP: - case WM_SYSKEYDOWN: + case WM_KEYDOWN: /* FALLTHROUGH */ + case WM_KEYUP: /* FALLTHROUGH */ + case WM_SYSKEYDOWN: /* FALLTHROUGH */ case WM_SYSKEYUP: - memset(&ev, 0, sizeof ev); + memset(&ev, 0, sizeof(ev)); ev.type = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; ev.key.key = peak_internal_win32_key_map(wparam); ev.key.mod = peak_internal_win32_mod_map(); peak_q_push(&w->q, ev); return 0; - case WM_MOUSEMOVE: - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_MBUTTONDOWN: + case WM_MOUSEMOVE: /* FALLTHROUGH */ + case WM_LBUTTONDOWN: /* FALLTHROUGH */ + case WM_LBUTTONUP: /* FALLTHROUGH */ + case WM_RBUTTONDOWN: /* FALLTHROUGH */ + case WM_RBUTTONUP: /* FALLTHROUGH */ + case WM_MBUTTONDOWN: /* FALLTHROUGH */ case WM_MBUTTONUP: - memset(&ev, 0, sizeof ev); + memset(&ev, 0, sizeof(ev)); ev.type = PEAK_EVENT_POINTER; ev.pointer.x = (float)(short)LOWORD(lparam); ev.pointer.y = (float)(short)HIWORD(lparam); @@ -215,6 +291,7 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) return 0; case WM_PAINT: { PAINTSTRUCT ps; + peak_user32.BeginPaint(hwnd, &ps); peak_user32.EndPaint(hwnd, &ps); return 0; @@ -255,8 +332,8 @@ peak_platform_init(void) } } if (!peak_win32.class_reg) { - memset(&wc, 0, sizeof wc); - wc.cbSize = sizeof wc; + memset(&wc, 0, sizeof(wc)); + wc.cbSize = sizeof(wc); wc.style = CS_OWNDC; wc.lpfnWndProc = peak_internal_win32_wndproc; wc.hInstance = GetModuleHandleA(NULL); @@ -293,10 +370,9 @@ peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uin if (!peak_user32.CreateWindowExA && !peak_platform_init()) return intern; - w = calloc(1, sizeof *w); - if (!w) + if (!(w = calloc(1, sizeof(*w)))) return intern; - if (!peak_win32_buffer(w, width, height)) { + if (!peak_internal_win32_buffer(w, width, height)) { free(w); return intern; } @@ -335,6 +411,7 @@ static void peak_platform_window_close(PeakWindowInternal *intern) { struct peak_win32_win *w; + if (!intern || !intern->w) return; w = intern->w; @@ -352,7 +429,9 @@ peak_platform_window_close(PeakWindowInternal *intern) static uint32_t * peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height) { - struct peak_win32_win *w = intern ? intern->w : NULL; + struct peak_win32_win *w; + + w = intern ? intern->w : NULL; if (!w) { *width = 0; *height = 0; @@ -366,13 +445,14 @@ peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *h static void peak_platform_window_present(PeakWindowInternal *intern) { - struct peak_win32_win *w = intern ? intern->w : NULL; + struct peak_win32_win *w; BITMAPINFO bmi; + w = intern ? intern->w : NULL; if (!w || !w->hwnd || !w->hdc || !w->buffer) return; - memset(&bmi, 0, sizeof bmi); + memset(&bmi, 0, sizeof(bmi)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = (LONG)w->width; bmi.bmiHeader.biHeight = -(LONG)w->height; @@ -389,8 +469,9 @@ static bool peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) { MSG msg; - struct peak_win32_win *w = intern ? intern->w : NULL; + struct peak_win32_win *w; + w = intern ? intern->w : NULL; if (!w || !w->hwnd) return 0; @@ -402,3 +483,195 @@ peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) } return peak_q_pop(&w->q, ev); } + +static int +peak_internal_winmm_load(void) +{ + HMODULE handle; + + if (peak_winmm.waveOutOpen) + return 1; + if (!(handle = LoadLibraryA(PEAK_WIN32_WINMM))) { + fputs("Failed to load winmm.dll. What system are you fucking using and abusing?", stderr); + return 0; + } +#define X(name, ret, conv, args) peak_winmm.name = (ret (conv *) args)(void *)GetProcAddress(handle, #name); + PEAK_WINMM_API(X) +#undef X +#define X(name, ret, conv, args) || !peak_winmm.name + if (0 PEAK_WINMM_API(X)) { + fputs("Failed to load winmm symbols", stderr); + return 0; + } +#undef X + return 1; +} + +static void +peak_internal_win32_audio_fill(int i) +{ + memset(peak_audio.pcm[i], 0, peak_audio.bytes); + if (peak_audio.fill) + peak_audio.fill(peak_audio.pcm[i], PEAK_AUDIO_FRAMES, peak_audio.userdata); + peak_winmm.waveOutWrite(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR)); +} + +static DWORD WINAPI +peak_internal_win32_audio_thread(LPVOID arg) +{ + int i, did; + + (void)arg; + for (;;) { + did = 0; + if (!peak_audio.run) + break; + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) { + if (peak_audio.hdr[i].dwFlags & WHDR_DONE) { + peak_internal_win32_audio_fill(i); + did = 1; + } + } + if (did) + continue; + WaitForSingleObject(peak_audio.event, INFINITE); + ResetEvent(peak_audio.event); + } + return 0; +} + +static int +peak_platform_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata) +{ + WAVEFORMATEX fmt; + int i; + + if (channels > 32) + return 0; + if (!peak_internal_winmm_load()) + return 0; + + memset(&fmt, 0, sizeof(fmt)); + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.nChannels = (WORD)channels; + fmt.nSamplesPerSec = rate; + fmt.wBitsPerSample = 16; + fmt.nBlockAlign = (WORD)(channels * 2); + fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign; + + peak_audio.event = CreateEventA(NULL, TRUE, FALSE, NULL); + if (!peak_audio.event) + return 0; + if (peak_winmm.waveOutOpen(&peak_audio.out, WAVE_MAPPER, &fmt, (DWORD_PTR)peak_audio.event, 0, CALLBACK_EVENT) != MMSYSERR_NOERROR) { + CloseHandle(peak_audio.event); + peak_audio.event = NULL; + fputs("Failed to open waveOut device", stderr); + return 0; + } + + peak_audio.channels = channels; + peak_audio.bytes = (uint32_t)channels * PEAK_AUDIO_FRAMES * sizeof(int16_t); + peak_audio.fill = fill; + peak_audio.userdata = userdata; + peak_audio.run = 1; + + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) { + if (!(peak_audio.pcm[i] = calloc(1, peak_audio.bytes))) + goto fail; + memset(&peak_audio.hdr[i], 0, sizeof(peak_audio.hdr[i])); + peak_audio.hdr[i].lpData = (LPSTR)peak_audio.pcm[i]; + peak_audio.hdr[i].dwBufferLength = peak_audio.bytes; + if (peak_winmm.waveOutPrepareHeader(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) + goto fail; + peak_audio.hdr[i].dwFlags |= WHDR_DONE; + } + + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) + peak_internal_win32_audio_fill(i); + + peak_audio.thread = CreateThread(NULL, 0, peak_internal_win32_audio_thread, NULL, 0, NULL); + if (!peak_audio.thread) + goto fail; + return 1; + +fail: + peak_platform_audio_stop(); + return 0; +} + +static uint64_t +peak_platform_get_time(void) +{ + LARGE_INTEGER count, freq; + QueryPerformanceCounter(&count); + QueryPerformanceFrequency(&freq); + return (uint64_t)((count.QuadPart * 1000000000ull) / freq.QuadPart); +} + +static void +peak_platform_sleep_ns(int64_t ns) +{ + if (ns <= 0) return; + Sleep((DWORD)(ns / 1000000)); +} + +static const char ** +peak_platform_vulkan_get_extensions(uint32_t *count) +{ + static const char *exts[] = { + "VK_KHR_surface", + "VK_KHR_win32_surface", + }; + if (count) *count = 2; + return exts; +} + +static int +peak_platform_vulkan_create_surface(PeakWindowInternal *intern, void *instance, const void *allocator, void *out_surface) +{ +#ifdef PEAK_VULKAN + VkWin32SurfaceCreateInfoKHR ci; + if (!intern || !intern->w || !intern->w->hwnd) return 0; + memset(&ci, 0, sizeof ci); + ci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + ci.hwnd = intern->w->hwnd; + ci.hinstance = GetModuleHandleA(NULL); + return vkCreateWin32SurfaceKHR((VkInstance)instance, &ci, (const VkAllocationCallbacks *)allocator, (VkSurfaceKHR *)out_surface) == VK_SUCCESS; +#else + (void)intern; (void)instance; (void)allocator; (void)out_surface; + return 0; +#endif +} + +static void +peak_platform_audio_stop(void) +{ + int i; + + peak_audio.run = 0; + if (peak_audio.event) + SetEvent(peak_audio.event); + if (peak_audio.thread) { + WaitForSingleObject(peak_audio.thread, INFINITE); + CloseHandle(peak_audio.thread); + peak_audio.thread = NULL; + } + if (peak_audio.out) { + peak_winmm.waveOutReset(peak_audio.out); + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) { + if (peak_audio.hdr[i].dwFlags & WHDR_PREPARED) + peak_winmm.waveOutUnprepareHeader(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR)); + free(peak_audio.pcm[i]); + peak_audio.pcm[i] = NULL; + memset(&peak_audio.hdr[i], 0, sizeof(peak_audio.hdr[i])); + } + peak_winmm.waveOutClose(peak_audio.out); + peak_audio.out = NULL; + } + if (peak_audio.event) { + CloseHandle(peak_audio.event); + peak_audio.event = NULL; + } + peak_audio.fill = NULL; + peak_audio.userdata = NULL; +} diff --git a/Peak/src/peak.c b/Peak/src/peak.c index 6c4c038..e6e7655 100644 --- a/Peak/src/peak.c +++ b/Peak/src/peak.c @@ -1,3 +1,6 @@ +#include <stdio.h> +#include <stdlib.h> + static uint32_t * peak_window_sync(PeakWindow *win, size_t *width, size_t *height) { @@ -20,6 +23,7 @@ peak_init(void) void peak_quit(void) { + peak_audio_stop(); peak_platform_quit(); } @@ -116,3 +120,79 @@ peak_window_run(PeakWindow *win, int (*peak_tick)(PeakWindow *win, void *userdat while (win->running && win->tick(win, win->userdata)); #endif } + +int +peak_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata) +{ + if (!fill || !channels || !rate) + return 0; + peak_audio_stop(); + return peak_platform_audio_start(channels, rate, fill, userdata); +} + +void +peak_audio_stop(void) +{ + peak_platform_audio_stop(); +} + +uint64_t +peak_get_time(void) +{ + return peak_platform_get_time(); +} + +void +peak_sleep_ns(int64_t ns) +{ + peak_platform_sleep_ns(ns); +} + +int +peak_file_exists(const char *path) +{ + FILE *f; + if (!path) return 0; + f = fopen(path, "rb"); + if (!f) return 0; + fclose(f); + return 1; +} + +void * +peak_file_alloc(const char *path, unsigned long *buf_size) +{ + FILE *f; + long n; + void *p; + if (!path) return NULL; + f = fopen(path, "rb"); + if (!f) return NULL; + if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; } + n = ftell(f); + if (n < 0) { fclose(f); return NULL; } + rewind(f); + p = malloc((size_t)n + (n == 0)); + if (!p) { fclose(f); return NULL; } + if (n && fread(p, 1, (size_t)n, f) != (size_t)n) { + free(p); + fclose(f); + return NULL; + } + fclose(f); + if (buf_size) *buf_size = (unsigned long)n; + return p; +} + +const char ** +peak_vulkan_get_extensions(uint32_t *count) +{ + return peak_platform_vulkan_get_extensions(count); +} + +int +peak_vulkan_create_surface(PeakWindow *win, void *instance, const void *allocator, void *out_surface) +{ + if (!win || !instance || !out_surface) return 0; + return peak_platform_vulkan_create_surface(&win->internal, instance, allocator, out_surface); +} diff --git a/Peak/src/peak_header.h b/Peak/src/peak_header.h index b0a3023..d5dd3b5 100644 --- a/Peak/src/peak_header.h +++ b/Peak/src/peak_header.h @@ -10,8 +10,8 @@ #define PEAK_H #define PEAK_MAJOR "0" -#define PEAK_MINOR "1" -#define PEAK_PATCH "1" +#define PEAK_MINOR "5" +#define PEAK_PATCH "2" /* CHANGE LOG * 0.0.0 - @vasco - prototyping @@ -21,8 +21,13 @@ * 0.2.0 - @vasco - multiple windows, major-ish API changes. * 0.3.0 - @vasco - web via emscripten * 0.4.0 - @vasco - win32 + * 0.5.0 - @vasco - audio start/stop, s16le pull callback + * 0.5.1 - @vasco - log, file, time, vulkan extensions + * 0.5.2 - @vasco - vulkan surface from window */ +#define NANOS_PER_SEC 1000000000ull + #include <assert.h> #include <stdint.h> #include <stddef.h> @@ -75,6 +80,62 @@ #define PEAK extern #endif +typedef enum PeakLogLevel { + P_LOG_LEVEL_FATAL = 0, + P_LOG_LEVEL_ERROR, + P_LOG_LEVEL_WARN, + P_LOG_LEVEL_INFO, + P_LOG_LEVEL_DEBUG, + P_LOG_LEVEL_TRACE, + P_COUNT_LOG_LEVEL +} PeakLogLevel; + +#define P_PREFIX_LEN 7 +static const char *p_prefix[P_COUNT_LOG_LEVEL] = { + [P_LOG_LEVEL_FATAL] = "[FATAL]", + [P_LOG_LEVEL_ERROR] = "[ERROR]", + [P_LOG_LEVEL_WARN] = "[WARNI]", + [P_LOG_LEVEL_INFO] = "[INFOR]", + [P_LOG_LEVEL_DEBUG] = "[DEBUG]", + [P_LOG_LEVEL_TRACE] = "[TRACE]", +}; + +#ifndef P_LOG_WARN_ENABLED +#define P_LOG_WARN_ENABLED 1 +#endif +#ifndef P_LOG_INFO_ENABLED +#define P_LOG_INFO_ENABLED 1 +#endif +#ifndef P_LOG_DEBUG_ENABLED +#define P_LOG_DEBUG_ENABLED 0 +#endif +#ifndef P_LOG_TRACE_ENABLED +#define P_LOG_TRACE_ENABLED 0 +#endif + +#define PFATAL(message, ...) peak_log_printf(P_LOG_LEVEL_FATAL, message, ##__VA_ARGS__) +#define PERROR(message, ...) peak_log_printf(P_LOG_LEVEL_ERROR, message, ##__VA_ARGS__) +#if P_LOG_WARN_ENABLED == 1 +#define PWARN(message, ...) peak_log_printf(P_LOG_LEVEL_WARN, message, ##__VA_ARGS__) +#else +#define PWARN(message, ...) +#endif +#if P_LOG_INFO_ENABLED == 1 +#define PINFO(message, ...) peak_log_printf(P_LOG_LEVEL_INFO, message, ##__VA_ARGS__) +#else +#define PINFO(message, ...) +#endif +#if P_LOG_DEBUG_ENABLED == 1 +#define PDEBUG(message, ...) peak_log_printf(P_LOG_LEVEL_DEBUG, message, ##__VA_ARGS__) +#else +#define PDEBUG(message, ...) +#endif +#if P_LOG_TRACE_ENABLED == 1 +#define PTRACE(message, ...) peak_log_printf(P_LOG_LEVEL_TRACE, message, ##__VA_ARGS__) +#else +#define PTRACE(message, ...) +#endif + typedef enum { PEAK_KEYMOD_NONE = 0, PEAK_KEYMOD_ALT, @@ -130,22 +191,49 @@ typedef struct { typedef struct peak_window_internal_t PeakWindowInternal; typedef struct PeakWindow PeakWindow; -/* Public API */ +/* Initialize the platform. */ PEAK int peak_init(void); // Initialize platform context and load necessary DLLs. PEAK void peak_quit(void); // Close platform context. + +/* Do sexy stuff with the windows. */ PEAK PeakWindow peak_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags); // Open a window. PEAK void peak_window_close(PeakWindow *window); // Close a window. PEAK void peak_window_run(PeakWindow *win, int (*peak_tick)(PeakWindow *win, void *userdata), void *userdata); // Hijacking the main loop makes life easier on platforms like web PEAK int peak_window_epoll(PeakWindow *win, PeakEvent *ev); // Poll a window for events. PEAK uint32_t* peak_window_backbuffer(PeakWindow *win, size_t *width, size_t *height); // Get the windows backbuffer. -PEAK void peak_window_clear(PeakWindow *win, float r, float g, float b, float a); -PEAK void peak_window_present(PeakWindow *win); +PEAK void peak_window_clear(PeakWindow *win, float r, float g, float b, float a); // Clear window to color. +PEAK void peak_window_present(PeakWindow *win); // Present window backbuffer. + +/* Play some tunes. */ +PEAK int peak_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata); // Device pulls interleaved s16le +PEAK void peak_audio_stop(void); // Stop audio. + +/* Time */ +PEAK uint64_t peak_get_time(void); // Get time in nanoseconds. +PEAK void peak_sleep_ns(int64_t ns); // Sleep for nanoseconds!!! + +/* File */ +PEAK int peak_file_exists(const char *path); // Does this file exist? +PEAK void *peak_file_alloc(const char *path, unsigned long *buf_size); // Allocate an entire file. + +/* Graphics API bull... */ +PEAK const char **peak_vulkan_get_extensions(uint32_t *count); // Get vulkan extensions. +PEAK int peak_vulkan_create_surface(PeakWindow *win, void *instance, const void *allocator, void *out_surface); // Create a vulkan surface for a window. Needs PEAK_VULKAN. + +/* Debug your amazing code. */ +PEAK void peak_log_printf(PeakLogLevel level, const char *src, ...); // Printf with log level. +PEAK void *peak_debug_malloc_impl(size_t size, const char *file, int line, const char *func); // Malloc for debuggin. +PEAK void peak_debug_free_impl(void *ptr, const char *file, int line, const char *func); // Free for debugging. +PEAK void *peak_debug_realloc_impl(void *ptr, size_t size, const char *file, int line, const char *func); // Realloc for debugging. +PEAK void peak_debug_memory_report(void); // Report memory. #endif // PEAK_H #ifdef PEAK_IMPLEMENTATION #undef PEAK_IMPLEMENTATION +/* NOTE(vasco): Some platforms require an event queue + * while others have it built in. */ #if defined(PEAK_WIN32) || defined(PEAK_WEB) #define PEAK_Q 64 @@ -182,7 +270,8 @@ peak_q_pop(PeakQ *q, PeakEvent *ev) #include "p_emscripten.c" // <REPLACE> #endif -/* We define PeakWindow only when we know the type of peak_window_internal_t */ +/* NOTE(vasco): We define PeakWindow only when + * we know the type of peak_window_internal_t */ struct PeakWindow { PeakWindowInternal internal; int (*tick)(struct PeakWindow *win, void *userdata); @@ -191,10 +280,12 @@ struct PeakWindow { uint32_t width; uint32_t height; uint32_t bufsize; + uint16_t *audio; // LRLRLR int running; }; +#include "p_log.c" // <REPLACE> #include "peak.c" // <REPLACE> #endif // PEAK_IMPLEMENTATION diff --git a/Peak/src/peak_header.h.out b/Peak/src/peak_header.h.out index 97bb0a3..f5cc83b 100644 --- a/Peak/src/peak_header.h.out +++ b/Peak/src/peak_header.h.out @@ -10,17 +10,24 @@ #define PEAK_H #define PEAK_MAJOR "0" -#define PEAK_MINOR "1" -#define PEAK_PATCH "1" +#define PEAK_MINOR "5" +#define PEAK_PATCH "2" /* CHANGE LOG * 0.0.0 - @vasco - prototyping * 0.1.0 - @vasco - linux x11 that automagically loads X11 DLL * 0.1.1 - @vasco - fixed event handling on linux * 0.1.2 - @vasco - better handling of windows, if multiple windows becomes necessary in the future - * 0.2.0 - @vasco - win32 + * 0.2.0 - @vasco - multiple windows, major-ish API changes. + * 0.3.0 - @vasco - web via emscripten + * 0.4.0 - @vasco - win32 + * 0.5.0 - @vasco - audio start/stop, s16le pull callback + * 0.5.1 - @vasco - log, file, time, vulkan extensions + * 0.5.2 - @vasco - vulkan surface from window */ +#define NANOS_PER_SEC 1000000000ull + #include <assert.h> #include <stdint.h> #include <stddef.h> @@ -73,6 +80,62 @@ #define PEAK extern #endif +typedef enum PeakLogLevel { + P_LOG_LEVEL_FATAL = 0, + P_LOG_LEVEL_ERROR, + P_LOG_LEVEL_WARN, + P_LOG_LEVEL_INFO, + P_LOG_LEVEL_DEBUG, + P_LOG_LEVEL_TRACE, + P_COUNT_LOG_LEVEL +} PeakLogLevel; + +#define P_PREFIX_LEN 7 +static const char *p_prefix[P_COUNT_LOG_LEVEL] = { + [P_LOG_LEVEL_FATAL] = "[FATAL]", + [P_LOG_LEVEL_ERROR] = "[ERROR]", + [P_LOG_LEVEL_WARN] = "[WARNI]", + [P_LOG_LEVEL_INFO] = "[INFOR]", + [P_LOG_LEVEL_DEBUG] = "[DEBUG]", + [P_LOG_LEVEL_TRACE] = "[TRACE]", +}; + +#ifndef P_LOG_WARN_ENABLED +#define P_LOG_WARN_ENABLED 1 +#endif +#ifndef P_LOG_INFO_ENABLED +#define P_LOG_INFO_ENABLED 1 +#endif +#ifndef P_LOG_DEBUG_ENABLED +#define P_LOG_DEBUG_ENABLED 0 +#endif +#ifndef P_LOG_TRACE_ENABLED +#define P_LOG_TRACE_ENABLED 0 +#endif + +#define PFATAL(message, ...) peak_log_printf(P_LOG_LEVEL_FATAL, message, ##__VA_ARGS__) +#define PERROR(message, ...) peak_log_printf(P_LOG_LEVEL_ERROR, message, ##__VA_ARGS__) +#if P_LOG_WARN_ENABLED == 1 +#define PWARN(message, ...) peak_log_printf(P_LOG_LEVEL_WARN, message, ##__VA_ARGS__) +#else +#define PWARN(message, ...) +#endif +#if P_LOG_INFO_ENABLED == 1 +#define PINFO(message, ...) peak_log_printf(P_LOG_LEVEL_INFO, message, ##__VA_ARGS__) +#else +#define PINFO(message, ...) +#endif +#if P_LOG_DEBUG_ENABLED == 1 +#define PDEBUG(message, ...) peak_log_printf(P_LOG_LEVEL_DEBUG, message, ##__VA_ARGS__) +#else +#define PDEBUG(message, ...) +#endif +#if P_LOG_TRACE_ENABLED == 1 +#define PTRACE(message, ...) peak_log_printf(P_LOG_LEVEL_TRACE, message, ##__VA_ARGS__) +#else +#define PTRACE(message, ...) +#endif + typedef enum { PEAK_KEYMOD_NONE = 0, PEAK_KEYMOD_ALT, @@ -128,22 +191,49 @@ typedef struct { typedef struct peak_window_internal_t PeakWindowInternal; typedef struct PeakWindow PeakWindow; -/* Public API */ +/* Initialize the platform. */ PEAK int peak_init(void); // Initialize platform context and load necessary DLLs. PEAK void peak_quit(void); // Close platform context. + +/* Do sexy stuff with the windows. */ PEAK PeakWindow peak_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags); // Open a window. PEAK void peak_window_close(PeakWindow *window); // Close a window. PEAK void peak_window_run(PeakWindow *win, int (*peak_tick)(PeakWindow *win, void *userdata), void *userdata); // Hijacking the main loop makes life easier on platforms like web PEAK int peak_window_epoll(PeakWindow *win, PeakEvent *ev); // Poll a window for events. PEAK uint32_t* peak_window_backbuffer(PeakWindow *win, size_t *width, size_t *height); // Get the windows backbuffer. -PEAK void peak_window_clear(PeakWindow *win, float r, float g, float b, float a); -PEAK void peak_window_present(PeakWindow *win); +PEAK void peak_window_clear(PeakWindow *win, float r, float g, float b, float a); // Clear window to color. +PEAK void peak_window_present(PeakWindow *win); // Present window backbuffer. + +/* Play some tunes. */ +PEAK int peak_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata); // Device pulls interleaved s16le +PEAK void peak_audio_stop(void); // Stop audio. + +/* Time */ +PEAK uint64_t peak_get_time(void); // Get time in nanoseconds. +PEAK void peak_sleep_ns(int64_t ns); // Sleep for nanoseconds!!! + +/* File */ +PEAK int peak_file_exists(const char *path); // Does this file exist? +PEAK void *peak_file_alloc(const char *path, unsigned long *buf_size); // Allocate an entire file. + +/* Graphics API bull... */ +PEAK const char **peak_vulkan_get_extensions(uint32_t *count); // Get vulkan extensions. +PEAK int peak_vulkan_create_surface(PeakWindow *win, void *instance, const void *allocator, void *out_surface); // Create a vulkan surface for a window. Needs PEAK_VULKAN. + +/* Debug your amazing code. */ +PEAK void peak_log_printf(PeakLogLevel level, const char *src, ...); // Printf with log level. +PEAK void *peak_debug_malloc_impl(size_t size, const char *file, int line, const char *func); // Malloc for debuggin. +PEAK void peak_debug_free_impl(void *ptr, const char *file, int line, const char *func); // Free for debugging. +PEAK void *peak_debug_realloc_impl(void *ptr, size_t size, const char *file, int line, const char *func); // Realloc for debugging. +PEAK void peak_debug_memory_report(void); // Report memory. #endif // PEAK_H #ifdef PEAK_IMPLEMENTATION #undef PEAK_IMPLEMENTATION +/* NOTE(vasco): Some platforms require an event queue + * while others have it built in. */ #if defined(PEAK_WIN32) || defined(PEAK_WEB) #define PEAK_Q 64 @@ -174,43 +264,68 @@ peak_q_pop(PeakQ *q, PeakEvent *ev) #if defined(PEAK_WIN32) /* --- Start of p_win32.c --- */ +/* + * Win32 window, input, StretchDIBits present, and waveOut audio. + * user32.dll, gdi32.dll, and winmm.dll are loaded at runtime. + * + * * 0.4.0 - @vasco - win32 + * * 0.5.0 - @vasco - audio start/stop, s16le pull callback + */ + #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +/* windows.h must precede mmsystem.h */ #include <windows.h> +#include <mmsystem.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#ifdef PEAK_VULKAN +#define VK_USE_PLATFORM_WIN32_KHR +#include <vulkan/vulkan.h> +#endif #define PEAK_WIN32_USER32 "user32.dll" #define PEAK_WIN32_GDI32 "gdi32.dll" +#define PEAK_WIN32_WINMM "winmm.dll" #define PEAK_WIN32_CLASS "PeakWindow" #define PEAK_WIN32_PROP "Peak" +#define PEAK_AUDIO_FRAMES 256 +#define PEAK_AUDIO_BUFFERS 2 #define PEAK_USER32_API(X) \ - X(RegisterClassExA, ATOM, WINAPI, (const WNDCLASSEXA *)) \ - X(UnregisterClassA, BOOL, WINAPI, (LPCSTR, HINSTANCE)) \ - X(CreateWindowExA, HWND, WINAPI, (DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)) \ - X(DestroyWindow, BOOL, WINAPI, (HWND)) \ - X(ShowWindow, BOOL, WINAPI, (HWND, int)) \ - X(GetDC, HDC, WINAPI, (HWND)) \ - X(ReleaseDC, int, WINAPI, (HWND, HDC)) \ - X(PeekMessageA, BOOL, WINAPI, (LPMSG, HWND, UINT, UINT, UINT)) \ - X(TranslateMessage, BOOL, WINAPI, (const MSG *)) \ - X(DispatchMessageA, LRESULT, WINAPI, (const MSG *)) \ - X(DefWindowProcA, LRESULT, WINAPI, (HWND, UINT, WPARAM, LPARAM)) \ - X(SetPropA, BOOL, WINAPI, (HWND, LPCSTR, HANDLE)) \ - X(GetPropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ - X(RemovePropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ - X(BeginPaint, HDC, WINAPI, (HWND, LPPAINTSTRUCT)) \ - X(EndPaint, BOOL, WINAPI, (HWND, const PAINTSTRUCT *)) \ - X(AdjustWindowRectEx, BOOL, WINAPI, (LPRECT, DWORD, BOOL, DWORD)) \ - X(LoadCursorA, HCURSOR, WINAPI, (HINSTANCE, LPCSTR)) \ - X(GetKeyState, SHORT, WINAPI, (int)) + X(RegisterClassExA, ATOM, WINAPI, (const WNDCLASSEXA *)) \ + X(UnregisterClassA, BOOL, WINAPI, (LPCSTR, HINSTANCE)) \ + X(CreateWindowExA, HWND, WINAPI, (DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)) \ + X(DestroyWindow, BOOL, WINAPI, (HWND)) \ + X(ShowWindow, BOOL, WINAPI, (HWND, int)) \ + X(GetDC, HDC, WINAPI, (HWND)) \ + X(ReleaseDC, int, WINAPI, (HWND, HDC)) \ + X(PeekMessageA, BOOL, WINAPI, (LPMSG, HWND, UINT, UINT, UINT)) \ + X(TranslateMessage, BOOL, WINAPI, (const MSG *)) \ + X(DispatchMessageA, LRESULT, WINAPI, (const MSG *)) \ + X(DefWindowProcA, LRESULT, WINAPI, (HWND, UINT, WPARAM, LPARAM)) \ + X(SetPropA, BOOL, WINAPI, (HWND, LPCSTR, HANDLE)) \ + X(GetPropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(RemovePropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(BeginPaint, HDC, WINAPI, (HWND, LPPAINTSTRUCT)) \ + X(EndPaint, BOOL, WINAPI, (HWND, const PAINTSTRUCT *)) \ + X(AdjustWindowRectEx, BOOL, WINAPI, (LPRECT, DWORD, BOOL, DWORD)) \ + X(LoadCursorA, HCURSOR, WINAPI, (HINSTANCE, LPCSTR)) \ + X(GetKeyState, SHORT, WINAPI, (int)) #define PEAK_GDI32_API(X) \ - X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD)) + X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD)) + +#define PEAK_WINMM_API(X) \ + X(waveOutOpen, MMRESULT, WINAPI, (LPHWAVEOUT, UINT, LPCWAVEFORMATEX, DWORD_PTR, DWORD_PTR, DWORD)) \ + X(waveOutClose, MMRESULT, WINAPI, (HWAVEOUT)) \ + X(waveOutPrepareHeader, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \ + X(waveOutUnprepareHeader, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \ + X(waveOutWrite, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \ + X(waveOutReset, MMRESULT, WINAPI, (HWAVEOUT)) typedef struct { #define X(name, ret, conv, args) ret (conv *name) args; @@ -225,6 +340,25 @@ typedef struct { } PeakGdi32Api; typedef struct { +#define X(name, ret, conv, args) ret (conv *name) args; + PEAK_WINMM_API(X) +#undef X +} PeakWinmmApi; + +typedef struct { + volatile int run; + HANDLE thread; + HANDLE event; + HWAVEOUT out; + WAVEHDR hdr[PEAK_AUDIO_BUFFERS]; + int16_t *pcm[PEAK_AUDIO_BUFFERS]; + uint32_t channels; + uint32_t bytes; + void (*fill)(int16_t *out, size_t frames, void *userdata); + void *userdata; +} PeakAudio; + +typedef struct { HMODULE user32; HMODULE gdi32; int class_reg; @@ -243,9 +377,28 @@ struct peak_window_internal_t { struct peak_win32_win *w; }; +static int peak_internal_user32_load(HMODULE handle); +static int peak_internal_gdi32_load(HMODULE handle); +static PeakKeyCode peak_internal_win32_key_map(WPARAM vk); +static PeakKeyMod peak_internal_win32_mod_map(void); +static int peak_internal_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height); +static LRESULT CALLBACK peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); +static int peak_platform_init(void); +static void peak_platform_quit(void); +static PeakWindowInternal peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags); +static void peak_platform_window_close(PeakWindowInternal *intern); +static uint32_t *peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height); +static void peak_platform_window_present(PeakWindowInternal *intern); +static bool peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev); +static int peak_internal_winmm_load(void); +static void peak_internal_win32_audio_fill(int i); +static DWORD WINAPI peak_internal_win32_audio_thread(LPVOID arg); + static PeakWin32 peak_win32; static PeakUser32Api peak_user32; static PeakGdi32Api peak_gdi32; +static PeakWinmmApi peak_winmm; +static PeakAudio peak_audio; static int peak_internal_user32_load(HMODULE handle) @@ -279,34 +432,45 @@ peak_internal_win32_key_map(WPARAM vk) if (vk >= 'A' && vk <= 'Z') return (PeakKeyCode)(PEAK_KEY_A + (int)(vk - 'A')); switch (vk) { - case VK_UP: return PEAK_KEY_UP; - case VK_DOWN: return PEAK_KEY_DOWN; - case VK_LEFT: return PEAK_KEY_LEFT; - case VK_RIGHT: return PEAK_KEY_RIGHT; - case VK_SPACE: return PEAK_KEY_SPACE; - case VK_ESCAPE: return PEAK_KEY_ESCAPE; - case VK_RETURN: return PEAK_KEY_ENTER; - default: return PEAK_KEY_UNKNOWN; + case VK_UP: + return PEAK_KEY_UP; + case VK_DOWN: + return PEAK_KEY_DOWN; + case VK_LEFT: + return PEAK_KEY_LEFT; + case VK_RIGHT: + return PEAK_KEY_RIGHT; + case VK_SPACE: + return PEAK_KEY_SPACE; + case VK_ESCAPE: + return PEAK_KEY_ESCAPE; + case VK_RETURN: + return PEAK_KEY_ENTER; + default: + return PEAK_KEY_UNKNOWN; } } static PeakKeyMod peak_internal_win32_mod_map(void) { - if (peak_user32.GetKeyState(VK_CONTROL) & 0x8000) return PEAK_KEYMOD_CTRL; - if (peak_user32.GetKeyState(VK_MENU) & 0x8000) return PEAK_KEYMOD_ALT; - if (peak_user32.GetKeyState(VK_SHIFT) & 0x8000) return PEAK_KEYMOD_SHIFT; - if (peak_user32.GetKeyState(VK_CAPITAL) & 1) return PEAK_KEYMOD_CAPS; + if (peak_user32.GetKeyState(VK_CONTROL) & 0x8000) + return PEAK_KEYMOD_CTRL; + if (peak_user32.GetKeyState(VK_MENU) & 0x8000) + return PEAK_KEYMOD_ALT; + if (peak_user32.GetKeyState(VK_SHIFT) & 0x8000) + return PEAK_KEYMOD_SHIFT; + if (peak_user32.GetKeyState(VK_CAPITAL) & 1) + return PEAK_KEYMOD_CAPS; return (PeakKeyMod)0; } static int -peak_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height) +peak_internal_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height) { uint32_t *buffer; - buffer = calloc((size_t)width * height, sizeof *buffer); - if (!buffer) + if (!(buffer = calloc((size_t)width * height, sizeof(*buffer)))) return 0; free(w->buffer); w->buffer = buffer; @@ -320,9 +484,10 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { struct peak_win32_win *w; PeakEvent ev; + CREATESTRUCTA *cs; if (msg == WM_NCCREATE) { - CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam; + cs = (CREATESTRUCTA *)lparam; peak_user32.SetPropA(hwnd, PEAK_WIN32_PROP, cs->lpCreateParams); } @@ -332,45 +497,46 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) switch (msg) { case WM_CLOSE: - memset(&ev, 0, sizeof ev); + memset(&ev, 0, sizeof(ev)); ev.type = PEAK_EVENT_WINDOW_CLOSE; peak_q_push(&w->q, ev); return 0; case WM_SIZE: { uint32_t width, height; + if (wparam == SIZE_MINIMIZED) return 0; width = (uint32_t)LOWORD(lparam); height = (uint32_t)HIWORD(lparam); if (!width || !height || (width == w->width && height == w->height)) return 0; - if (!peak_win32_buffer(w, width, height)) + if (!peak_internal_win32_buffer(w, width, height)) return 0; - memset(&ev, 0, sizeof ev); + memset(&ev, 0, sizeof(ev)); ev.type = PEAK_EVENT_WINDOW_RESIZE; ev.resize.width = w->width; ev.resize.height = w->height; peak_q_push(&w->q, ev); return 0; } - case WM_KEYDOWN: - case WM_KEYUP: - case WM_SYSKEYDOWN: + case WM_KEYDOWN: /* FALLTHROUGH */ + case WM_KEYUP: /* FALLTHROUGH */ + case WM_SYSKEYDOWN: /* FALLTHROUGH */ case WM_SYSKEYUP: - memset(&ev, 0, sizeof ev); + memset(&ev, 0, sizeof(ev)); ev.type = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; ev.key.key = peak_internal_win32_key_map(wparam); ev.key.mod = peak_internal_win32_mod_map(); peak_q_push(&w->q, ev); return 0; - case WM_MOUSEMOVE: - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_MBUTTONDOWN: + case WM_MOUSEMOVE: /* FALLTHROUGH */ + case WM_LBUTTONDOWN: /* FALLTHROUGH */ + case WM_LBUTTONUP: /* FALLTHROUGH */ + case WM_RBUTTONDOWN: /* FALLTHROUGH */ + case WM_RBUTTONUP: /* FALLTHROUGH */ + case WM_MBUTTONDOWN: /* FALLTHROUGH */ case WM_MBUTTONUP: - memset(&ev, 0, sizeof ev); + memset(&ev, 0, sizeof(ev)); ev.type = PEAK_EVENT_POINTER; ev.pointer.x = (float)(short)LOWORD(lparam); ev.pointer.y = (float)(short)HIWORD(lparam); @@ -391,6 +557,7 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) return 0; case WM_PAINT: { PAINTSTRUCT ps; + peak_user32.BeginPaint(hwnd, &ps); peak_user32.EndPaint(hwnd, &ps); return 0; @@ -431,8 +598,8 @@ peak_platform_init(void) } } if (!peak_win32.class_reg) { - memset(&wc, 0, sizeof wc); - wc.cbSize = sizeof wc; + memset(&wc, 0, sizeof(wc)); + wc.cbSize = sizeof(wc); wc.style = CS_OWNDC; wc.lpfnWndProc = peak_internal_win32_wndproc; wc.hInstance = GetModuleHandleA(NULL); @@ -469,10 +636,9 @@ peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uin if (!peak_user32.CreateWindowExA && !peak_platform_init()) return intern; - w = calloc(1, sizeof *w); - if (!w) + if (!(w = calloc(1, sizeof(*w)))) return intern; - if (!peak_win32_buffer(w, width, height)) { + if (!peak_internal_win32_buffer(w, width, height)) { free(w); return intern; } @@ -511,6 +677,7 @@ static void peak_platform_window_close(PeakWindowInternal *intern) { struct peak_win32_win *w; + if (!intern || !intern->w) return; w = intern->w; @@ -528,7 +695,9 @@ peak_platform_window_close(PeakWindowInternal *intern) static uint32_t * peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height) { - struct peak_win32_win *w = intern ? intern->w : NULL; + struct peak_win32_win *w; + + w = intern ? intern->w : NULL; if (!w) { *width = 0; *height = 0; @@ -542,13 +711,14 @@ peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *h static void peak_platform_window_present(PeakWindowInternal *intern) { - struct peak_win32_win *w = intern ? intern->w : NULL; + struct peak_win32_win *w; BITMAPINFO bmi; + w = intern ? intern->w : NULL; if (!w || !w->hwnd || !w->hdc || !w->buffer) return; - memset(&bmi, 0, sizeof bmi); + memset(&bmi, 0, sizeof(bmi)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = (LONG)w->width; bmi.bmiHeader.biHeight = -(LONG)w->height; @@ -565,8 +735,9 @@ static bool peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) { MSG msg; - struct peak_win32_win *w = intern ? intern->w : NULL; + struct peak_win32_win *w; + w = intern ? intern->w : NULL; if (!w || !w->hwnd) return 0; @@ -578,6 +749,198 @@ peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) } return peak_q_pop(&w->q, ev); } + +static int +peak_internal_winmm_load(void) +{ + HMODULE handle; + + if (peak_winmm.waveOutOpen) + return 1; + if (!(handle = LoadLibraryA(PEAK_WIN32_WINMM))) { + fputs("Failed to load winmm.dll. What system are you fucking using and abusing?", stderr); + return 0; + } +#define X(name, ret, conv, args) peak_winmm.name = (ret (conv *) args)(void *)GetProcAddress(handle, #name); + PEAK_WINMM_API(X) +#undef X +#define X(name, ret, conv, args) || !peak_winmm.name + if (0 PEAK_WINMM_API(X)) { + fputs("Failed to load winmm symbols", stderr); + return 0; + } +#undef X + return 1; +} + +static void +peak_internal_win32_audio_fill(int i) +{ + memset(peak_audio.pcm[i], 0, peak_audio.bytes); + if (peak_audio.fill) + peak_audio.fill(peak_audio.pcm[i], PEAK_AUDIO_FRAMES, peak_audio.userdata); + peak_winmm.waveOutWrite(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR)); +} + +static DWORD WINAPI +peak_internal_win32_audio_thread(LPVOID arg) +{ + int i, did; + + (void)arg; + for (;;) { + did = 0; + if (!peak_audio.run) + break; + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) { + if (peak_audio.hdr[i].dwFlags & WHDR_DONE) { + peak_internal_win32_audio_fill(i); + did = 1; + } + } + if (did) + continue; + WaitForSingleObject(peak_audio.event, INFINITE); + ResetEvent(peak_audio.event); + } + return 0; +} + +static int +peak_platform_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata) +{ + WAVEFORMATEX fmt; + int i; + + if (channels > 32) + return 0; + if (!peak_internal_winmm_load()) + return 0; + + memset(&fmt, 0, sizeof(fmt)); + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.nChannels = (WORD)channels; + fmt.nSamplesPerSec = rate; + fmt.wBitsPerSample = 16; + fmt.nBlockAlign = (WORD)(channels * 2); + fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign; + + peak_audio.event = CreateEventA(NULL, TRUE, FALSE, NULL); + if (!peak_audio.event) + return 0; + if (peak_winmm.waveOutOpen(&peak_audio.out, WAVE_MAPPER, &fmt, (DWORD_PTR)peak_audio.event, 0, CALLBACK_EVENT) != MMSYSERR_NOERROR) { + CloseHandle(peak_audio.event); + peak_audio.event = NULL; + fputs("Failed to open waveOut device", stderr); + return 0; + } + + peak_audio.channels = channels; + peak_audio.bytes = (uint32_t)channels * PEAK_AUDIO_FRAMES * sizeof(int16_t); + peak_audio.fill = fill; + peak_audio.userdata = userdata; + peak_audio.run = 1; + + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) { + if (!(peak_audio.pcm[i] = calloc(1, peak_audio.bytes))) + goto fail; + memset(&peak_audio.hdr[i], 0, sizeof(peak_audio.hdr[i])); + peak_audio.hdr[i].lpData = (LPSTR)peak_audio.pcm[i]; + peak_audio.hdr[i].dwBufferLength = peak_audio.bytes; + if (peak_winmm.waveOutPrepareHeader(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) + goto fail; + peak_audio.hdr[i].dwFlags |= WHDR_DONE; + } + + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) + peak_internal_win32_audio_fill(i); + + peak_audio.thread = CreateThread(NULL, 0, peak_internal_win32_audio_thread, NULL, 0, NULL); + if (!peak_audio.thread) + goto fail; + return 1; + +fail: + peak_platform_audio_stop(); + return 0; +} + +static uint64_t +peak_platform_get_time(void) +{ + LARGE_INTEGER count, freq; + QueryPerformanceCounter(&count); + QueryPerformanceFrequency(&freq); + return (uint64_t)((count.QuadPart * 1000000000ull) / freq.QuadPart); +} + +static void +peak_platform_sleep_ns(int64_t ns) +{ + if (ns <= 0) return; + Sleep((DWORD)(ns / 1000000)); +} + +static const char ** +peak_platform_vulkan_get_extensions(uint32_t *count) +{ + static const char *exts[] = { + "VK_KHR_surface", + "VK_KHR_win32_surface", + }; + if (count) *count = 2; + return exts; +} + +static int +peak_platform_vulkan_create_surface(PeakWindowInternal *intern, void *instance, const void *allocator, void *out_surface) +{ +#ifdef PEAK_VULKAN + VkWin32SurfaceCreateInfoKHR ci; + if (!intern || !intern->w || !intern->w->hwnd) return 0; + memset(&ci, 0, sizeof ci); + ci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + ci.hwnd = intern->w->hwnd; + ci.hinstance = GetModuleHandleA(NULL); + return vkCreateWin32SurfaceKHR((VkInstance)instance, &ci, (const VkAllocationCallbacks *)allocator, (VkSurfaceKHR *)out_surface) == VK_SUCCESS; +#else + (void)intern; (void)instance; (void)allocator; (void)out_surface; + return 0; +#endif +} + +static void +peak_platform_audio_stop(void) +{ + int i; + + peak_audio.run = 0; + if (peak_audio.event) + SetEvent(peak_audio.event); + if (peak_audio.thread) { + WaitForSingleObject(peak_audio.thread, INFINITE); + CloseHandle(peak_audio.thread); + peak_audio.thread = NULL; + } + if (peak_audio.out) { + peak_winmm.waveOutReset(peak_audio.out); + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) { + if (peak_audio.hdr[i].dwFlags & WHDR_PREPARED) + peak_winmm.waveOutUnprepareHeader(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR)); + free(peak_audio.pcm[i]); + peak_audio.pcm[i] = NULL; + memset(&peak_audio.hdr[i], 0, sizeof(peak_audio.hdr[i])); + } + peak_winmm.waveOutClose(peak_audio.out); + peak_audio.out = NULL; + } + if (peak_audio.event) { + CloseHandle(peak_audio.event); + peak_audio.event = NULL; + } + peak_audio.fill = NULL; + peak_audio.userdata = NULL; +} /* --- End of p_win32.c --- */ #elif defined(PEAK_LINUX) /* --- Start of p_linux.c --- */ @@ -590,10 +953,24 @@ peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) #include <X11/Xutil.h> #include <X11/keysym.h> #include <dlfcn.h> +#include <pthread.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <time.h> +#ifdef PEAK_VULKAN +#define VK_USE_PLATFORM_XLIB_KHR +#include <vulkan/vulkan.h> +#endif #define PEAK_X11_LINUX "libX11.so.6" +#define PEAK_PULSE_LINUX "libpulse-simple.so.0" +#define PEAK_AUDIO_FRAMES 256 + +#define PEAK_PULSE_API(X) \ + X(pa_simple_new, void *, (const char *, const char *, int, const char *, const char *, const void *, const void *, const void *, int *)) \ + X(pa_simple_free, void, (void *)) \ + X(pa_simple_write, int, (void *, const void *, size_t, int *)) #define PEAK_X11_API(X) \ X(XOpenDisplay, Display *, (const char *)) \ @@ -620,10 +997,33 @@ typedef struct { } PeakX11Api; typedef struct { +#define X(name, ret, args) ret (*name) args; + PEAK_PULSE_API(X) +#undef X +} PeakPulseApi; + +typedef struct { + int format; + uint32_t rate; + uint8_t channels; +} PeakPaSampleSpec; + +typedef struct { Display *display; Atom wm_delete_window; } PeakLinux; +typedef struct { + volatile int run; + int thread_on; + pthread_t thread; + void *stream; + int16_t *buf; + uint32_t channels; + void (*fill)(int16_t *out, size_t frames, void *userdata); + void *userdata; +} PeakAudio; + struct peak_window_internal_t { Window window; GC gfx_ctx; @@ -635,6 +1035,8 @@ struct peak_window_internal_t { static PeakLinux peak_linux; static PeakX11Api peak_x11; +static PeakPulseApi peak_pulse; +static PeakAudio peak_audio; static int peak_internal_x11_load(void *handle) @@ -877,6 +1279,154 @@ peak_platform_epoll(PeakWindowInternal *w, PeakEvent *ev) } return 0; } + +static int +peak_internal_pulse_load(void) +{ + void *handle; + + if (peak_pulse.pa_simple_new) + return 1; + if (!(handle = dlopen(PEAK_PULSE_LINUX, RTLD_LOCAL | RTLD_NOW))) { + fputs("Failed to load PulseAudio library. What system are you fucking using and abusing?", stderr); + return 0; + } +#define X(name, ret, args) peak_pulse.name = (ret (*) args)dlsym(handle, #name); + PEAK_PULSE_API(X) +#undef X +#define X(name, ret, args) || !peak_pulse.name + if (0 PEAK_PULSE_API(X)) { + fputs("Failed to load PulseAudio symbols", stderr); + return 0; + } +#undef X + return 1; +} + +static void * +peak_internal_audio_thread(void *arg) +{ + size_t n; + int error; + + (void)arg; + n = (size_t)peak_audio.channels * PEAK_AUDIO_FRAMES; + while (peak_audio.run) { + memset(peak_audio.buf, 0, n * sizeof(int16_t)); + if (peak_audio.fill) + peak_audio.fill(peak_audio.buf, PEAK_AUDIO_FRAMES, peak_audio.userdata); + if (peak_pulse.pa_simple_write(peak_audio.stream, peak_audio.buf, n * sizeof(int16_t), &error) < 0) + break; + } + return NULL; +} + +static int +peak_platform_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata) +{ + PeakPaSampleSpec ss; + int error; + + if (channels > 32) + return 0; + if (!peak_internal_pulse_load()) + return 0; + + ss.format = 3; /* PA_SAMPLE_S16LE */ + ss.rate = rate; + ss.channels = (uint8_t)channels; + peak_audio.stream = peak_pulse.pa_simple_new(NULL, "Peak", 1, NULL, "playback", &ss, NULL, NULL, &error); + if (!peak_audio.stream) { + fputs("Failed to open PulseAudio stream", stderr); + return 0; + } + if (!(peak_audio.buf = calloc((size_t)channels * PEAK_AUDIO_FRAMES, sizeof(int16_t)))) { + peak_pulse.pa_simple_free(peak_audio.stream); + peak_audio.stream = NULL; + return 0; + } + peak_audio.fill = fill; + peak_audio.userdata = userdata; + peak_audio.channels = channels; + peak_audio.run = 1; + if (pthread_create(&peak_audio.thread, NULL, peak_internal_audio_thread, NULL) != 0) { + peak_audio.thread_on = 0; + peak_audio.run = 0; + free(peak_audio.buf); + peak_audio.buf = NULL; + peak_pulse.pa_simple_free(peak_audio.stream); + peak_audio.stream = NULL; + return 0; + } + peak_audio.thread_on = 1; + return 1; +} + +static uint64_t +peak_platform_get_time(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * NANOS_PER_SEC + (uint64_t)ts.tv_nsec; +} + +static void +peak_platform_sleep_ns(int64_t ns) +{ + struct timespec ts; + if (ns <= 0) return; + ts.tv_sec = ns / 1000000000ll; + ts.tv_nsec = ns % 1000000000ll; + nanosleep(&ts, NULL); +} + +static const char ** +peak_platform_vulkan_get_extensions(uint32_t *count) +{ + static const char *exts[] = { + "VK_KHR_surface", + "VK_KHR_xlib_surface", + }; + if (count) *count = 2; + return exts; +} + +static int +peak_platform_vulkan_create_surface(PeakWindowInternal *w, void *instance, const void *allocator, void *out_surface) +{ +#ifdef PEAK_VULKAN + VkXlibSurfaceCreateInfoKHR ci; + if (!w || !peak_linux.display || !w->window) return 0; + memset(&ci, 0, sizeof ci); + ci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + ci.dpy = peak_linux.display; + ci.window = w->window; + return vkCreateXlibSurfaceKHR((VkInstance)instance, &ci, (const VkAllocationCallbacks *)allocator, (VkSurfaceKHR *)out_surface) == VK_SUCCESS; +#else + (void)w; (void)instance; (void)allocator; (void)out_surface; + return 0; +#endif +} + +static void +peak_platform_audio_stop(void) +{ + if (!peak_audio.run && !peak_audio.stream) + return; + peak_audio.run = 0; + if (peak_audio.thread_on) { + pthread_join(peak_audio.thread, NULL); + peak_audio.thread_on = 0; + } + if (peak_audio.stream) { + peak_pulse.pa_simple_free(peak_audio.stream); + peak_audio.stream = NULL; + } + free(peak_audio.buf); + peak_audio.buf = NULL; + peak_audio.fill = NULL; + peak_audio.userdata = NULL; +} /* --- End of p_linux.c --- */ #elif defined(PEAK_WEB) /* --- Start of p_emscripten.c --- */ @@ -1068,10 +1618,129 @@ peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) struct peak_web_win *w = intern ? intern->w : NULL; return w ? peak_q_pop(&w->q, ev) : 0; } + +#define PEAK_AUDIO_FRAMES 1024 + +static struct { + int run; + uint32_t channels; + int16_t *buf; + void (*fill)(int16_t *out, size_t frames, void *userdata); + void *userdata; +} peak_web_audio; + +void EMSCRIPTEN_KEEPALIVE +peak_internal_web_audio_fill(int16_t *out, int frames) +{ + size_t n; + + if (!peak_web_audio.run || !out || frames <= 0) + return; + n = (size_t)frames * peak_web_audio.channels; + memset(out, 0, n * sizeof(int16_t)); + if (peak_web_audio.fill) + peak_web_audio.fill(out, (size_t)frames, peak_web_audio.userdata); +} + +EM_JS(int, peak_web_audio_dom_start, (int channels, int rate, int frames, uintptr_t ptr), { + var AC = window.AudioContext || window.webkitAudioContext; + var ctx, proc, i, c, heap, off, ch; + if (!AC || Module._peak_web_audio) + return 0; + ctx = new AC({ sampleRate: rate }); + if (ctx.sampleRate !== rate) { + ctx.close(); + return 0; + } + proc = ctx.createScriptProcessor(frames, 0, channels); + proc.onaudioprocess = function(e) { + Module._peak_internal_web_audio_fill(ptr, frames); + heap = Module.HEAP16; + off = ptr >> 1; + for (c = 0; c < channels; c++) { + ch = e.outputBuffer.getChannelData(c); + for (i = 0; i < frames; i++) + ch[i] = heap[off + i * channels + c] / 32768.0; + } + }; + proc.connect(ctx.destination); + ctx.resume(); + Module._peak_web_audio = { ctx: ctx, proc: proc }; + return 1; +}); + +EM_JS(void, peak_web_audio_dom_stop, (void), { + var a = Module._peak_web_audio; + if (!a) + return; + a.proc.disconnect(); + a.ctx.close(); + Module._peak_web_audio = null; +}); + +static int +peak_platform_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata) +{ + if (channels > 32) + return 0; + if (!(peak_web_audio.buf = calloc((size_t)channels * PEAK_AUDIO_FRAMES, sizeof(int16_t)))) + return 0; + peak_web_audio.fill = fill; + peak_web_audio.userdata = userdata; + peak_web_audio.channels = channels; + peak_web_audio.run = 1; + if (!peak_web_audio_dom_start((int)channels, (int)rate, PEAK_AUDIO_FRAMES, (uintptr_t)peak_web_audio.buf)) { + free(peak_web_audio.buf); + peak_web_audio.buf = NULL; + peak_web_audio.run = 0; + peak_web_audio.fill = NULL; + return 0; + } + return 1; +} + +static uint64_t +peak_platform_get_time(void) +{ + return (uint64_t)(emscripten_get_now() * 1000000.0); +} + +static void +peak_platform_sleep_ns(int64_t ns) +{ + if (ns <= 0) return; + emscripten_sleep((unsigned)(ns / 1000000)); +} + +static const char ** +peak_platform_vulkan_get_extensions(uint32_t *count) +{ + if (count) *count = 0; + return NULL; +} + +static int +peak_platform_vulkan_create_surface(PeakWindowInternal *w, void *instance, const void *allocator, void *out_surface) +{ + (void)w; (void)instance; (void)allocator; (void)out_surface; + return 0; +} + +static void +peak_platform_audio_stop(void) +{ + peak_web_audio.run = 0; + peak_web_audio_dom_stop(); + free(peak_web_audio.buf); + peak_web_audio.buf = NULL; + peak_web_audio.fill = NULL; + peak_web_audio.userdata = NULL; +} /* --- End of p_emscripten.c --- */ #endif -/* We define PeakWindow only when we know the type of peak_window_internal_t */ +/* NOTE(vasco): We define PeakWindow only when + * we know the type of peak_window_internal_t */ struct PeakWindow { PeakWindowInternal internal; int (*tick)(struct PeakWindow *win, void *userdata); @@ -1080,11 +1749,170 @@ struct PeakWindow { uint32_t width; uint32_t height; uint32_t bufsize; + uint16_t *audio; // LRLRLR int running; }; +/* --- Start of p_log.c --- */ +/* + * Logging! + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> + +#define PEAK_MAX_PRINTF 1024 +#define PEAK_MAX_ALLOCS 512 + +typedef struct { + void *ptr; + size_t size; + const char *file; + const char *func; + int line; +} PeakDebugMemoryInfo; + +static uint64_t peak_alloc_count = 0; +static PeakDebugMemoryInfo peak_ptr_array[PEAK_MAX_ALLOCS]; + +void +peak_log_printf(PeakLogLevel level, const char *src, ...) +{ + char out[PEAK_MAX_PRINTF]; + va_list ap; + int len; + size_t offset = P_PREFIX_LEN + 1; + + if (level < 0 || level >= P_COUNT_LOG_LEVEL) + level = P_LOG_LEVEL_ERROR; + memcpy(out, p_prefix[level], P_PREFIX_LEN); + out[P_PREFIX_LEN] = ' '; + va_start(ap, src); + len = vsnprintf(out + offset, PEAK_MAX_PRINTF - offset, src, ap); + va_end(ap); + if (len < 0) len = 0; + if (offset + (size_t)len >= PEAK_MAX_PRINTF) + len = (int)(PEAK_MAX_PRINTF - offset - 1); + out[offset + (size_t)len] = '\n'; + fwrite(out, 1, offset + (size_t)len + 1, (level <= P_LOG_LEVEL_ERROR) ? stderr : stdout); +} + +void * +peak_debug_malloc_impl(size_t size, const char *file, int line, const char *func) +{ + void *ptr = malloc(size); + printf("[ALLOC] %p (%zu bytes) -> %s:%d %s()\n", ptr, size, file, line, func); + + if (ptr) { + if (peak_alloc_count < PEAK_MAX_ALLOCS) { + peak_ptr_array[peak_alloc_count++] = (PeakDebugMemoryInfo){ + .ptr = ptr, + .size = size, + .file = file, + .func = func, + .line = line + }; + } else { + fprintf(stderr, "[ERROR] Debug allocator tracking capacity (%d) exceeded!\n", PEAK_MAX_ALLOCS); + } + } + return ptr; +} + +void +peak_debug_free_impl(void *ptr, const char *file, int line, const char *func) +{ + printf("[FREE] %p -> %s:%d %s()\n", ptr, file, line, func); + + if (!ptr) return; + + bool found = false; + uint64_t index = 0; + + for (index = 0; index < peak_alloc_count; ++index) { + if (peak_ptr_array[index].ptr == ptr) { + found = true; + break; + } + } + + if (found) { + for (uint64_t j = index; j < peak_alloc_count - 1; ++j) { + peak_ptr_array[j] = peak_ptr_array[j + 1]; + } + peak_alloc_count--; + } else { + fprintf(stderr, "[WARNING] Attempted to free untracked/double-freed pointer %p at %s:%d %s()\n", + ptr, file, line, func); + } + + free(ptr); +} + +void * +peak_debug_realloc_impl(void *ptr, size_t size, const char *file, int line, const char *func) +{ + if (!ptr) { + return peak_debug_malloc_impl(size, file, line, func); + } + if (size == 0) { + peak_debug_free_impl(ptr, file, line, func); + return NULL; + } + + uintptr_t old_addr = (uintptr_t)ptr; + void *new_ptr = realloc(ptr, size); + printf("[REALLOC] %p -> %p (%zu bytes) -> %s:%d %s()\n", (void *)old_addr, new_ptr, size, file, line, func); + + if (new_ptr) { + bool found = false; + for (uint64_t i = 0; i < peak_alloc_count; ++i) { + if (peak_ptr_array[i].ptr == (void *)old_addr) { + peak_ptr_array[i].ptr = new_ptr; + peak_ptr_array[i].size = size; + peak_ptr_array[i].file = file; + peak_ptr_array[i].line = line; + peak_ptr_array[i].func = func; + found = true; + break; + } + } + if (!found) { + if (peak_alloc_count < PEAK_MAX_ALLOCS) { + peak_ptr_array[peak_alloc_count++] = (PeakDebugMemoryInfo){ + .ptr = new_ptr, + .size = size, + .file = file, + .func = func, + .line = line + }; + } + } + } + return new_ptr; +} + +void +peak_debug_memory_report(void) +{ + printf("\n==================== MEMORY REPORT ====================\n"); + printf("Remaining unfreed allocations: %lu\n", (unsigned long)peak_alloc_count); + + for (uint64_t i = 0; i < peak_alloc_count; ++i) { + PeakDebugMemoryInfo *info = &peak_ptr_array[i]; + printf("[LEAK] %p (%zu bytes) allocated at %s:%d in %s()\n", + info->ptr, info->size, info->file, info->line, info->func); + } + printf("=======================================================\n"); +} +/* --- End of p_log.c --- */ /* --- Start of peak.c --- */ +#include <stdio.h> +#include <stdlib.h> + static uint32_t * peak_window_sync(PeakWindow *win, size_t *width, size_t *height) { @@ -1107,6 +1935,7 @@ peak_init(void) void peak_quit(void) { + peak_audio_stop(); peak_platform_quit(); } @@ -1203,6 +2032,82 @@ peak_window_run(PeakWindow *win, int (*peak_tick)(PeakWindow *win, void *userdat while (win->running && win->tick(win, win->userdata)); #endif } + +int +peak_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata) +{ + if (!fill || !channels || !rate) + return 0; + peak_audio_stop(); + return peak_platform_audio_start(channels, rate, fill, userdata); +} + +void +peak_audio_stop(void) +{ + peak_platform_audio_stop(); +} + +uint64_t +peak_get_time(void) +{ + return peak_platform_get_time(); +} + +void +peak_sleep_ns(int64_t ns) +{ + peak_platform_sleep_ns(ns); +} + +int +peak_file_exists(const char *path) +{ + FILE *f; + if (!path) return 0; + f = fopen(path, "rb"); + if (!f) return 0; + fclose(f); + return 1; +} + +void * +peak_file_alloc(const char *path, unsigned long *buf_size) +{ + FILE *f; + long n; + void *p; + if (!path) return NULL; + f = fopen(path, "rb"); + if (!f) return NULL; + if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; } + n = ftell(f); + if (n < 0) { fclose(f); return NULL; } + rewind(f); + p = malloc((size_t)n + (n == 0)); + if (!p) { fclose(f); return NULL; } + if (n && fread(p, 1, (size_t)n, f) != (size_t)n) { + free(p); + fclose(f); + return NULL; + } + fclose(f); + if (buf_size) *buf_size = (unsigned long)n; + return p; +} + +const char ** +peak_vulkan_get_extensions(uint32_t *count) +{ + return peak_platform_vulkan_get_extensions(count); +} + +int +peak_vulkan_create_surface(PeakWindow *win, void *instance, const void *allocator, void *out_surface) +{ + if (!win || !instance || !out_surface) return 0; + return peak_platform_vulkan_create_surface(&win->internal, instance, allocator, out_surface); +} /* --- End of peak.c --- */ #endif // PEAK_IMPLEMENTATION @@ -16,6 +16,7 @@ Use at your own risk. | ------ | ------------------------------------------ |
| Peak | Single-header platform library that automatically links the write system libraries. |
| Draw | Simple RGBA software raster |
+| Math | Math for games. Speed over precision.|
| Poof | Build software without going insane. |
| Rend | Modern graphics API layer. |
| Cool | Render HTML by calling functions. |
@@ -30,12 +31,3 @@ because it works. Documentation can be found on [godgun.net](https://godgun.net/docs) (results may vary).
We recommend just reading the header files. It's all there.
-
-## Archive
-
-Archived projects are incomplete with no intentions of being completed
-or just have been succeeded.
-
-| Name | Description |
-| ------ | ------------------------------------------ |
-| Podium | Platform layer & kitchen sink. Underrated. |
diff --git a/Rend/demos/build.c b/Rend/demos/build.c deleted file mode 100644 index 3d67006..0000000 --- a/Rend/demos/build.c +++ /dev/null @@ -1,85 +0,0 @@ -#define POOF_IMPLEMENTATION -#include "poof.h" - -void add_podium_config(Poof_CC *cc) { - poof_cmd_append(&cc->includes, "Podium"); - -#if defined(PODIUM_WIN32) - poof_cc_append_win32(cc, "-DPODIUM_WIN32", "-luser32", "-lgdi32"); - const char *vulkan_sdk = getenv("VULKAN_SDK"); - if (vulkan_sdk) { - char inc_buf[512], lib_buf[512]; - snprintf(inc_buf, sizeof(inc_buf), "-I%s/Include", vulkan_sdk); - snprintf(lib_buf, sizeof(lib_buf), "-L%s/Lib", vulkan_sdk); - poof_cc_append_win32(cc, strdup(inc_buf), strdup(lib_buf)); - } - poof_cc_append_win32(cc, "-lvulkan-1"); -#else - poof_cc_append_linux(cc, "-lX11", "-D_REENTRANT", "-lpulse", "-lpulse-simple", "-pthread", "-lvulkan"); -#endif -} - -int main(int argc, char **argv) { - POOF_GO_REBUILD_URSELF(argc, argv); - - Poof_Batch batch = {0}; - - { - Poof_Cmd cmd = {0}; - poof_cmd_append(&cmd, "slangc", "rend_compute.slang", "-target", "spirv", "-entry", "particleMain", "-stage", "compute", "-o", "compute/particle.spv"); - poof_batch_append_cmd(&batch, cmd); - } - - { - Poof_Cmd cmd = {0}; - poof_cmd_append(&cmd, "slangc", "rend_compute.slang", "-target", "spirv", "-entry", "sandMain", "-stage", "compute", "-o", "compute/sand.spv"); - poof_batch_append_cmd(&batch, cmd); - } - - { - Poof_Cmd cmd = {0}; - poof_cmd_append(&cmd, "slangc", "rend_compute.slang", "-target", "spirv", "-entry", "vertMain", "-stage", "vertex", "-o", "compute/vert.spv"); - poof_batch_append_cmd(&batch, cmd); - } - - { - Poof_Cmd cmd = {0}; - poof_cmd_append(&cmd, "slangc", "rend_compute.slang", "-target", "spirv", "-entry", "fragMain", "-stage", "fragment", "-o", "compute/frag.spv"); - poof_batch_append_cmd(&batch, cmd); - } - - // Debug build - { - Poof_CC cc; - poof_cc_init(&cc, POOF_CC_GCC, POOF_TARGET_HOST); - cc.debug_mode = true; - cc.optimization = POOF_O0; - cc.output = "compute/rend_compute_demo"; - poof_cmd_append(&cc.inputs, "rend_compute.c"); - poof_cmd_append(&cc.includes, "..", "."); - poof_cmd_append(&cc.defines, "REND_DEBUG"); - poof_cmd_append(&cc.libs, "m"); - poof_cmd_append(&cc.extra_flags, "-ggdb"); - add_podium_config(&cc); - - poof_batch_append_cc(&batch, &cc); - } - - // Debug build - { - Poof_CC cc; - poof_cc_init(&cc, POOF_CC_GCC, POOF_TARGET_HOST); - cc.debug_mode = true; - cc.optimization = POOF_O3 | POOF_MSSE2; - cc.output = "compute/rend_compute_fast"; - poof_cmd_append(&cc.inputs, "rend_compute.c"); - poof_cmd_append(&cc.includes, ".."); - poof_cmd_append(&cc.libs, "m"); - add_podium_config(&cc); - - poof_batch_append_cc(&batch, &cc); - } - - poof_batch_run(&batch, "Rend Demos"); - return 0; -} diff --git a/Rend/demos/compute/rend_compute_demo b/Rend/demos/compute/rend_compute_demo Binary files differdeleted file mode 100755 index 071b495..0000000 --- a/Rend/demos/compute/rend_compute_demo +++ /dev/null diff --git a/Rend/demos/compute/rend_compute_fast b/Rend/demos/compute/rend_compute_fast Binary files differdeleted file mode 100755 index b9dac67..0000000 --- a/Rend/demos/compute/rend_compute_fast +++ /dev/null diff --git a/Rend/demos/poof.h b/Rend/demos/poof.h deleted file mode 100644 index 3480557..0000000 --- a/Rend/demos/poof.h +++ /dev/null @@ -1,874 +0,0 @@ -/* =========================================================================== - * POOF - The Universal Build System - Copyright (c) 2026 Vasco Alves - * - * PREFIX: Poof_ (types) or poof_ (functions & variables) - * - * DESCRIPTION: - * Platform independent-ish build system! - * Performant, multi-threaded and mesmerizing. - * - * See LICENSE at the end of the file. - * =========================================================================== */ - -#ifndef _POOF_H_ -#define _POOF_H_ - -#define POOF_MAJOR 0 -#define POOF_MINOR 1 -#define POOF_PATCH 0 - -#include <stdint.h> -#include <stdbool.h> - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> -#include <sys/stat.h> - -#if defined(_WIN32) - #include <windows.h> - #include <direct.h> - #include <io.h> -#else - #include <unistd.h> - #include <sys/wait.h> - #include <sys/types.h> - #include <dirent.h> - #include <errno.h> -#endif - -enum PoofTargetPlatform { - POOF_TARGET_HOST = 0, - POOF_TARGET_WIN32 = 1, - POOF_TARGET_LINUX = 1 << 1, - POOF_TARGET_MACOS = 1 << 2, -}; - -enum PoofCCFlags { - POOF_CC_GCC = 1, - POOF_CC_CLANG = 1 << 1, - POOF_CC_MSVC = 1 << 2, - POOF_CC_MINGW = 1 << 3, -}; - -enum PoofOptimizationFlags { - POOF_O0 = 0, - POOF_O1 = 1, - POOF_O2 = 2, - POOF_O3 = 3, - POOF_MSSE = 1 << 3, - POOF_MSSE2 = 1 << 4, - POOF_AVX = 1 << 5, - POOF_AVX2 = 1 << 6, - POOF_AVX512F = 1 << 7, -}; - -#define POOF_IS_SET(var, flag) (((var) & (flag)) != 0) - -typedef struct Poof_Cmd { - const char **items; - size_t capacity; - size_t count; -} Poof_Cmd; - -typedef struct Poof_CC { - uint8_t compiler; // enum PoofCCFlags - uint8_t target_platform; // enum PoofTargetPlatform - uint32_t optimization; // bitwise PoofOptimizationFlags - bool debug_mode; - Poof_Cmd inputs; - const char *output; - Poof_Cmd includes; - Poof_Cmd lib_paths; - Poof_Cmd libs; - Poof_Cmd defines; - Poof_Cmd extra_flags; - - /* Platform specific flags */ - Poof_Cmd win32_flags; - Poof_Cmd linux_flags; - Poof_Cmd macos_flags; -} Poof_CC; - -typedef struct Poof_Batch { - Poof_Cmd *cmds; - size_t capacity; - size_t count; -} Poof_Batch; - -/* Command operations */ -extern void poof_cmd_append_internal(Poof_Cmd *cmd, ...); -#define poof_cmd_append(cmd, ...) poof_cmd_append_internal((cmd), __VA_ARGS__, NULL) - -#define poof_cc_append_win32(cc, ...) poof_cmd_append_internal(&((cc)->win32_flags), __VA_ARGS__, NULL) -#define poof_cc_append_linux(cc, ...) poof_cmd_append_internal(&((cc)->linux_flags), __VA_ARGS__, NULL) -#define poof_cc_append_macos(cc, ...) poof_cmd_append_internal(&((cc)->macos_flags), __VA_ARGS__, NULL) - -extern void poof_cmd_free(Poof_Cmd *cmd); -extern void poof_cmd_clear(Poof_Cmd *cmd); -extern bool poof_cmd_run(Poof_Cmd *cmd); - -/* Batch operations */ -extern void poof_batch_append_cmd(Poof_Batch *batch, Poof_Cmd cmd); -extern void poof_batch_append_cc(Poof_Batch *batch, Poof_CC *cc); -#define poof_batch_append(batch, cmd) poof_batch_append_cmd((batch), (cmd)) -extern void poof_batch_free(Poof_Batch *batch); -extern bool poof_batch_run(Poof_Batch *batch, const char *label); -extern bool poof_batch_run_parallel(Poof_Batch *batch, const char *label, size_t max_jobs); - -/* Compiler abstraction */ -extern uint8_t poof_cc_available(void); -extern void poof_cc_init(Poof_CC *cc, uint8_t compiler, uint8_t target_platform); -extern void poof_cc_free(Poof_CC *cc); -extern bool poof_cc_run(Poof_CC *cc); - -/* File and directory utilities */ -extern bool poof_mkdir(const char *path); -extern bool poof_touch(const char *path); -extern bool poof_rm(const char *path); -extern bool poof_rm_recursive(const char *path); -extern bool poof_copy_file(const char *src, const char *dst); -extern bool poof_needs_rebuild(const char *target, const char **sources, size_t source_count); - -/* Output and pretty printing */ -extern int poof_print(uint32_t col, const char *text, ...); -extern int poof_progress_bar(const char *label, float value, float min, float max, float width); - -/* Rebuild self macro */ -extern void poof_go_rebuild_urself_impl(int argc, char **argv, const char *source_file); - -#define POOF_GO_REBUILD_URSELF(argc, argv) poof_go_rebuild_urself_impl(argc, argv, __FILE__) - -#endif // _POOF_H_ - -#ifdef POOF_IMPLEMENTATION -#undef POOF_IMPLEMENTATION - -#if defined(_WIN32) -#define POOF_DEV_NULL "nul 2>&1" -#else -#define POOF_DEV_NULL "/dev/null 2>&1" -#endif - -static bool -poof__probe_cmd(const char *cmd) -{ - char buffer[256]; - snprintf(buffer, sizeof(buffer), "%s > " POOF_DEV_NULL, cmd); - return (system(buffer) == 0); -} - -extern uint8_t -poof_cc_available(void) -{ - uint8_t available = 0; - - poof_print(0x00FF88, "[POOF] Probing available compilers:\n"); - if (poof__probe_cmd("gcc --version")) { - available |= POOF_CC_GCC; - poof_print(0x00FF88, " - GCC: available\n"); - } - - if (poof__probe_cmd("clang --version")) { - available |= POOF_CC_CLANG; - poof_print(0x00FF88, " - CLANG: available\n"); - } - - if (poof__probe_cmd("x86_64-w64-mingw32-gcc --version") || poof__probe_cmd("mingw32-gcc --version")) { - available |= POOF_CC_MINGW; - poof_print(0x00FF88, " - MinGW: available\n"); - } - - if (poof__probe_cmd("cl")) { - available |= POOF_CC_MSVC; - poof_print(0x00FF88, " - MSVC: available\n"); - } - - return available; -} - -extern void -poof_cmd_append_internal(Poof_Cmd *cmd, ...) -{ - va_list args; - va_start(args, cmd); - const char *arg = NULL; - while ((arg = va_arg(args, const char *)) != NULL) { - - if (cmd->count >= cmd->capacity) { - size_t new_cap = cmd->capacity == 0 ? 16 : cmd->capacity * 2; - const char **new_items = (const char **)realloc(cmd->items, new_cap * sizeof(const char *)); - if (!new_items) return; - cmd->items = new_items; - cmd->capacity = new_cap; - } - cmd->items[cmd->count++] = arg; - } - va_end(args); -} - - -extern void -poof_cmd_clear(Poof_Cmd *cmd) -{ - /* clear count while keeping capacity */ - cmd->count = 0; -} - -extern void -poof_cmd_free(Poof_Cmd *cmd) -{ - if (cmd->items) { - free(cmd->items); - cmd->items = NULL; - } - cmd->capacity = 0; - cmd->count = 0; -} - -static char * -poof__cmd_to_string(const Poof_Cmd *cmd) -{ - if (!cmd || cmd->count == 0) return NULL; - - size_t total_len = 0; - for (size_t i = 0; i < cmd->count; ++i) { - total_len += strlen(cmd->items[i]) + 3; // quotes and space - } - - char *cmdline = (char *)malloc(total_len + 1); - if (!cmdline) return NULL; - cmdline[0] = '\0'; - - for (size_t i = 0; i < cmd->count; ++i) { - if (i > 0) strcat(cmdline, " "); - bool need_quote = (strchr(cmd->items[i], ' ') != NULL); - if (need_quote) strcat(cmdline, "\""); - strcat(cmdline, cmd->items[i]); - if (need_quote) strcat(cmdline, "\""); - } - - return cmdline; -} - -extern bool -poof_cmd_run(Poof_Cmd *cmd) -{ - char *cmdline = poof__cmd_to_string(cmd); - if (!cmdline) return false; - - printf("[POOF] Executing: "); - poof_print(0xA9B665, "%s\n", cmdline); - - int status = system(cmdline); - free(cmdline); - poof_cmd_clear(cmd); - return (status == 0); -} - -extern void -poof_cc_init(Poof_CC *cc, uint8_t compiler, uint8_t target_platform) -{ - memset(cc, 0, sizeof(Poof_CC)); - cc->compiler = compiler; - cc->target_platform = target_platform; - cc->optimization = POOF_O0; - cc->debug_mode = true; -} - -extern void -poof_cc_free(Poof_CC *cc) -{ - poof_cmd_free(&cc->inputs); - poof_cmd_free(&cc->includes); - poof_cmd_free(&cc->lib_paths); - poof_cmd_free(&cc->libs); - poof_cmd_free(&cc->defines); - poof_cmd_free(&cc->extra_flags); - poof_cmd_free(&cc->win32_flags); - poof_cmd_free(&cc->linux_flags); - poof_cmd_free(&cc->macos_flags); -} - -extern void -poof_batch_append_cmd(Poof_Batch *batch, Poof_Cmd cmd) -{ - if (batch->count >= batch->capacity) { - size_t new_cap = batch->capacity == 0 ? 16 : batch->capacity * 2; - Poof_Cmd *new_cmds = (Poof_Cmd *)realloc(batch->cmds, new_cap * sizeof(Poof_Cmd)); - if (!new_cmds) return; - batch->cmds = new_cmds; - batch->capacity = new_cap; - } - batch->cmds[batch->count++] = cmd; -} - -static Poof_Cmd -poof__cc_build_cmd(const Poof_CC *cc, uint8_t target_platform, bool multi_target) -{ - Poof_Cmd cmd = {0}; - - // Pick compiler binary - if (cc->compiler & POOF_CC_GCC) { - if (target_platform == POOF_TARGET_WIN32) { - poof_cmd_append(&cmd, "x86_64-w64-mingw32-gcc"); - } else { - poof_cmd_append(&cmd, "gcc"); - } - } else if (cc->compiler & POOF_CC_CLANG) { - poof_cmd_append(&cmd, "clang"); - if (target_platform == POOF_TARGET_WIN32) { - poof_cmd_append(&cmd, "-target"); - poof_cmd_append(&cmd, "x86_64-pc-windows-gnu"); - } else if (target_platform == POOF_TARGET_LINUX) { - poof_cmd_append(&cmd, "-target"); - poof_cmd_append(&cmd, "x86_64-unknown-linux-gnu"); - } else if (target_platform == POOF_TARGET_MACOS) { - poof_cmd_append(&cmd, "-target"); - poof_cmd_append(&cmd, "x86_64-apple-darwin"); - } - } else if (cc->compiler & POOF_CC_MINGW) { - poof_cmd_append(&cmd, "x86_64-w64-mingw32-gcc"); - } else if (cc->compiler & POOF_CC_MSVC) { - poof_cmd_append(&cmd, "cl"); - } else { - poof_cmd_append(&cmd, "gcc"); - } - - bool is_msvc = (cc->compiler & POOF_CC_MSVC) != 0; - - // Debug flags - if (cc->debug_mode) { - poof_cmd_append(&cmd, is_msvc ? "/Zi" : "-g"); - } - - // Optimization flags - uint32_t opt_level = cc->optimization & 3; - if (opt_level == POOF_O1) poof_cmd_append(&cmd, is_msvc ? "/O1" : "-O1"); - else if (opt_level == POOF_O2) poof_cmd_append(&cmd, is_msvc ? "/O2" : "-O2"); - else if (opt_level == POOF_O3) poof_cmd_append(&cmd, is_msvc ? "/O2" : "-O3"); - else poof_cmd_append(&cmd, is_msvc ? "/Od" : "-O0"); - - // SIMD / Arch extensions - if (POOF_IS_SET(cc->optimization, POOF_MSSE)) poof_cmd_append(&cmd, is_msvc ? "/arch:SSE" : "-msse"); - if (POOF_IS_SET(cc->optimization, POOF_MSSE2)) poof_cmd_append(&cmd, is_msvc ? "/arch:SSE2" : "-msse2"); - if (POOF_IS_SET(cc->optimization, POOF_AVX)) poof_cmd_append(&cmd, is_msvc ? "/arch:AVX" : "-mavx"); - if (POOF_IS_SET(cc->optimization, POOF_AVX2)) poof_cmd_append(&cmd, is_msvc ? "/arch:AVX2" : "-mavx2"); - if (POOF_IS_SET(cc->optimization, POOF_AVX512F)) poof_cmd_append(&cmd, is_msvc ? "/arch:AVX512" : "-mavx512f"); - - // Defines - for (size_t i = 0; i < cc->defines.count; ++i) { - char buf[256]; - snprintf(buf, sizeof(buf), "%s%s", is_msvc ? "/D" : "-D", cc->defines.items[i]); - poof_cmd_append(&cmd, strdup(buf)); - } - - // Include dirs - for (size_t i = 0; i < cc->includes.count; ++i) { - char buf[512]; - snprintf(buf, sizeof(buf), "%s%s", is_msvc ? "/I" : "-I", cc->includes.items[i]); - poof_cmd_append(&cmd, strdup(buf)); - } - - // Input files - for (size_t i = 0; i < cc->inputs.count; ++i) { - poof_cmd_append(&cmd, cc->inputs.items[i]); - } - - // Output target - if (cc->output) { - char out_path[512]; - if (target_platform == POOF_TARGET_WIN32) { - if (strstr(cc->output, ".exe")) { - snprintf(out_path, sizeof(out_path), "%s", cc->output); - } else { - snprintf(out_path, sizeof(out_path), multi_target ? "%s_win32.exe" : "%s.exe", cc->output); - } - } else if (target_platform == POOF_TARGET_MACOS) { - snprintf(out_path, sizeof(out_path), multi_target ? "%s_macos" : "%s", cc->output); - } else if (target_platform == POOF_TARGET_LINUX) { - snprintf(out_path, sizeof(out_path), multi_target ? "%s_linux" : "%s", cc->output); - } else { - snprintf(out_path, sizeof(out_path), "%s", cc->output); - } - - if (is_msvc) { - char buf[512]; - snprintf(buf, sizeof(buf), "/Fe:%s", out_path); - poof_cmd_append(&cmd, strdup(buf)); - } else { - poof_cmd_append(&cmd, "-o"); - poof_cmd_append(&cmd, strdup(out_path)); - } - } - - // Lib paths & libs - for (size_t i = 0; i < cc->lib_paths.count; ++i) { - char buf[512]; - snprintf(buf, sizeof(buf), "%s%s", is_msvc ? "/LIBPATH:" : "-L", cc->lib_paths.items[i]); - poof_cmd_append(&cmd, strdup(buf)); - } - - for (size_t i = 0; i < cc->libs.count; ++i) { - if (is_msvc) { - char buf[256]; - snprintf(buf, sizeof(buf), "%s.lib", cc->libs.items[i]); - poof_cmd_append(&cmd, strdup(buf)); - } else { - char buf[256]; - snprintf(buf, sizeof(buf), "-l%s", cc->libs.items[i]); - poof_cmd_append(&cmd, strdup(buf)); - } - } - - // Extra flags - for (size_t i = 0; i < cc->extra_flags.count; ++i) { - poof_cmd_append(&cmd, cc->extra_flags.items[i]); - } - - // Platform specific flags - const Poof_Cmd *plat_cmd = NULL; - if (target_platform == POOF_TARGET_WIN32) plat_cmd = &cc->win32_flags; - else if (target_platform == POOF_TARGET_LINUX) plat_cmd = &cc->linux_flags; - else if (target_platform == POOF_TARGET_MACOS) plat_cmd = &cc->macos_flags; - - if (plat_cmd) { - for (size_t i = 0; i < plat_cmd->count; ++i) { - poof_cmd_append(&cmd, plat_cmd->items[i]); - } - } - - return cmd; -} - -extern void -poof_batch_append_cc(Poof_Batch *batch, Poof_CC *cc) -{ - uint8_t mask = cc->target_platform; - if (mask == 0) { -#if defined(_WIN32) - mask = POOF_TARGET_WIN32; -#elif defined(__APPLE__) - mask = POOF_TARGET_MACOS; -#else - mask = POOF_TARGET_LINUX; -#endif - } - - uint8_t targets[3] = { POOF_TARGET_WIN32, POOF_TARGET_LINUX, POOF_TARGET_MACOS }; - int target_count = 0; - for (int i = 0; i < 3; ++i) { - if (mask & targets[i]) target_count++; - } - - bool multi_target = target_count > 1; - - for (int i = 0; i < 3; ++i) { - if (mask & targets[i]) { - Poof_Cmd cmd = poof__cc_build_cmd(cc, targets[i], multi_target); - poof_batch_append_cmd(batch, cmd); - } - } - - poof_cc_free(cc); -} - -extern void -poof_batch_free(Poof_Batch *batch) -{ - if (batch->cmds) { - for (size_t i = 0; i < batch->count; ++i) { - poof_cmd_free(&batch->cmds[i]); - } - free(batch->cmds); - batch->cmds = NULL; - } - batch->capacity = 0; - batch->count = 0; -} - -extern bool -poof_batch_run_parallel(Poof_Batch *batch, const char *label, size_t max_jobs) -{ - if (!batch || batch->count == 0) return true; - if (!label) label = "BATCH"; - -#if defined(_WIN32) - if (max_jobs == 0) { - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - max_jobs = sysinfo.dwNumberOfProcessors > 0 ? sysinfo.dwNumberOfProcessors : 4; - } - if (max_jobs > MAXIMUM_WAIT_OBJECTS) max_jobs = MAXIMUM_WAIT_OBJECTS; - - size_t total = batch->count; - size_t next_job = 0; - size_t completed = 0; - bool success = true; - - HANDLE *handles = (HANDLE *)calloc(max_jobs, sizeof(HANDLE)); - if (!handles) return false; - size_t active_jobs = 0; - - poof_progress_bar(label, 0.0f, 0.0f, (float)total, 30.0f); - - while (completed < total) { - while (active_jobs < max_jobs && next_job < total) { - char *cmdline = poof__cmd_to_string(&batch->cmds[next_job]); - if (cmdline) { - char win_cmd[1024]; - snprintf(win_cmd, sizeof(win_cmd), "cmd.exe /c \"%s\"", cmdline); - - STARTUPINFOA si = { sizeof(si) }; - PROCESS_INFORMATION pi = {0}; - - if (CreateProcessA(NULL, win_cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { - CloseHandle(pi.hThread); - handles[active_jobs++] = pi.hProcess; - } else { - success = false; - } - free(cmdline); - } - next_job++; - } - - if (active_jobs == 0) break; - - DWORD dwWait = WaitForMultipleObjects((DWORD)active_jobs, handles, FALSE, INFINITE); - if (dwWait >= WAIT_OBJECT_0 && dwWait < WAIT_OBJECT_0 + active_jobs) { - DWORD idx = dwWait - WAIT_OBJECT_0; - DWORD exit_code = 0; - GetExitCodeProcess(handles[idx], &exit_code); - if (exit_code != 0) success = false; - CloseHandle(handles[idx]); - - for (size_t i = idx; i < active_jobs - 1; ++i) { - handles[i] = handles[i + 1]; - } - active_jobs--; - completed++; - poof_progress_bar(label, (float)completed, 0.0f, (float)total, 30.0f); - } else { - break; - } - } - - free(handles); - poof_batch_free(batch); - return success; -#else - if (max_jobs == 0) { - long nprocs = sysconf(_SC_NPROCESSORS_ONLN); - max_jobs = (nprocs > 0) ? (size_t)nprocs : 4; - } - - size_t total = batch->count; - size_t next_job = 0; - size_t active_jobs = 0; - size_t completed = 0; - bool success = true; - - poof_progress_bar(label, 0.0f, 0.0f, (float)total, 30.0f); - - while (completed < total) { - while (active_jobs < max_jobs && next_job < total) { - char *cmdline = poof__cmd_to_string(&batch->cmds[next_job]); - if (cmdline) { - pid_t pid = fork(); - if (pid == 0) { - execl("/bin/sh", "sh", "-c", cmdline, (char *)NULL); - _exit(127); - } else if (pid > 0) { - active_jobs++; - } else { - success = false; - } - free(cmdline); - } - next_job++; - } - - if (active_jobs == 0) break; - - int status = 0; - pid_t done_pid = wait(&status); - if (done_pid > 0) { - active_jobs--; - completed++; - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - success = false; - } - poof_progress_bar(label, (float)completed, 0.0f, (float)total, 30.0f); - } else if (done_pid < 0 && errno == ECHILD) { - break; - } - } - - poof_batch_free(batch); - return success; -#endif -} - -extern bool -poof_batch_run(Poof_Batch *batch, const char *label) -{ - return poof_batch_run_parallel(batch, label, 0); -} - -static bool -poof__cc_run_single(const Poof_CC *cc, uint8_t target_platform, bool multi_target) -{ - Poof_Cmd cmd = poof__cc_build_cmd(cc, target_platform, multi_target); - bool result = poof_cmd_run(&cmd); - poof_cmd_free(&cmd); - return result; -} - -extern bool -poof_cc_run(Poof_CC *cc) -{ - uint8_t mask = cc->target_platform; - if (mask == 0) { -#if defined(_WIN32) - mask = POOF_TARGET_WIN32; -#elif defined(__APPLE__) - mask = POOF_TARGET_MACOS; -#else - mask = POOF_TARGET_LINUX; -#endif - } - - uint8_t targets[3] = { POOF_TARGET_WIN32, POOF_TARGET_LINUX, POOF_TARGET_MACOS }; - int target_count = 0; - for (int i = 0; i < 3; ++i) { - if (mask & targets[i]) target_count++; - } - - bool multi_target = target_count > 1; - bool success = true; - - for (int i = 0; i < 3; ++i) { - if (mask & targets[i]) { - if (!poof__cc_run_single(cc, targets[i], multi_target)) { - success = false; - } - } - } - - poof_cc_free(cc); - return success; -} - - -extern bool -poof_mkdir(const char *path) -{ -#if defined(_WIN32) - return _mkdir(path) == 0 || errno == EEXIST; -#else - return mkdir(path, 0755) == 0 || errno == EEXIST; -#endif -} - -extern bool -poof_touch(const char *path) -{ - FILE *f = fopen(path, "a"); - if (!f) return false; - fclose(f); - return true; -} - -extern bool -poof_rm(const char *path) -{ - return remove(path) == 0; -} - -extern bool -poof_rm_recursive(const char *path) -{ -#if defined(_WIN32) - char cmd[512]; - snprintf(cmd, sizeof(cmd), "rmdir /s /q \"%s\"", path); - return system(cmd) == 0; -#else - char cmd[512]; - snprintf(cmd, sizeof(cmd), "rm -rf \"%s\"", path); - return system(cmd) == 0; -#endif -} - -extern bool -poof_copy_file(const char *src, const char *dst) -{ - FILE *in = fopen(src, "rb"); - if (!in) return false; - FILE *out = fopen(dst, "wb"); - if (!out) { - fclose(in); - return false; - } - - char buffer[8192]; - size_t bytes; - while ((bytes = fread(buffer, 1, sizeof(buffer), in)) > 0) { - fwrite(buffer, 1, bytes, out); - } - - fclose(in); - fclose(out); - return true; -} - -static time_t -poof__get_mtime(const char *path) -{ - struct stat attr; - if (stat(path, &attr) != 0) return 0; - return attr.st_mtime; -} - -extern bool -poof_needs_rebuild(const char *target, const char **sources, size_t source_count) -{ - time_t target_mtime = poof__get_mtime(target); - if (target_mtime == 0) return true; // Target does not exist - - for (size_t i = 0; i < source_count; ++i) { - time_t src_mtime = poof__get_mtime(sources[i]); - if (src_mtime > target_mtime) return true; - } - return false; -} - -extern int -poof_print(uint32_t col, const char *text, ...) -{ - uint8_t r = (col >> 16) & 0xFF; - uint8_t g = (col >> 8) & 0xFF; - uint8_t b = col & 0xFF; - - printf("\033[38;2;%d;%d;%dm", r, g, b); - - va_list args; - va_start(args, text); - int res = vprintf(text, args); - va_end(args); - - printf("\033[0m"); - fflush(stdout); - return res; -} - -extern int -poof_progress_bar(const char *label, float value, float min, float max, float width) -{ - float norm = (value - min) / (max - min); - if (norm < 0.0f) norm = 0.0f; - if (norm > 1.0f) norm = 1.0f; - - int filled = (int)(norm * width); - int total = (int)width; - - char bar[256]; - int pos = 0; - for (int i = 0; i < total && pos < (int)sizeof(bar) - 1; ++i) { - if (i < filled) bar[pos++] = '|'; - else bar[pos++] = ' '; - } - bar[pos] = '\0'; - - printf("\r[%s] [", label ? label : "PROGRESS"); - poof_print(0xFE9900, "%s", bar); - printf("] %d/%d jobs", (int)value, (int)max); - if (norm >= 1.0f) printf("\n"); - fflush(stdout); - return 0; -} - -extern void -poof_go_rebuild_urself_impl(int argc, char **argv, const char *source_file) -{ - const char *binary_file = argv[0]; - const char *sources[] = { source_file, "Poof/poof.h" }; - - if (!poof_needs_rebuild(binary_file, sources, 2)) { - return; - } - - char binary_old[4096]; - snprintf(binary_old, sizeof(binary_old), "%s.old", binary_file); - - poof_print(0xFF8800, "[POOF] Source changed. Rebuilding %s...\n", binary_file); - - poof_rm(binary_old); - rename(binary_file, binary_old); - - Poof_Cmd cmd = {0}; - poof_cmd_append(&cmd, "gcc", "-o", binary_file, source_file, "-IPodium", "-IPoof"); - - if (!poof_cmd_run(&cmd)) { - poof_print(0xFF0000, "[POOF] Rebuild failed!\n"); - rename(binary_old, binary_file); - exit(1); - } - poof_cmd_free(&cmd); - - poof_print(0x00FF00, "[POOF] Rebuild successful! Re-launching binary...\n"); - - #if defined(_WIN32) - // Windows restart - int status = system(binary_file); - exit(status); - #else - execv(binary_file, argv); - perror("execv"); - exit(1); - #endif -} -#endif - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2026 Vasco Alves -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/Rend/demos/rend_teapot.c b/Rend/demos/rend_teapot.c deleted file mode 100644 index 63f96ee..0000000 --- a/Rend/demos/rend_teapot.c +++ /dev/null @@ -1,172 +0,0 @@ - -#include "teapot.h" - -#include "../rend.h" -#include "../rend.c" -#include <stddef.h> - -#define PODIUM_IMPLEMENTATION -#define P_MODULE_MATH -#define P_MODULE_VULKAN -#include "podium.h" - -int main() { - - const char *texture_path = "wal.jpg"; - if (!p_file_exists(texture_path)) { - texture_path = "demos/wal.jpg"; - } - typedef struct CoolPushConstants { - P_Mat4 MVP; - } CoolPushConstants; - - typedef struct CoolVertex { - float pos[3]; - float uv[2]; - } CoolVertex; - - CoolVertex cool_array[TEAPOT_VERTEX_COUNT]; - - for (uint32_t i = 0; i < TEAPOT_VERTEX_COUNT; ++i) { - cool_array[i].pos[0] = teapot_positions[i][0]; - cool_array[i].pos[1] = teapot_positions[i][1]; - cool_array[i].pos[2] = teapot_positions[i][2]; - - float u = teapot_positions[i][0] / teapot_positions[i][2]; - float v = teapot_positions[i][1] / teapot_positions[i][2]; - - cool_array[i].uv[0] = u; - cool_array[i].uv[1] = v; - } - - size_t vert_size = TEAPOT_VERTEX_COUNT * sizeof *cool_array; - size_t ind_size = TEAPOT_INDEX_COUNT * sizeof *teapot_indices; - - RendBuffer vert_buf = rend_buffer_create(renderer, vert_size, REND_BUFFER_VERTEX, true); - RendBuffer ind_buf = rend_buffer_create(renderer, ind_size, REND_BUFFER_INDEX, true); - rend_buffer_write(renderer, &vert_buf, cool_array, vert_size, 0); - rend_buffer_write(renderer, &ind_buf, teapot_indices, ind_size, 0); - - int tex_w, tex_h, tex_channels; - unsigned char *pixels = stbi_load(texture_path, &tex_w, &tex_h, &tex_channels, 4); - RendTexture texture = rend_texture_create(renderer, tex_w, tex_h, 1, 0, 1, REND_FORMAT_R8G8B8A8_SRGB); - if (pixels) { - rend_texture_copy_data(renderer, &texture, pixels, (size_t)tex_w * tex_h * 4); - stbi_image_free(pixels); - } - - RendPipeline basic_pipeline = rend_pipeline_create_graphics_spirv( - renderer, - basic_vert, basic_vert_bytes, - basic_frag, basic_frag_bytes, - &(RendVertexBinding){ .binding = 0, .stride = sizeof(CoolVertex), .input_rate = REND_INPUT_RATE_VERTEX }, 1, - (RendVertexAttributes[]){ - { .binding = 0, .location = 0, .format = REND_FORMAT_3_SFLOAT32, .offset = offsetof(CoolVertex, pos) }, - { .binding = 0, .location = 1, .format = REND_FORMAT_2_SFLOAT32, .offset = offsetof(CoolVertex, uv) } - }, 2, - &(RendPushConstantInfo){ .offset = 0, .size = sizeof(CoolPushConstants) }, 1, - REND_POLYGON_MODE_FILL, - REND_CULL_MODE_NONE, - REND_TOPOLOGY_TRIANGLE_STRIP, - REND_FORMAT_UNDEFINED, - true - ); - - P_Mat4 view = p_mat4_translate((Vec3f){0.0f, 0.0f, -3.0f}); - P_Mat4 proj = p_mat4_perspective(P_PI_HALF, 800.0f / 600.0f, 0.1f, 100.0f); - proj.m[1 * 4 + 1] *= -1.0f; /* Vulkan clip space Y inversion fix */ - - bool running = true; - uint64_t frame_count = 0; - float angle = 0; - - bool pointer_down = false; - uint32_t last_pointer_x = 0; - uint32_t last_pointer_y = 0; - - uint16_t frames = 3; - while (running) { - - P_Event ev; - while (p_window_poll_event(win, &ev)) { - if (ev.type == P_EVENT_WINDOW_CLOSE) { - running = false; - break; - } - if (ev.type == P_EVENT_KEY_DOWN) { - switch (ev.key.key) { - case P_KEY_W: - view = p_mat4_translate_by(view, (Vec3f){0.0f, 0.0f, 0.1f}); - break; - case P_KEY_S: - view = p_mat4_translate_by(view, (Vec3f){0.0f, 0.0f, -0.1f}); - break; - case P_KEY_A: - view = p_mat4_translate_by(view, (Vec3f){0.1f, 0.0f, 0.0f}); - break; - case P_KEY_D: - view = p_mat4_translate_by(view, (Vec3f){-0.1f, 0.0f, 0.0f}); - break; - - } - break; - } - if (ev.type == P_EVENT_POINTER) { - if (ev.pointer.state == P_POINTER_PRESSED && ev.pointer.button == 1) { - pointer_down = true; - last_pointer_x = ev.pointer.x; - last_pointer_y = ev.pointer.y; - } else if (ev.pointer.state == P_POINTER_RELEASED && ev.pointer.button == 1) { - pointer_down = false; - } else if (ev.pointer.state == P_POINTER_MOVED) { - if (pointer_down) { - float dx = (float)((int)ev.pointer.x - (int)last_pointer_x); - float dy = (float)((int)ev.pointer.y - (int)last_pointer_y); - - float sensitivity = 0.005f; - float yaw = dx * sensitivity; - float pitch = dy * sensitivity; - - view = p_mat4_rotate_y_by(view, yaw); - view = p_mat4_mul(p_mat4_rotate_x(pitch), view); - - last_pointer_x = ev.pointer.x; - last_pointer_y = ev.pointer.y; - } - } - } - } - - angle += 0.05; - - P_Mat4 model = p_mat4_identity(); - model = p_mat4_rotate_y_by(model, angle * 0.5f); - - P_Mat4 mvp = p_mat4_mul(proj, p_mat4_mul(view, model)); - - CoolPushConstants pc = { - .MVP = mvp, - }; - - if (rend_renderer_frame_begin(renderer)) { - - rend_renderer_render_pass_begin(renderer, 0.5, 0.5, 0.5, 0.0); { - rend_pipeline_bind(basic_pipeline); - rend_pipeline_push_constants(basic_pipeline, &pc, sizeof pc); - rend_pipeline_bind_texture(basic_pipeline, &texture, 1, 0); - rend_pipeline_bind_vertex_buffer(basic_pipeline, 0, vert_buf, 0); - rend_pipeline_bind_index_buffer(basic_pipeline, ind_buf, 0, REND_INDEX_UINT16); - rend_pipeline_draw_indexed(basic_pipeline, TEAPOT_INDEX_COUNT, 0, 0, 1); - } rend_renderer_render_pass_end(renderer); - - rend_renderer_frame_end(renderer, NULL); - } - - } - - rend_texture_destroy(renderer, &texture); - rend_buffer_destroy(&vert_buf); - rend_buffer_destroy(&ind_buf); - - return 0; -} diff --git a/Rend/podium.h b/Rend/podium.h deleted file mode 100644 index 517774e..0000000 --- a/Rend/podium.h +++ /dev/null @@ -1,2282 +0,0 @@ -/* ===========================================================================
- * PODIUM - The Finished Platform Layer - Copyright (c) 2026 Vasco Alves
- *
- * ---------------------------------------------------------------------------
- * To stand on the shoulders of giants...
- * Giants must stand still!"
- * - Eskil Steenberg - "You should finish your software" – BSC 2025
- * ---------------------------------------------------------------------------
- *
- * PREFIX: P_ (types) or p_ (functions & variables)
- *
- * GOAL:
- * Replace the standard library, always talk to the os directly.
- * Besides some optimization and security benefits. Obviously some standard
- * library functions are highly optimized, namely memory and math
- * into our code and debug it. Still a lot is left to be desired
- * and so this library includes it via "modules" which can be imported
- * by flags.
- *
- * DESIGN:
- * The API is finished. It's done. Newer modules may be added in the future
- * but that should not change the underlying core. New platforms may also
- * be added, but the API stays the same.
- *
- * COMPILE TIME FLAGS
- * | Name | Description |
- * |-----------------|---------------------------------------------------------|
- * | PODIUM_WIN32 | Used to explicitly compile for Windows. |
- * | PODIUM_LINUX | Used to explicitly compile for Linux and FreeBSD. |
- *
- * MODULES
- * | Name | Description |
- * |-----------------|---------------------------------------------------------|
- * | P_MODULE_VULKAN | Gives access to vulkan related utilities. |
- * | P_MODULE_STRING | Include string manipulation utilities. |
- * | P_MODULE_MATH | Trigonometry and linear algebra functions. |
- * | P_MODULE_ALLOC | Memory allocators: Arena, Stack & Pool. |
- *
- * =========================================================================== */
-
-#ifndef _PODIUM_H_
-#define _PODIUM_H_
-
-#define PODIUM_MAJOR "0" // needs more revisions and use to reach 1.0 status
-#define PODIUM_MINOR "2" // vulkan module
-#define PODIUM_PATCH "2" // gamepad
-
-#define POAPI static inline
-#define PODEF static inline
-
-/*
- * Auto-detect platforms.
- */
-
-/* Windows */
-#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__)
- #define PODIUM_WIN32
- #ifndef WIN32_LEAN_AND_MEAN
- #define WIN32_LEAN_AND_MEAN
- #endif
-/* Apple */
-#elif defined(__APPLE__) || defined(__MACH__)
- #include <TargetConditionals.h>
- #define PODIUM_APPLE
- #if TARGET_OS_IPHONE
- #define PODIUM_IOS
- #else
- #define PODIUM_MACOS
- #endif
-/* Linux / BSD */
-#elif defined(__linux__) || defined(__FreeBSD__)
- #define PODIUM_LINUX
- #ifndef _POSIX_C_SOURCE
- #define _POSIX_C_SOURCE 200112L
- #endif
-#endif
-
-#include <stdarg.h>
-#include <stdint.h>
-#include <stddef.h>
-#include <string.h> /* memcpy */
-#include <stdio.h> /* vsnprintf */
-
-/*
- * NOTE: The <REPLACE> comments are used by the build
- * script to know what includes to inline into the
- * final single header file!
- */
-
-/* --- Start of p_types.h --- */
-#ifndef _PODIUM_TYPES_
-#define _PODIUM_TYPES_
-
-#define NANOS_PER_SEC (1000*1000*1000)
-#define P_PI_HALF 1.57079632679f
-#define P_PI 3.14159265f
-#define P_PI2 6.28318531f
-#define P_PI_POW2 9.86960440f
-
-#ifdef NDEBUG
- #define assert(x) ((void)0)
-#else
- #ifndef assert
- #define assert(x) \
- do { \
- if (!(x)) { \
- __builtin_trap(); \
- } \
- } while (0)
- #endif
-#endif
-
-#ifndef BUFSIZ
- #define BUFSIZ 8192
-#endif
-
-typedef unsigned char u8;
-typedef signed char i8;
-typedef unsigned short u16;
-typedef short i16;
-typedef unsigned int u32;
-typedef int i32;
-typedef unsigned long long u64;
-typedef long long i64;
-typedef float f32;
-typedef double f64;
-
-#ifndef __cplusplus
-#define bool _Bool
-#define false 0
-#define true 1
-#endif
-
-/* C++ uses static_assert, C11 uses _Static_assert */
-#ifdef __cplusplus
- #define STATIC_ASSERT static_assert
-#else
- #define STATIC_ASSERT _Static_assert
-#endif
-
-STATIC_ASSERT(sizeof(u8) == 1, "u8 must be 1 byte");
-STATIC_ASSERT(sizeof(u16) == 2, "u16 must be 2 bytes");
-STATIC_ASSERT(sizeof(u32) == 4, "u32 must be 4 bytes");
-STATIC_ASSERT(sizeof(u64) == 8, "u64 must be 8 bytes");
-
-STATIC_ASSERT(sizeof(i8) == 1, "i8 must be 1 byte");
-STATIC_ASSERT(sizeof(i16) == 2, "i16 must be 2 bytes");
-STATIC_ASSERT(sizeof(i32) == 4, "i32 must be 4 bytes");
-STATIC_ASSERT(sizeof(i64) == 8, "i64 must be 8 bytes");
-
-STATIC_ASSERT(sizeof(f32) == 4, "f32 must be 4 bytes");
-STATIC_ASSERT(sizeof(f64) == 8, "f64 must be 8 bytes");
-#define U8_MAX 255
-#define U16_MAX 65535
-#define U32_MAX 4294967295
-
-/* Architecture specific sizing */
-#if defined(__x86_64__) || defined(__aarch64__) || defined(_M_X64)
- typedef u64 usize;
- typedef u64 uintptr;
- typedef u32 idx;
- STATIC_ASSERT(sizeof(usize) == 8, "usize must be 8 bytes on 64-bit");
-#else
- typedef u32 usize;
- typedef u32 uintptr;
- typedef u16 idx;
- STATIC_ASSERT(sizeof(usize) == 4, "usize must be 4 bytes on 32-bit");
-#endif
-
-#ifndef NULL
- #ifdef __cplusplus
- #define NULL 0
- #else
- #define NULL ((void*)0)
- #endif
-#endif
-
-#endif // _PODIUM_TYPES_
-/* --- End of p_types.h --- */
-/* --- Start of p_log.h --- */
-/*
- * Logging!
- */
-
-#include <stdlib.h>
-
-#ifdef DEBUG
-#define LOG_DEBUG(...) PRINTF(__VA_ARGS__)
-#else
-#define LOG_DEBUG(...) ((void)0)
-#endif
-
-/* fatal and error always enabled */
-#define PFATAL(message, ...) p_log_printf(P_LOG_LEVEL_FATAL, message, ##__VA_ARGS__)
-#define PERROR(message, ...) p_log_printf(P_LOG_LEVEL_ERROR, message, ##__VA_ARGS__)
-
-/* warn and info enabled by default but can be disabled */
-#define P_LOG_WARN_ENABLED 1
-#define P_LOG_INFO_ENABLED 1
-
-#if P_LOG_WARN_ENABLED == 1
-#define PWARN(message, ...) p_log_printf(P_LOG_LEVEL_WARN, message, ##__VA_ARGS__)
-#else
-#define PWARN(message, ...)
-#endif
-
-#if P_LOG_INFO_ENABLED == 1
-#define PINFO(message, ...) p_log_printf(P_LOG_LEVEL_INFO, message, ##__VA_ARGS__)
-#else
-#define PINFO(message, ...)
-#endif
-
-#if P_LOG_DEBUG_ENABLED == 1
-#define PDEBUG(message, ...) p_log_printf(P_LOG_LEVEL_DEBUG, message, ##__VA_ARGS__)
-#else
-#define PDEBUG(message, ...)
-#endif
-
-#if P_LOG_TRACE_ENABLED == 1
-#define PTRACE(message, ...) p_log_printf(P_LOG_LEVEL_TRACE, message, ##__VA_ARGS__)
-#else
-#define PTRACE(message, ...)
-#endif
-
-typedef enum P_Log_Level {
- P_LOG_LEVEL_FATAL = 0,
- P_LOG_LEVEL_ERROR,
- P_LOG_LEVEL_WARN,
- P_LOG_LEVEL_INFO,
- P_LOG_LEVEL_DEBUG,
- P_LOG_LEVEL_TRACE,
- P_COUNT_LOG_LEVEL
-} P_Log_Level;
-
-#define P_PREFIX_LEN 7
-static const char *p_prefix[P_COUNT_LOG_LEVEL] = {
- [P_LOG_LEVEL_FATAL] = "[FATAL]",
- [P_LOG_LEVEL_ERROR] = "[ERROR]",
- [P_LOG_LEVEL_WARN] = "[WARNI]",
- [P_LOG_LEVEL_INFO] = "[INFOR]",
- [P_LOG_LEVEL_DEBUG] = "[DEBUG]",
- [P_LOG_LEVEL_TRACE] = "[TRACE]",
-};
-
-#define P_MAX_ALLOCS 512
-
-typedef struct P_DebugMemoryInfo {
- void *ptr;
- size_t size;
- const char *file;
- const char *func;
- int line;
-} P_DebugMemoryInfo;
-
-static uint64_t p_alloc_count = 0;
-static P_DebugMemoryInfo p_ptr_array[P_MAX_ALLOCS];
-
-static inline void *
-p_debug_malloc_impl(size_t size, const char *file, int line, const char *func)
-{
- void *ptr = malloc(size);
- printf("[ALLOC] %p (%zu bytes) -> %s:%d %s()\n", ptr, size, file, line, func);
-
- if (ptr) {
- if (p_alloc_count < P_MAX_ALLOCS) {
- p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){
- .ptr = ptr,
- .size = size,
- .file = file,
- .func = func,
- .line = line
- };
- } else {
- fprintf(stderr, "[ERROR] Debug allocator tracking capacity (%d) exceeded!\n", P_MAX_ALLOCS);
- }
- }
- return ptr;
-}
-
-static inline void
-p_debug_free_impl(void *ptr, const char *file, int line, const char *func)
-{
- printf("[FREE] %p -> %s:%d %s()\n", ptr, file, line, func);
-
- if (!ptr) return; // Standard C allows free(NULL)
-
- bool found = false;
- uint64_t index = 0;
-
- /* Find allocation metadata */
- for (index = 0; index < p_alloc_count; ++index) {
- if (p_ptr_array[index].ptr == ptr) {
- found = true;
- break;
- }
- }
-
- if (found) {
- /* Shift array left to remove tracked struct */
- for (uint64_t j = index; j < p_alloc_count - 1; ++j) {
- p_ptr_array[j] = p_ptr_array[j + 1];
- }
- p_alloc_count--;
- } else {
- fprintf(stderr, "[WARNING] Attempted to free untracked/double-freed pointer %p at %s:%d %s()\n",
- ptr, file, line, func);
- }
-
- free(ptr);
-}
-
-static inline void *
-p_debug_realloc_impl(void *ptr, size_t size, const char *file, int line, const char *func)
-{
- if (!ptr) {
- return p_debug_malloc_impl(size, file, line, func);
- }
- if (size == 0) {
- p_debug_free_impl(ptr, file, line, func);
- return NULL;
- }
-
- void *new_ptr = realloc(ptr, size);
- printf("[REALLOC] %p -> %p (%zu bytes) -> %s:%d %s()\n", ptr, new_ptr, size, file, line, func);
-
- if (new_ptr) {
- bool found = false;
- for (uint64_t i = 0; i < p_alloc_count; ++i) {
- if (p_ptr_array[i].ptr == ptr) {
- p_ptr_array[i].ptr = new_ptr;
- p_ptr_array[i].size = size;
- p_ptr_array[i].file = file;
- p_ptr_array[i].line = line;
- p_ptr_array[i].func = func;
- found = true;
- break;
- }
- }
- if (!found) {
- if (p_alloc_count < P_MAX_ALLOCS) {
- p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){
- .ptr = new_ptr,
- .size = size,
- .file = file,
- .func = func,
- .line = line
- };
- }
- }
- }
- return new_ptr;
-}
-
-static void
-p_debug_memory_report(void)
-{
- printf("\n==================== MEMORY REPORT ====================\n");
- printf("Remaining unfreed allocations: %lu\n", (unsigned long)p_alloc_count);
-
- for (uint64_t i = 0; i < p_alloc_count; ++i) {
- P_DebugMemoryInfo *info = &p_ptr_array[i];
- printf("[LEAK] %p (%zu bytes) allocated at %s:%d in %s()\n",
- info->ptr, info->size, info->file, info->line, info->func);
- }
- printf("=======================================================\n");
-}
-/* --- End of p_log.h --- */
-/* --- Start of p_ds.h --- */
-#define DEFAULT_ALIGNMENT (2 * sizeof(void *))
-#define ARRAY_GROW_FACTOR 2
-
-typedef struct {
- u32 size;
- u32 capacity;
-} DynamicArrayHeader;
-
-#define DARRAY_INIT_CAPACITY 64
-#define p_darray_push(array, var) \
- do { \
- if (array == NULL) { \
- DynamicArrayHeader *header = \
- malloc(DARRAY_INIT_CAPACITY * sizeof *array + sizeof *header); \
- header->size = 0; \
- header->capacity = DARRAY_INIT_CAPACITY; \
- array = (void *)(header + 1); \
- } \
- DynamicArrayHeader *header = (DynamicArrayHeader *)(array) - 1; \
- if (header->size >= header->capacity - 1) { \
- unsigned long new_capacity = header->capacity * 2; \
- DynamicArrayHeader *new_header = \
- realloc(header, new_capacity * sizeof *array + sizeof *header); \
- if (!new_header) \
- break; \
- header = new_header; \
- array = (void *)(header + 1); \
- header->capacity = new_capacity; \
- } \
- (array)[header->size++] = var; \
- } while (0)
-
-#define p_darray_len(array) ((array) ? ((DynamicArrayHeader *)(array) - 1)->size : 0)
-#define p_darray_destroy(array) \
-{ \
- free((DynamicArrayHeader *)(array) - 1); \
-}
-
-#define p_darray_shrink(array) \
- do { \
- DynamicArrayHeader *header = (array) ? ((DynamicArrayHeader *)(array) - 1) : 0;\
- if (header) {\
- if (header->capacity > 4) {\
- unsigned long new_capacity = (header->capacity >> 1) + (header->capacity & 1); \
- void *new_header = realloc(header, new_capacity * sizeof *array + sizeof *header); \
- if (!new_header) break; \
- header = new_header; \
- array = (void *)(header + 1); \
- header->capacity = new_capacity; \
- }\
- }\
- } while (0)
-/* --- End of p_ds.h --- */
-
-/*
- * Main Platform Types (Keyboard, Events, Context, etc.)
- */
-
-typedef enum {
- P_KEYMOD_ALT = 0,
- P_KEYMOD_SHIFT,
- P_KEYMOD_CTRL,
- P_KEYMOD_CAPS,
-} P_KeyMod;
-
-typedef enum {
- P_KEY_UNKNOWN = 0,
- P_KEY_UP, P_KEY_DOWN, P_KEY_LEFT, P_KEY_RIGHT,
- P_KEY_SPACE, P_KEY_ESCAPE, P_KEY_ENTER,
- P_KEY_A, P_KEY_B, P_KEY_C, P_KEY_D, P_KEY_E, P_KEY_F, P_KEY_G, P_KEY_H, P_KEY_I,
- P_KEY_J, P_KEY_K, P_KEY_L, P_KEY_M, P_KEY_N, P_KEY_O, P_KEY_P, P_KEY_Q, P_KEY_R,
- P_KEY_S, P_KEY_T, P_KEY_U, P_KEY_V, P_KEY_W, P_KEY_X, P_KEY_Y, P_KEY_Z,
-} P_KeyCode;
-
-/* Platform event type */
-typedef enum {
- P_EVENT_NONE = 0,
- P_EVENT_KEY_DOWN,
- P_EVENT_KEY_UP,
- P_EVENT_WINDOW_CLOSE,
- P_EVENT_WINDOW_RESIZE,
- P_EVENT_POINTER,
- P_EVENT_CONTROLLER_CONNECTED,
- P_EVENT_CONTROLLER_DISCONNECTED,
-} P_EvenType;
-
-typedef enum {
- P_POINTER_MOVED = 0,
- P_POINTER_PRESSED,
- P_POINTER_RELEASED
-} P_PointerState;
-
-typedef struct {
- P_EvenType type;
- union {
- struct {P_KeyCode key; P_KeyMod mod; } key;
- struct { u32 width, height; } resize;
- struct { P_PointerState state; u32 button; u32 x, y; } pointer;
- };
-} P_Event;
-
-/*
- * Platform dependent functions to be implemented by each layer.
- */
-
-POAPI u64 p_get_time(void); // Get time in nanoseconds.
-POAPI void p_sleep_ns(i64 ns); // Sleep for nanoseconds.
-
-POAPI void p_stdout(void *msg, usize bytes); // Print buffer to stdout.
-PODEF void p_log_printf(P_Log_Level level, const char* src, ...); // Print buffer to stdout.
-
-
-POAPI void* p_file_alloc(const char *path, unsigned long *buf_size);
-POAPI bool p_file_exists(const char *path); // Check if file exists.
-POAPI usize p_file_size(const char *path); // Check file size.
-POAPI void p_file_load(const char *path, void *buf_ptr, unsigned long buf_size); // Load file to buffer.
-PODEF void p_file_write(const char *path, void *buf_ptr, unsigned long buf_size); // Write buffer to file.
-
-typedef struct P_Window_Impl P_Window;
-POAPI bool p_window_open(P_Window *win, int width, int height, const char *title); // Opens a window. Returns false on failure.
-POAPI void p_window_close(P_Window *win); // Close window.
-POAPI bool p_window_is_open(P_Window *win); // Check if window is open.
-POAPI void p_window_size(P_Window *win, int *window_width, int *window_height); // Get window size.
-POAPI void p_window_draw(P_Window *win, u32 *pixels, int width, int height); // Draw directly to the window's buffer. Normally much slower than using the GPU.
-POAPI bool p_window_poll_event(P_Window *win, P_Event *ev); // Returns true while there are events to poll. Write event data to a pointer.
-
-POAPI void p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc);
-POAPI void p_audio_quit();
-POAPI void p_audio_write(const i16 *samples, usize count);
-
-/*
- * The Vulkan Module is platform specific
- */
-
-#ifdef P_MODULE_VULKAN
-#include <vulkan/vulkan.h>
-POAPI char** p_vulkan_get_extensions();
-POAPI bool p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface);
-#endif
-
-/*
- * The Remaining Modules are PLATFORM INDEPENDENT!
- */
-
-#ifdef P_MODULE_STRING
-/* --- Start of p_string.h --- */
-typedef struct {
- const char *cstr;
- u32 len;
-} P_StringView;
-
-typedef struct {
- P_StringView sv;
-} P_StringBuilder;
-
-/* Classic C string functions */
-PODEF char* p_find_char(char *buf, char needle);
-PODEF bool p_strcmp(const char *a, const char *b);
-PODEF int p_strlen(const char *a);
-PODEF int p_strtoi(const char *str);
-PODEF char* p_strtok(const char *str, char c);
-
-/* String views */
-PODEF P_StringView p_strview(const char *cstr);
-PODEF void p_strview_chop_left(P_StringView *sv, u32 n);
-PODEF void p_strview_chop_right(P_StringView *sv, u32 n);
-PODEF P_StringView p_strview_chop_delim(P_StringView *sv, char delim);
-PODEF P_StringView p_strview_chop_type(P_StringView *sv, int(*istype)(int));
-PODEF void p_strview_trim_left(P_StringView *sv);
-PODEF void p_strview_trim_right(P_StringView *sv);
-PODEF void p_strview_trim(P_StringView *sv);
-
-/* Classification */
-PODEF int p_is_space(int c);
-PODEF int p_is_digit(int c);
-PODEF int p_is_alpha(int c);
-PODEF int p_is_alnum(int c);
-PODEF int p_is_upper(int c);
-PODEF int p_is_lower(int c);
-PODEF int p_is_hex(int c);
-
-/* Transformation */
-PODEF int p_to_lower(int c);
-PODEF int p_to_upper(int c);
-/* --- End of p_string.h --- */
-#endif
-
-#ifdef P_MODULE_MATH
-/* --- Start of p_math.h --- */
-typedef struct {int x, y; } Vec2i;
-typedef struct {float x, y; } Vec2f;
-typedef struct {float x, y, z; } Vec3f;
-typedef struct { float m[16];} P_Mat4; // column-major: m[col * 4 + row]
-PODEF int p_ceil(float x);
-PODEF float p_cosf(float x);
-PODEF float p_sinf(float x);
-
-PODEF Vec2f p_vec2f(float, float);
-PODEF Vec2f p_vec2f_add(Vec2f, Vec2f);
-PODEF float p_vec2f_cross(Vec2f, Vec2f);
-PODEF float p_vec2f_dot(Vec2f, Vec2f);
-PODEF Vec2f p_vec2f_mult(Vec2f a, float b);
-PODEF Vec2f p_vec2f_sub(Vec2f, Vec2f);
-PODEF Vec2i p_vec2i(float, float);
-PODEF Vec2i p_vec2i_add(Vec2i, Vec2i);
-PODEF float p_vec2i_cross(Vec2i, Vec2i);
-PODEF float p_vec2i_dot(Vec2i, Vec2i);
-PODEF Vec2i p_vec2i_mult(Vec2i a, float b);
-PODEF Vec2i p_vec2i_sub(Vec2i, Vec2i);
-
-PODEF Vec3f p_vec3f(float, float, float);
-
-PODEF P_Mat4 p_mat4_identity(void);
-PODEF P_Mat4 p_mat4_mul(P_Mat4 a, P_Mat4 b);
-PODEF P_Mat4 p_mat4_translate(Vec3f v);
-PODEF P_Mat4 p_mat4_translate_by(P_Mat4 m, Vec3f v);
-PODEF P_Mat4 p_mat4_rotate_x(float rad);
-PODEF P_Mat4 p_mat4_rotate_x_by(P_Mat4 m, float rad);
-PODEF P_Mat4 p_mat4_rotate_y(float rad);
-PODEF P_Mat4 p_mat4_rotate_y_by(P_Mat4 m, float rad);
-PODEF P_Mat4 p_mat4_rotate_z(float rad);
-PODEF P_Mat4 p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z);
-/* --- End of p_math.h --- */
-#endif
-
-#ifdef P_MODULE_ALLOC
-/* --- Start of p_allocators.h --- */
-typedef struct Arena {
- unsigned char *buf;
- usize buf_len;
- usize curr_offset;
- usize prev_offset;
-} Arena;
-
-PODEF void p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length);
-PODEF void* p_arena_alloc_align(Arena *a, usize s, usize align);
-PODEF void* p_arena_alloc(Arena *a, usize s);
-PODEF void* p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align);
-PODEF void* p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size);
-PODEF void p_arena_free_all(Arena *a);
-PODEF void p_arena_destroy(Arena* a);
-
-typedef struct { u8 padding; } Stack_Allocation_Header;
-typedef struct Stack {
- unsigned char *buf;
- usize buf_len;
- usize offset;
-} Stack;
-
-PODEF void p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length);
-PODEF usize calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size);
-PODEF void* p_stack_alloc(Stack *s, usize len, usize alignment);
-PODEF void p_stack_free(Stack *s, void *ptr);
-PODEF void p_stack_free_all(Stack* s);
-PODEF void p_stack_destroy(Stack* s);
-
-typedef struct Pool_Free_Node Pool_Free_Node;
-
-typedef struct Pool {
- unsigned char *buf;
- usize chunk_size;
- usize pool_size;
- Pool_Free_Node *head;
-} Pool;
-
-struct Pool_Free_Node { struct Pool_Free_Node *next; };
-PODEF void p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align);
-PODEF void* p_pool_alloc(Pool *p);
-PODEF void p_pool_free(Pool *p, void *ptr);
-PODEF void p_pool_free_all(Pool *p);
-PODEF void p_pool_destroy(Pool *p);
-/* --- End of p_allocators.h --- */
-#endif
-
-#endif /* _PODIUM_H_ */
-
-#ifdef PODIUM_IMPLEMENTATION
-#undef PODIUM_IMPLEMENTATION
-
-#if defined(PODIUM_SDL)
-/* #include "p_platform_sdl.c" */
-#elif defined(PODIUM_WIN32)
-/* --- Start of p_platform_win32.c --- */
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <mmsystem.h>
-#include <xinput.h> /* gamepad support */
-
-typedef struct {
- HWAVEOUT wave_out;
- WAVEHDR wave_header;
-} P_Win32Ctx;
-
-struct P_Window_Impl {
- HWND hwnd;
- HDC hdc;
- BITMAPINFO bitmap_info;
- int width;
- int height;
- bool closed;
-};
-
-P_Win32Ctx p_ctx = {0};
-
-static inline P_KeyCode
-_win32_translate_vkey(WPARAM vk)
-{
- switch (vk) {
- case VK_LEFT: return P_KEY_LEFT;
- case VK_RIGHT: return P_KEY_RIGHT;
- case VK_UP: return P_KEY_UP;
- case VK_DOWN: return P_KEY_DOWN;
- case VK_SPACE: return P_KEY_SPACE;
- case VK_ESCAPE: return P_KEY_ESCAPE;
- case VK_RETURN: return P_KEY_ENTER;
- case 'A': return P_KEY_A;
- case 'B': return P_KEY_B;
- case 'C': return P_KEY_C;
- case 'D': return P_KEY_D;
- case 'E': return P_KEY_E;
- case 'F': return P_KEY_F;
- case 'G': return P_KEY_G;
- case 'H': return P_KEY_H;
- case 'I': return P_KEY_I;
- case 'J': return P_KEY_J;
- case 'K': return P_KEY_K;
- case 'L': return P_KEY_L;
- case 'M': return P_KEY_M;
- case 'N': return P_KEY_N;
- case 'O': return P_KEY_O;
- case 'P': return P_KEY_P;
- case 'Q': return P_KEY_Q;
- case 'R': return P_KEY_R;
- case 'S': return P_KEY_S;
- case 'T': return P_KEY_T;
- case 'U': return P_KEY_U;
- case 'V': return P_KEY_V;
- case 'W': return P_KEY_W;
- case 'X': return P_KEY_X;
- case 'Y': return P_KEY_Y;
- case 'Z': return P_KEY_Z;
- default: return P_KEY_UNKNOWN;
- }
-}
-
-POAPI u64
-p_get_time(void)
-{
- LARGE_INTEGER count, freq;
- QueryPerformanceCounter(&count);
- QueryPerformanceFrequency(&freq);
- return (u64)((count.QuadPart * 1000000000ULL) / freq.QuadPart);
-}
-
-POAPI void
-p_sleep_ns(i64 ns)
-{
- /* windows sleep is millisecond-based........ */
- Sleep((DWORD)(ns / 1000000));
-}
-
-POAPI void
-p_stdout(void *msg, usize bytes)
-{
- HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
- DWORD bytes_written;
- WriteFile(stdout_handle, msg, bytes, &bytes_written, NULL);
-}
-
-POAPI bool
-p_file_exists(const char *path)
-{
- DWORD attr = GetFileAttributesA(path);
- return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY));
-}
-
-POAPI usize
-p_file_size(const char *path)
-{
- WIN32_FILE_ATTRIBUTE_DATA data;
- if (GetFileAttributesExA(path, GetFileExInfoStandard, &data)) {
- return data.nFileSizeLow;
- }
- return 0;
-}
-
-PODEF void
-p_file_load(const char *path, void *buf_ptr, unsigned long buf_size)
-{
- HANDLE file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
- if (file != INVALID_HANDLE_VALUE) {
- DWORD bytes_read;
- ReadFile(file, buf_ptr, buf_size, &bytes_read, NULL);
- CloseHandle(file);
- }
-}
-
-PODEF void
-p_file_write(const char *path, void *buf_ptr, unsigned long buf_size)
-{
- HANDLE file = CreateFileA(path, GENERIC_WRITE, 0, NULL,
- CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- if (file != INVALID_HANDLE_VALUE) {
- DWORD bytes_written;
- WriteFile(file, buf_ptr, buf_size, &bytes_written, NULL);
- CloseHandle(file);
- }
-}
-
-static LRESULT CALLBACK
-_win32_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
- P_Window *win = (P_Window*)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
- switch (msg) {
- case WM_CLOSE:
- /* Handled in poll_event; mark as closed */
- if (win) win->closed = true;
- return 0;
- }
- return DefWindowProcA(hwnd, msg, wparam, lparam);
-}
-
-POAPI bool
-p_window_open(P_Window *win, int width, int height, const char *title)
-{
- HINSTANCE instance = GetModuleHandleA(NULL);
-
- WNDCLASSA wc = {0};
- wc.lpfnWndProc = _win32_wnd_proc;
- wc.hInstance = instance;
- wc.lpszClassName = "P_Window_Class";
- wc.hCursor = LoadCursor(NULL, IDC_ARROW);
-
- RegisterClassA(&wc);
-
- win->hwnd = CreateWindowExA(0, wc.lpszClassName, title,
- WS_OVERLAPPEDWINDOW | WS_VISIBLE,
- CW_USEDEFAULT, CW_USEDEFAULT, width, height,
- NULL, NULL, instance, NULL);
-
- if (!win->hwnd) return false;
-
- SetWindowLongPtrA(win->hwnd, GWLP_USERDATA, (LONG_PTR)win);
- win->hdc = GetDC(win->hwnd);
- win->width = width;
- win->height = height;
- win->closed = false;
-
- return true;
-}
-
-POAPI void
-p_window_close(P_Window *win)
-{
- if (!win) return;
- if (win->hdc) ReleaseDC(win->hwnd, win->hdc);
- if (win->hwnd) DestroyWindow(win->hwnd);
- win->hdc = NULL;
- win->hwnd = NULL;
-}
-
-POAPI bool
-p_window_is_open(P_Window *win)
-{
- return win && win->hwnd != NULL;
-}
-
-POAPI void
-p_window_size(P_Window *win, int *window_width, int *window_height)
-{
- if (!win) return;
- RECT rect;
- GetClientRect(win->hwnd, &rect);
- *window_width = rect.right - rect.left;
- *window_height = rect.bottom - rect.top;
- win->width = *window_width;
- win->height = *window_height;
-}
-
-POAPI void
-p_window_draw(P_Window *win, u32 *pixels, int width, int height)
-{
- BITMAPINFO bmi = {0};
- bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
- bmi.bmiHeader.biWidth = width;
- bmi.bmiHeader.biHeight = -height; /* top-down */
- bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = 32;
- bmi.bmiHeader.biCompression = BI_RGB;
-
- StretchDIBits(win->hdc,
- 0, 0, win->width, win->height,
- 0, 0, width, height,
- pixels, &bmi, DIB_RGB_COLORS, SRCCOPY);
-}
-
-POAPI bool
-p_window_poll_event(P_Window *win, P_Event *ev)
-{
- if (win->closed) {
- win->closed = false;
- ev->type = P_EVENT_WINDOW_CLOSE;
- return true;
- }
-
- MSG msg;
- while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
- switch (msg.message) {
-
- case WM_QUIT:
- ev->type = P_EVENT_WINDOW_CLOSE;
- return true;
-
- /* Key press */
- case WM_KEYDOWN:
- case WM_SYSKEYDOWN:
- ev->type = P_EVENT_KEY_DOWN;
- ev->key.key = _win32_translate_vkey(msg.wParam);
- return true;
-
- /* Key release */
- case WM_KEYUP:
- case WM_SYSKEYUP:
- ev->type = P_EVENT_KEY_UP;
- ev->key.key = _win32_translate_vkey(msg.wParam);
- return true;
-
- /* Mouse movement */
- case WM_MOUSEMOVE:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_MOVED;
- ev->pointer.button = 0;
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- /* Mouse buttons */
- case WM_LBUTTONDOWN:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_PRESSED;
- ev->pointer.button = 1; /* 1 = LMB, matching X11 convention */
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- case WM_LBUTTONUP:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_RELEASED;
- ev->pointer.button = 1;
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- case WM_RBUTTONDOWN:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_PRESSED;
- ev->pointer.button = 3; /* 3 = RMB */
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- case WM_RBUTTONUP:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_RELEASED;
- ev->pointer.button = 3;
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- case WM_MBUTTONDOWN:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_PRESSED;
- ev->pointer.button = 2; /* 2 = MMB */
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- case WM_MBUTTONUP:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_RELEASED;
- ev->pointer.button = 2;
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- /* Window resize */
- case WM_SIZE:
- ev->type = P_EVENT_WINDOW_RESIZE;
- ev->resize.width = (u32)(msg.lParam & 0xFFFF);
- ev->resize.height = (u32)((msg.lParam >> 16) & 0xFFFF);
- win->width = ev->resize.width;
- win->height = ev->resize.height;
- return true;
-
- default:
- TranslateMessage(&msg);
- DispatchMessageA(&msg);
- break;
- }
- }
-
- ev->type = P_EVENT_NONE;
- return false;
-}
-
-
-POAPI void
-p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc)
-{
- (void)name; (void)desc; /* WinMM doesn't use a stream name */
-
- WAVEFORMATEX wfx = {0};
- wfx.wFormatTag = WAVE_FORMAT_PCM;
- wfx.nChannels = channels;
- wfx.nSamplesPerSec = sample_rate;
- wfx.wBitsPerSample = 16;
- wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample) / 8;
- wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
-
- if (waveOutOpen(&p_ctx.wave_out, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) {
- exit(1);
- }
-}
-
-POAPI void
-p_audio_quit()
-{
- if (p_ctx.wave_out) {
- waveOutReset(p_ctx.wave_out);
- waveOutClose(p_ctx.wave_out);
- p_ctx.wave_out = NULL;
- }
-}
-
-POAPI void
-p_audio_write(const i16 *samples, usize count)
-{
- if (!p_ctx.wave_out) return;
-
- WAVEHDR header = {0};
- header.lpData = (LPSTR)samples;
- header.dwBufferLength = (DWORD)(count * sizeof(i16));
-
- waveOutPrepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR));
- waveOutWrite(p_ctx.wave_out, &header, sizeof(WAVEHDR));
-
- /* wait for playback to finish (synchronous, mirrors pa_simple_drain) */
- while (!(header.dwFlags & WHDR_DONE)) Sleep(1);
-
- waveOutUnprepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR));
-}
-
-#ifdef P_MODULE_VULKAN
-
-#ifndef VK_USE_PLATFORM_WIN32_KHR
-#define VK_USE_PLATFORM_WIN32_KHR
-#endif
-#include <vulkan/vulkan_win32.h>
-
-POAPI char**
-p_vulkan_get_extensions()
-{
- char **darr = NULL;
- p_darray_push(darr, "VK_KHR_surface");
- p_darray_push(darr, "VK_KHR_win32_surface");
- return darr;
-}
-
-POAPI bool
-p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface)
-{
- if (!instance || !window || !window->hwnd || !out_surface) {
- return false;
- }
-
- VkWin32SurfaceCreateInfoKHR create_info = {0};
- create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
- create_info.pNext = NULL;
- create_info.flags = 0;
- create_info.hwnd = window->hwnd;
- create_info.hinstance = GetModuleHandleA(NULL);
-
- VkResult result = vkCreateWin32SurfaceKHR(instance, &create_info, allocator, out_surface);
- return (result == VK_SUCCESS);
-}
-#endif
-/* --- End of p_platform_win32.c --- */
-#elif defined(PODIUM_LINUX)
-/* --- Start of p_platform_linux.c --- */
-/* linux */
-#include <time.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <errno.h>
-/* x11 */
-#include <X11/Xlib.h>
-#include <X11/keysym.h>
-/* pulse audio */
-#include <pulse/pulseaudio.h>
-#include <pulse/simple.h>
-#include <pulse/error.h>
-/* joystick */
-#include <linux/joystick.h>
-
-typedef struct {
- pa_simple *audio_stream;
-} P_LinuxCtx;
-
-struct P_Window_Impl {
- Display *display;
- XImage *image;
- Window window;
- GC gc;
- int screen;
- int width;
- int height;
-};
-
-P_LinuxCtx p_ctx = {0};
-
-static inline P_KeyCode
-_x11_translate_keycode(XEvent xev)
-{
- static KeySym ks;
- ks = XLookupKeysym(&xev.xkey, 0);
- switch (ks) {
- /* Arrow keys */
- case XK_Left: return P_KEY_LEFT;
- case XK_Right: return P_KEY_RIGHT;
- case XK_Up: return P_KEY_UP;
- case XK_Down: return P_KEY_DOWN;
- /* Common keys */
- case XK_space: return P_KEY_SPACE;
- case XK_Escape: return P_KEY_ESCAPE;
- case XK_Return: return P_KEY_ENTER;
- case XK_A: case XK_a: return P_KEY_A;
- case XK_B: case XK_b: return P_KEY_B;
- case XK_C: case XK_c: return P_KEY_C;
- case XK_D: case XK_d: return P_KEY_D;
- case XK_E: case XK_e: return P_KEY_E;
- case XK_F: case XK_f: return P_KEY_F;
- case XK_G: case XK_g: return P_KEY_G;
- case XK_H: case XK_h: return P_KEY_H;
- case XK_I: case XK_i: return P_KEY_I;
- case XK_J: case XK_j: return P_KEY_J;
- case XK_K: case XK_k: return P_KEY_K;
- case XK_L: case XK_l: return P_KEY_L;
- case XK_M: case XK_m: return P_KEY_M;
- case XK_N: case XK_n: return P_KEY_N;
- case XK_O: case XK_o: return P_KEY_O;
- case XK_P: case XK_p: return P_KEY_P;
- case XK_Q: case XK_q: return P_KEY_Q;
- case XK_R: case XK_r: return P_KEY_R;
- case XK_S: case XK_s: return P_KEY_S;
- case XK_T: case XK_t: return P_KEY_T;
- case XK_U: case XK_u: return P_KEY_U;
- case XK_V: case XK_v: return P_KEY_V;
- case XK_W: case XK_w: return P_KEY_W;
- case XK_X: case XK_x: return P_KEY_X;
- case XK_Y: case XK_y: return P_KEY_Y;
- case XK_Z: case XK_z: return P_KEY_Z;
- default: return P_KEY_UNKNOWN;
- }
-}
-
-POAPI u64
-p_get_time(void)
-{
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return NANOS_PER_SEC * ts.tv_sec + ts.tv_nsec;
-}
-
-POAPI void
-p_sleep_ns(i64 ns)
-{
- struct timespec ts;
- ts.tv_sec = ns / 1000000000L;
- ts.tv_nsec = ns % 1000000000L;
- clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
-}
-
-POAPI void
-p_stdout(void *msg, usize bytes)
-{
- write(1, msg, bytes);
-}
-
-POAPI bool
-p_file_exists(const char *path)
-{
- return (access(path, F_OK) == 0);
-}
-
-POAPI usize
-p_file_size(const char *path)
-{
- struct stat st;
- if (stat(path, &st) == 0) {
- return st.st_size;
- }
- return 0;
-}
-
-PODEF void
-p_file_load(const char *path, void *buf_ptr, unsigned long buf_size)
-{
- int fd = open(path, O_RDONLY);
- if (fd >= 0) {
- read(fd, buf_ptr, (size_t)buf_size);
- close(fd);
- }
-}
-
-PODEF void
-p_file_write(const char *path, void *buf_ptr, unsigned long buf_size)
-{
- int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0644);
- if (fd >= 0) {
- write(fd, buf_ptr, (size_t)buf_size);
- close(fd);
- }
-}
-
-POAPI bool
-p_window_open(P_Window *win, int width, int height, const char *title)
-{
- win->width = width;
- win->height = height;
-
- XInitThreads();
-
- win->display = XOpenDisplay(NULL);
- if (win->display == NULL) {
- PERROR("Cannot open display!");
- return false;
- }
-
- int screen = DefaultScreen(win->display);
- win->window = XCreateSimpleWindow(
- win->display,
- RootWindow(win->display, screen),
- 0, 0, width, height,
- 1,
- BlackPixel(win->display, screen),
- BlackPixel(win->display, screen)
- );
-
- win->screen = screen;
-
- XStoreName(win->display, win->window, title);
- XSelectInput(win->display, win->window,
- KeyPressMask | KeyReleaseMask |
- ButtonPressMask | ButtonReleaseMask |
- PointerMotionMask | StructureNotifyMask);
- XMapWindow(win->display, win->window);
-
- win->gc = DefaultGC(win->display, screen);
-
- Atom wm_delete = XInternAtom(win->display, "WM_DELETE_WINDOW", False);
- XSetWMProtocols(win->display, win->window, &wm_delete, 1);
-
- return true;
-}
-
-POAPI void
-p_window_close(P_Window *win)
-{
- if (!win) return;
-
- if (win->image) {
- // XDestroyImage(win->image);
- win->image = NULL;
- }
-
- if (win->display) {
- Display *dpy = win->display;
- Window w = win->window;
- win->display = NULL;
- win->window = 0;
-
- if (w) {
- XDestroyWindow(dpy, w);
- }
- XSync(dpy, False);
- }
-}
-
-POAPI bool
-p_window_is_open(P_Window *win)
-{
- return win && win->display != NULL;
-}
-
-POAPI void
-p_window_size(P_Window *win, int *window_width, int *window_height)
-{
- if (!win) return;
-
- XWindowAttributes attr;
- XGetWindowAttributes(win->display, win->window, &attr);
- *window_width = attr.width;
- *window_height = attr.height;
-
- win->width = attr.width;
- win->height = attr.height;
-}
-
-POAPI void
-p_window_draw(P_Window *win, u32 *pixels, int width, int height)
-{
- int screen = win->screen;
-
- win->image = XCreateImage(
- win->display,
- DefaultVisual(win->display, screen),
- DefaultDepth(win->display, screen),
- ZPixmap,
- 0,
- (char*)pixels,
- width,
- height,
- 32,
- width * 4
- );
-
- int window_width, window_height;
- p_window_size(win, &window_width, &window_height);
-
- XPutImage(
- win->display,
- win->window,
- win->gc,
- win->image,
- 0, 0,
- 0, 0,
- win->width,
- win->height);
-
- XFlush(win->display);
-}
-
-POAPI bool
-p_window_poll_event(P_Window *win, P_Event *ev)
-{
- if (XPending(win->display)) {
- static XEvent xev;
- XNextEvent(win->display, &xev);
-
- switch (xev.type) {
-
- case KeyPress:
- ev->type = P_EVENT_KEY_DOWN;
- ev->key.key = _x11_translate_keycode(xev);
- return true;
-
- case KeyRelease:
- ev->type = P_EVENT_KEY_UP;
- ev->key.key = _x11_translate_keycode(xev);
- return true;
-
- case ConfigureNotify:
- ev->type = P_EVENT_WINDOW_RESIZE;
- ev->resize.width = xev.xconfigure.width;
- ev->resize.height = xev.xconfigure.height;
- return true;
-
- case ClientMessage:
- if ((Atom)xev.xclient.data.l[0] ==
- XInternAtom(win->display, "WM_DELETE_WINDOW", False)) {
- ev->type = P_EVENT_WINDOW_CLOSE;
- return true;
- }
- break;
-
- case MotionNotify:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_MOVED;
- ev->pointer.button = 0;
- ev->pointer.x = xev.xmotion.x;
- ev->pointer.y = xev.xmotion.y;
- return true;
-
- case ButtonPress:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_PRESSED;
- ev->pointer.button = xev.xbutton.button; /* 1=LMB, 2=MMB, 3=RMB */
- ev->pointer.x = xev.xbutton.x;
- ev->pointer.y = xev.xbutton.y;
- return true;
-
- case ButtonRelease:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_RELEASED;
- ev->pointer.button = xev.xbutton.button;
- ev->pointer.x = xev.xbutton.x;
- ev->pointer.y = xev.xbutton.y;
- return true;
- }
- }
-
- ev->type = P_EVENT_NONE;
- return false;
-}
-
-POAPI void
-p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc)
-{
- pa_sample_spec ss = {
- .format = PA_SAMPLE_S16LE,
- .rate = sample_rate,
- .channels = channels,
- };
-
- int error;
- p_ctx.audio_stream = pa_simple_new(
- NULL,
- name,
- PA_STREAM_PLAYBACK,
- NULL,
- desc,
- &ss,
- NULL,
- NULL,
- &error
- );
-
- if (!p_ctx.audio_stream) {
- PERROR("PulseAudio init failed: %s\n", pa_strerror(error));
- exit(1);
- }
-}
-
-POAPI void
-p_audio_quit()
-{
- if (p_ctx.audio_stream) {
- pa_simple_free(p_ctx.audio_stream);
- p_ctx.audio_stream = NULL;
- }
-}
-
-POAPI void
-p_audio_write(const i16 *samples, usize count)
-{
- if (!p_ctx.audio_stream) return;
-
- int error;
- if (pa_simple_write(p_ctx.audio_stream, samples, count * sizeof(i16), &error) < 0) {
- PERROR("PulseAudio write failed: %s\n", pa_strerror(error));
- }
-
- pa_simple_drain(p_ctx.audio_stream, &error);
-}
-
-#ifdef P_MODULE_VULKAN
-
-#ifndef VK_USE_PLATFORM_XLIB_KHR
-#define VK_USE_PLATFORM_XLIB_KHR
-#endif
-#include <vulkan/vulkan_xlib.h>
-
-POAPI char**
-p_vulkan_get_extensions()
-{
- char **darr = NULL;
- p_darray_push(darr, "VK_KHR_surface");
- p_darray_push(darr, "VK_KHR_xcb_surface");
- p_darray_push(darr, "VK_KHR_xlib_surface");
- return darr;
-}
-
-POAPI bool
-p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface)
-{
- if (!instance || !window || !window->display || !window->window || !out_surface) {
- return false;
- }
-
- VkXlibSurfaceCreateInfoKHR create_info = {0};
- create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
- create_info.pNext = NULL;
- create_info.flags = 0;
- create_info.dpy = window->display;
- create_info.window = window->window;
-
- VkResult result = vkCreateXlibSurfaceKHR(instance, &create_info, allocator, out_surface);
-
- return (result == VK_SUCCESS);
-}
-#endif
-/* --- End of p_platform_linux.c --- */
-#endif /* linux */
-
-
-POAPI void* p_file_alloc(const char *path, unsigned long *buf_size) {
- if (p_file_exists(path) == false) {
- return NULL;
- }
-
- usize file_size = p_file_size(path);
- *buf_size = file_size;
-
- void *ptr = malloc(file_size);
- if (ptr) {
- p_file_load(path, ptr, file_size); // Load file to buffer.
- }
- return ptr;
-}
-
-
-/*
- * Import Module Code
- */
-
-#ifdef P_MODULE_STRING
-/* --- Start of p_string.c --- */
-PODEF char*
-p_find_char(char *buf, char needle)
-{
- while (buf && *buf != needle) buf++;
- return buf;
-}
-
-PODEF bool
-p_strcmp(const char *a, const char *b)
-{
- if (p_strlen(a) != p_strlen(b))
- return false;
-
- while (a && b && *a != '\0') {
- if (*(a++) != *(b++)) return false;
- }
-
- return true;
-}
-
-PODEF int
-p_strlen(const char *a)
-{
- int i = 0;
- while (*a++ != '\0') i++;
- return i;
-}
-
-PODEF int
-p_strtoi(const char *str)
-{
- int res = 0;
-
- while (*str) {
- if (*str >= '0' && *str <= '9') {
- res = res * 10 + (*str - '0');
- } else {
- return res;
- }
- str++;
- }
-
- return res;
-}
-
-PODEF char*
-p_strtok(const char *str, char c)
-{
- while (*str != c && *str++ != '\0');
- return (*str == c) ? (char*) ++str : 0;
-}
-
-PODEF P_StringView
-p_strview(const char *cstr)
-{
- P_StringView sv = {0};
- sv.cstr = cstr;
- sv.len = p_strlen(cstr);
- return sv;
-}
-
-PODEF void
-p_strview_chop_left(P_StringView *sv, u32 n)
-{
- if (n > sv->len) n = sv->len;
- sv->cstr += n;
- sv->len -= n;
-}
-
-PODEF void
-p_strview_chop_right(P_StringView *sv, u32 n)
-{
- if (n > sv->len) n = sv->len;
- sv->len -= n;
-}
-
-PODEF P_StringView
-p_strview_chop_delim(P_StringView *sv, char delim)
-{
- u32 i = 0;
- while (i < sv->len && sv->cstr[i] != delim) {
- i++;
- }
-
- P_StringView tok;
- if (i < sv->len) {
- tok.cstr = sv->cstr;
- tok.len = i;
- p_strview_chop_left(sv, i + 1);
- return tok;
- }
-
- tok = *sv;
- p_strview_chop_left(sv, sv->len);
- return tok;
-}
-
-PODEF P_StringView
-p_strview_chop_type(P_StringView *sv, int(*istype)(int))
-{
- u32 i = 0;
- while (i < sv->len && istype(sv->cstr[i])) {
- i++;
- }
-
- P_StringView tok;
- if (i < sv->len) {
- tok.cstr = sv->cstr;
- tok.len = i;
- p_strview_chop_left(sv, i + 1);
- return tok;
- }
-
- tok = *sv;
- p_strview_chop_left(sv, sv->len);
- return tok;
-}
-
-
-PODEF void
-p_strview_trim_left(P_StringView *sv)
-{
- while (sv->len > 0 && p_is_space(sv->cstr[0])) {
- p_strview_chop_left(sv, 1);
- }
-}
-
-PODEF void
-p_strview_trim_right(P_StringView *sv)
-{
- while (sv->len > 0 && p_is_space(sv->cstr[sv->len-1])) {
- p_strview_chop_right(sv, 1);
- }
-}
-
-PODEF void
-p_strview_trim(P_StringView *sv)
-{
- p_strview_trim_left(sv);
- p_strview_trim_right(sv);
-}
-
-PODEF int
-p_is_space(int c)
-{
- /* Checks for: space, form feed (\f), line feed (\n),
- * carriage return (\r), horizontal tab (\t), vertical tab (\v) */
- return (c == ' ' || (c >= '\t' && c <= '\r'));
-}
-
-PODEF int
-p_is_digit(int c)
-{
- return (c >= '0' && c <= '9');
-}
-
-PODEF int
-p_is_alpha(int c)
-{
- return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
-}
-
-PODEF int
-p_is_alnum(int c)
-{
- return (p_is_alpha(c) || p_is_digit(c));
-}
-
-PODEF int
-p_is_hex(int c)
-{
- return (p_is_digit(c) ||
- (c >= 'a' && c <= 'f') ||
- (c >= 'A' && c <= 'F'));
-}
-
-PODEF int
-p_is_upper(int c)
-{
- return (c >= 'A' && c <= 'Z');
-}
-
-PODEF int
-p_is_lower(int c)
-{
- return (c >= 'a' && c <= 'z');
-}
-
-PODEF int
-p_to_lower(int c)
-{
- if (p_is_upper(c)) return (c + ('a' - 'A'));
- return c;
-}
-
-PODEF int
-p_to_upper(int c)
-{
- if (p_is_lower(c)) return (c - ('a' - 'A'));
- return c;
-}
-/* --- End of p_string.c --- */
-#endif
-
-#ifdef P_MODULE_MATH
-/* --- Start of p_math.c --- */
-PODEF
-int p_ceil(float x)
-{
- int base = (int) x;
- return (x > base) ? base + 1 : base;
-}
-
-PODEF float
-p_cosf(float x)
-{
- x += 1.57079632679f;
-
- while (x > P_PI) x -= P_PI2;
- while (x < -P_PI) x += P_PI2;
-
- const float B = 4.0f / P_PI;
- const float C = -4.0f / P_PI_POW2;
-
- float y = B * x + C * x * (x < 0 ? -x : x);
-
- const float P = 0.225f;
- y = P * (y * (y < 0 ? -y : y) - y) + y;
-
- return y;
-}
-
-PODEF float
-p_sinf(float x)
-{
- while (x > P_PI) x -= P_PI2;
- while (x < -P_PI) x += P_PI2;
-
- const float B = 4.0f / P_PI;
- const float C = -4.0f / (P_PI_POW2);
-
- float y = B * x + C * x * (x < 0 ? -x : x);
-
- const float P = 0.225f;
- y = P * (y * (y < 0 ? -y : y) - y) + y;
-
- return y;
-}
-
-PODEF Vec2f
-p_vec2f(float x, float y)
-{
- return (Vec2f) { .x = x, .y = y };
-}
-
-PODEF Vec2f
-p_vec2f_add(Vec2f a, Vec2f b)
-{
- return (Vec2f) {
- .x = a.x + b.x,
- .y = a.y + b.y,
- };
-}
-
-PODEF float
-p_vec2f_cross(Vec2f a, Vec2f b)
-{
- // axby − aybx
- return a.x * b.y - a.y * b.x;
-}
-
-PODEF float
-p_vec2f_dot(Vec2f a, Vec2f b)
-{
- // a · b = ax × bx + ay × by
- return a.x * b.x + a.y * b.y;
-}
-
-PODEF Vec2f
-p_vec2f_mult(Vec2f a, float b)
-{
- return (Vec2f) {
- .x = a.x * b,
- .y = a.y * b,
- };
-}
-
-PODEF Vec2f
-p_vec2f_sub(Vec2f a, Vec2f b)
-{
- return (Vec2f) {
- .x = a.x - b.x,
- .y = a.y - b.y,
- };
-}
-
-PODEF Vec2i
-p_vec2i(float x, float y)
-{
- return (Vec2i) { .x = x, .y = y };
-}
-
-PODEF Vec2i
-p_vec2i_add(Vec2i a, Vec2i b)
-{
- return (Vec2i) {
- .x = a.x + b.x,
- .y = a.y + b.y,
- };
-}
-
-PODEF float
-p_vec2i_cross(Vec2i a, Vec2i b)
-{
- // axby − aybx
- return a.x * b.y - a.y * b.x;
-}
-
-PODEF float
-p_vec2i_dot(Vec2i a, Vec2i b)
-{
- // a · b = ax × bx + ay × by
- return a.x * b.x + a.y * b.y;
-}
-
-PODEF Vec2i
-p_vec2i_mult(Vec2i a, float b)
-{
- return (Vec2i) {
- .x = a.x * b,
- .y = a.y * b,
- };
-}
-
-PODEF Vec2i
-p_vec2i_sub(Vec2i a, Vec2i b)
-{
- return (Vec2i) {
- .x = a.x - b.x,
- .y = a.y - b.y,
- };
-}
-
-PODEF Vec3f
-p_vec3f(float x, float y, float z)
-{
- return (Vec3f) { .x = x, .y = y, .z = z };
-}
-
-PODEF P_Mat4
-p_mat4_identity(void)
-{
- P_Mat4 res = {0};
- res.m[0] = 1.0f;
- res.m[5] = 1.0f;
- res.m[10] = 1.0f;
- res.m[15] = 1.0f;
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_mul(P_Mat4 a, P_Mat4 b)
-{
- P_Mat4 res = {0};
- for (int col = 0; col < 4; ++col) {
- for (int row = 0; row < 4; ++row) {
- float sum = 0.0f;
- for (int k = 0; k < 4; ++k) {
- sum += a.m[k * 4 + row] * b.m[col * 4 + k];
- }
- res.m[col * 4 + row] = sum;
- }
- }
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_translate(Vec3f v)
-{
- P_Mat4 res = p_mat4_identity();
- res.m[12] = v.x;
- res.m[13] = v.y;
- res.m[14] = v.z;
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_translate_by(P_Mat4 m, Vec3f v)
-{
- P_Mat4 res = m;
- res.m[12] = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12];
- res.m[13] = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13];
- res.m[14] = m.m[2] * v.x + m.m[6] * v.y + m.m[10] * v.z + m.m[14];
- res.m[15] = m.m[3] * v.x + m.m[7] * v.y + m.m[11] * v.z + m.m[15];
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_rotate_x(float rad)
-{
- P_Mat4 res = p_mat4_identity();
- float c = p_cosf(rad);
- float s = p_sinf(rad);
- res.m[5] = c;
- res.m[6] = s;
- res.m[9] = -s;
- res.m[10] = c;
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_rotate_x_by(P_Mat4 m, float rad)
-{
- return p_mat4_mul(m, p_mat4_rotate_x(rad));
-}
-
-PODEF P_Mat4
-p_mat4_rotate_y(float rad)
-{
- P_Mat4 res = p_mat4_identity();
- float c = p_cosf(rad);
- float s = p_sinf(rad);
- res.m[0] = c;
- res.m[2] = -s;
- res.m[8] = s;
- res.m[10] = c;
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_rotate_y_by(P_Mat4 m, float rad)
-{
- return p_mat4_mul(m, p_mat4_rotate_y(rad));
-}
-
-PODEF P_Mat4
-p_mat4_rotate_z(float rad)
-{
- P_Mat4 res = p_mat4_identity();
- float c = p_cosf(rad);
- float s = p_sinf(rad);
- res.m[0] = c;
- res.m[1] = s;
- res.m[4] = -s;
- res.m[5] = c;
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z)
-{
- P_Mat4 res = {0};
- float tan_half_fov = p_sinf(fov_rad * 0.5f) / p_cosf(fov_rad * 0.5f);
-
- res.m[0] = 1.0f / (aspect * tan_half_fov);
- res.m[5] = 1.0f / tan_half_fov;
- res.m[10] = far_z / (near_z - far_z);
- res.m[11] = -1.0f;
- res.m[14] = (near_z * far_z) / (near_z - far_z);
- return res;
-}
-/* --- End of p_math.c --- */
-#endif
-
-#ifdef P_MODULE_ALLOC
-/* --- Start of p_allocators.c --- */
-PODEF bool
-is_power_of_two(uintptr x) {
- return (x & (x-1)) == 0;
-}
-
-PODEF uintptr
-align_forward(uintptr ptr, usize align)
-{
- uintptr p, a, remainder;
- assert(is_power_of_two(align));
-
- p = ptr;
- a = (uintptr)align;
- remainder = p & (a-1);
-
- return (remainder != 0) ? p + a - remainder : p;
-}
-
-PODEF void
-p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length)
-{
- a->buf = (unsigned char *)backing_buffer;
- a->buf_len = backing_buffer_length;
- a->curr_offset = 0;
- a->prev_offset = 0;
-}
-
-PODEF void*
-p_arena_alloc_align(Arena *a, usize s, usize align)
-{
-
- // Align 'curr_offset' forward to the specified alignment
- uintptr curr_ptr = (uintptr)a->buf + (uintptr)a->curr_offset;
- uintptr offset = align_forward(curr_ptr, align);
- offset -= (uintptr)a->buf; // Change to relative offset
-
- // Check to see if the backing memory has space left
- if (offset+s <= a->buf_len) {
- void *ptr = &a->buf[offset];
- a->prev_offset = offset;
- a->curr_offset = offset+s;
-
- memset(ptr, 0, s);
- return ptr;
- }
-
- return NULL;
-}
-
-PODEF void*
-p_arena_alloc(Arena *a, usize s) {
- return p_arena_alloc_align(a, s, DEFAULT_ALIGNMENT);
-}
-
-PODEF void*
-p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align)
-{
- assert(is_power_of_two(align));
- unsigned char *old_mem = (unsigned char *)old_memory;
-
- if (old_mem == NULL || old_size == 0) {
- return p_arena_alloc_align(a, new_size, align);
- } else if (a->buf <= old_mem && old_mem < a->buf+a->buf_len) {
- if (a->buf+a->prev_offset == old_mem) {
- a->curr_offset = a->prev_offset + new_size;
- if (new_size > old_size) {
- // Zero the new memory by default
- memset(&a->buf[a->curr_offset], 0, new_size-old_size);
- }
- return old_memory;
- } else {
- void *new_memory = p_arena_alloc_align(a, new_size, align);
- size_t copy_size = old_size < new_size ? old_size : new_size;
- memmove(new_memory, old_memory, copy_size);
- return new_memory;
- }
-
- } else {
- assert(0 && "Memory is out of bounds of the buffer in this arena");
- return NULL;
- }
-}
-
-PODEF void*
-p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size)
-{
- return p_arena_resize_align(a, old_memory, old_size, new_size, DEFAULT_ALIGNMENT);
-}
-
-PODEF void
-p_arena_free_all(Arena *a)
-{
- a->curr_offset = 0;
- a->prev_offset = 0;
-}
-
-PODEF void
-p_arena_destroy(Arena* a)
-{
- a->buf_len = 0;
- a->curr_offset = 0;
- a->prev_offset = 0;
-}
-
-PODEF void
-p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length)
-{
- s->buf = (unsigned char*) backing_buffer;
- s->buf_len = backing_buffer_length;
- s->offset = 0;
-}
-
-PODEF usize
-calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size)
-{
- uintptr p, a, modulo, padding, needed_space;
-
- assert(is_power_of_two(alignment));
-
- p = ptr;
- a = alignment;
- modulo = p & (a-1); // (p % a) as it assumes alignment is a power of two
-
- padding = 0;
- needed_space = 0;
-
- if (modulo != 0) { // Same logic as 'align_forward'
- padding = a - modulo;
- }
-
- needed_space = (uintptr)header_size;
-
- if (padding < needed_space) {
- needed_space -= padding;
-
- if ((needed_space & (a-1)) != 0) {
- padding += a * (1+(needed_space/a));
- } else {
- padding += a * (needed_space/a);
- }
- }
-
- return (usize)padding;
-}
-
-PODEF void*
-p_stack_alloc(Stack *s, usize len, usize alignment)
-{
- uintptr curr_addr, next_addr;
- usize padding;
- Stack_Allocation_Header *header;
-
- assert(is_power_of_two(alignment));
-
- if (alignment > 128) {
- // As the padding is 8 bits (1 byte), the largest alignment that can
- // be used is 128 bytes
- alignment = 128;
- }
-
- curr_addr = (uintptr)s->buf + (uintptr)s->offset;
- padding = calc_padding_with_header(curr_addr, (uintptr)alignment, sizeof(Stack_Allocation_Header));
- if (s->offset + padding + len > s->buf_len) {
- // Stack allocator is out of memory
- return NULL;
- }
- s->offset += padding;
-
- next_addr = curr_addr + (uintptr)padding;
- header = (Stack_Allocation_Header *)(next_addr - sizeof(Stack_Allocation_Header));
- header->padding = (u8)padding;
-
- s->offset += len;
-
- return memset((void *)next_addr, 0, len);
-}
-
-PODEF void
-p_stack_free(Stack *s, void *ptr)
-{
- if (ptr != NULL) {
- uintptr start, end, curr_addr;
- Stack_Allocation_Header *header;
- usize prev_offset;
-
- start = (uintptr)s->buf;
- end = start + (uintptr)s->buf_len;
- curr_addr = (uintptr)ptr;
-
- if (!(start <= curr_addr && curr_addr < end)) {
- assert(0 && "Out of bounds memory address passed to stack allocator (free)");
- return;
- }
-
- if (curr_addr >= start+(uintptr)s->offset) {
- // Allow double frees
- return;
- }
-
- header = (Stack_Allocation_Header *)(curr_addr - sizeof(Stack_Allocation_Header));
- prev_offset = (usize)(curr_addr - (uintptr)header->padding - start);
-
- s->offset = prev_offset;
- }
-}
-
-PODEF void
-p_stack_free_all(Stack* s)
-{
- s->offset = 0;
-}
-
-PODEF void
-p_stack_destroy(Stack* s)
-{
- s->buf = 0;
- s->buf_len = 0;
- s->offset = 0;
-}
-
-PODEF void
-p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align)
-{
- /* align start */
- uintptr start = (uintptr) buf;
- uintptr aligned = align_forward(start, chunk_align);
- buf_len -= (aligned - start);
-
- /* align chunk usize */
- chunk_size = (usize) align_forward(chunk_size, chunk_align);
-
- assert(chunk_size >= sizeof(Pool_Free_Node) && "Chunk usize is too small");
- assert(buf_len >= chunk_size && "Backing buffer length is smaller than the chunk size");
-
- p->buf = (unsigned char *) buf;
- p->pool_size = buf_len;
- p->chunk_size = chunk_size;
- p->head = NULL;
-
- p_pool_free_all(p);
-}
-
-PODEF void*
-p_pool_alloc(Pool *p)
-{
- Pool_Free_Node *node = p->head;
-
- assert(node != NULL && "Pool allocator has no free memory");
-
- p->head = p->head->next;
-
- return memset(node, 0 , p->chunk_size);
-}
-
-PODEF void
-p_pool_free(Pool *p, void *ptr)
-{
- Pool_Free_Node *node;
-
- void *start = p->buf;
- void *end = &p->buf[p->pool_size];
-
- if (ptr == NULL) {
- return;
- }
-
- if (!(start <= ptr && ptr < end)) {
- assert(0 && "Memory is out of bounds of the buffer in this pool");
- return;
- }
-
- node = (Pool_Free_Node *)ptr;
- node->next = p->head;
- p->head = node;
-}
-
-PODEF void
-p_pool_free_all(Pool *p)
-{
- usize chunk_count = p->pool_size / p->chunk_size;
- usize i;
-
- for (i = 0; i < chunk_count; i++) {
- void *ptr = &p->buf[i * p->chunk_size];
- Pool_Free_Node *node = (Pool_Free_Node *)ptr;
-
- // Push free node onto thte free list
- node->next = p->head;
- p->head = node;
- }
-}
-
-PODEF void
-p_pool_destroy(Pool *p)
-{
- p->buf = NULL;
- p->pool_size = 0;
- p->chunk_size = 0;
- p->head = NULL;
-}
-
-/* --- End of p_allocators.c --- */
-#endif
-
-#define P_MAX_PRINTF_SIZE 1024
-PODEF void
-p_log_printf(P_Log_Level level, const char* src, ...)
-{
- static char out_message[P_MAX_PRINTF_SIZE];
- static u32 offset = P_PREFIX_LEN + 1;
-
- memcpy(out_message, p_prefix[level], P_PREFIX_LEN); /* prefixes have a fixed len */
- out_message[P_PREFIX_LEN] = ' '; /* write space */
-
- __builtin_va_list arg_ptr;
- va_start(arg_ptr, src);
- int len = vsnprintf(out_message+offset, P_MAX_PRINTF_SIZE-offset, src, arg_ptr); // append message
- va_end(arg_ptr);
-
- if (len < 0) len = 0;
- usize total_len = offset + len;
- if (total_len > P_MAX_PRINTF_SIZE) {
- total_len = P_MAX_PRINTF_SIZE;
- }
-
- p_stdout(out_message, total_len);
- p_stdout("\n", 1);
-}
-
-#endif // PODIUM_IMPLEMENTATION
diff --git a/Rend/rend.c b/Rend/rend.c index 9d31949..4d85745 100644 --- a/Rend/rend.c +++ b/Rend/rend.c @@ -75,7 +75,7 @@ rend_quit() } extern RendRenderer -rend_renderer_create(P_Window *target, RendBackendType backend, void* device, bool vsync, RendBindingInfo *bind_info) +rend_renderer_create(PeakWindow *target, RendBackendType backend, void* device, bool vsync, RendBindingInfo *bind_info) { RendRenderer rend = rmalloc(sizeof *rend); @@ -93,7 +93,7 @@ rend_renderer_create(P_Window *target, RendBackendType backend, void* device, bo rend->window = target; rend->pipeline_head = 0; - rend->context = rend_vtables[rend->backend].renderer_create(); + rend->context = rend_vtables[rend->backend].renderer_create(target, bind_info); if (rend->context) { return rend; } @@ -124,13 +124,16 @@ rend_renderer_destroy(RendRenderer renderer) extern bool rend_renderer_frame_begin(RendRenderer renderer) { + bool ok; + RASSERT(renderer && (uintptr_t)renderer != 0xffffffff00000000 && "Invalid renderer. (Possible stack corruption.)"); RASSERT(renderer->context, "Uninitialized renderer."); RASSERT(!renderer->in_frame, "Must not be called inside a frame."); RASSERT(!renderer->in_pass, "Must not be called inside a render pass."); - renderer->in_frame = 1; - return rend_vtables[renderer->backend].renderer_frame_begin(renderer->context); + ok = rend_vtables[renderer->backend].renderer_frame_begin(renderer->context); + renderer->in_frame = ok ? 1 : 0; + return ok; } extern void diff --git a/Rend/rend.h b/Rend/rend.h index a9a4a81..c4ba1d4 100644 --- a/Rend/rend.h +++ b/Rend/rend.h @@ -6,7 +6,7 @@ * - Pushes data to the GPU in a highly configurable and STABLE fashion. * - Is not responsible for initializing windows. * - Is not responsible for compiling shaders. - * - Depends on PODIUM to be cross-platform. + * - Depends on Peak to be cross-platform. * * =========================================================================== */ @@ -15,11 +15,15 @@ #define REND_MAJOR 1 // breaking API changes #define REND_MINOR 0 // non-breaking features -#define REND_PATCH 1 // non-breaking patches and bug fixes +#define REND_PATCH 3 // non-breaking patches and bug fixes -#define P_MODULE_VULKAN -#define P_MODULE_MATH -#include "podium.h" +#ifndef PEAK_VULKAN +#define PEAK_VULKAN +#endif +#if defined(REND_DEBUG) && !defined(P_LOG_DEBUG_ENABLED) +#define P_LOG_DEBUG_ENABLED 1 +#endif +#include "peak.h" typedef struct rend_renderer_t* RendRenderer; // renderer target handle typedef struct rend_pipeline_t* RendPipeline; // represents a baked shader + gpu pipeline state (blend mode, depth, vertex format) @@ -79,7 +83,7 @@ typedef struct { extern void rend_quit(void); // Will free ALL resources created by the library such as RendRenderer, RendPipeline, RendBuffer and RendTexture. /* Renderer */ -extern RendRenderer rend_renderer_create(P_Window*, RendBackendType backend, void* device, bool vsync, RendBindingInfo *bind_info); // Create Renderer that renders to a target window with +extern RendRenderer rend_renderer_create(PeakWindow*, RendBackendType backend, void* device, bool vsync, RendBindingInfo *bind_info); // Create Renderer that renders to a target window with extern void rend_renderer_destroy(RendRenderer renderer); // Destroy renderer. Unless you need to freely create and destroy renderers, you can rely on rend_quit to clean up. extern bool rend_renderer_frame_begin(RendRenderer renderer); // May fail. Acquires backbuffer and starts recording commands! extern void rend_renderer_frame_end(RendRenderer renderer, float *delta); // Stops recording commands and presents the contents to the screen. @@ -278,12 +282,14 @@ enum RendBufferType_t { * 0.11.2 - @vasco - cool beans * 1.0.0 - @vasco - finished API release * 1.0.1 - @vasco - render pass that targets textures + * 1.0.2 - @vasco - Peak instead of Podium + * 1.0.3 - @vasco - frame_begin no longer sticks in_frame or burns timeline on OUT_OF_DATE * * 1.0.0 finished API release * * ------------------------------------------- * - * 1.1.0 shader hot realoading plugin (need to add settings managament and dll loading to Podium) + * 1.1.0 shader hot reloading plugin (need settings management and dll loading in Peak) */ /* diff --git a/Rend/rend_internal.h b/Rend/rend_internal.h index 03e6634..4993d92 100644 --- a/Rend/rend_internal.h +++ b/Rend/rend_internal.h @@ -5,16 +5,21 @@ #if defined(REND_DEBUG) +#ifndef P_LOG_DEBUG_ENABLED #define P_LOG_DEBUG_ENABLED 1 -#define RASSERT(a, s) assert((a) && s) +#endif +#define RASSERT_N(_1, _2, N, ...) N +#define RASSERT(...) RASSERT_N(__VA_ARGS__, RASSERT2, RASSERT1)(__VA_ARGS__) +#define RASSERT1(a) assert(a) +#define RASSERT2(a, s) assert((a) && (s)) #else -#define RASSERT(a,s) ((void*)0) +#define RASSERT(...) ((void)0) #endif #if defined(REND_DEBUG_MEMORY) -#define rmalloc(size) p_debug_malloc_impl((size), __FILE__, __LINE__, __func__) -#define rrealloc(ptr, size) p_debug_realloc_impl((ptr), (size), __FILE__, __LINE__, __func__) -#define rfree(ptr) p_debug_free_impl((ptr), __FILE__, __LINE__, __func__) +#define rmalloc(size) peak_debug_malloc_impl((size), __FILE__, __LINE__, __func__) +#define rrealloc(ptr, size) peak_debug_realloc_impl((ptr), (size), __FILE__, __LINE__, __func__) +#define rfree(ptr) peak_debug_free_impl((ptr), __FILE__, __LINE__, __func__) #else #define rmalloc malloc #define rrealloc(ptr, size) realloc((ptr), (size)) @@ -33,7 +38,6 @@ #define REND__WARN(...) PWARN("[REND] "__VA_ARGS__); -#include "podium.h" #include "rend.h" #include <stdint.h> @@ -67,7 +71,7 @@ typedef struct rend_pipeline_config_t { } Rend__PipelineConfig; typedef struct { - RendContextHandle (*renderer_create)(); + RendContextHandle (*renderer_create)(PeakWindow *window, RendBindingInfo *bind_info); void (*renderer_destroy)(RendContextHandle); @@ -121,7 +125,7 @@ struct rend_renderer_t { struct rend_pipeline_t *pipeline_head; RendContextHandle context; // backend specific internal data - P_Window *window; + PeakWindow *window; uint64_t frame_count; RendBindingInfo bind_info; diff --git a/Rend/rend_vk.c b/Rend/rend_vk.c index ad1cc34..8fac97b 100644 --- a/Rend/rend_vk.c +++ b/Rend/rend_vk.c @@ -59,7 +59,7 @@ typedef struct RendVk14Context { RendVkDevice device; // SPEC: All objects created or allocated from a VkDevice are private to that device, and must not be used on other devices. - P_Window *window; + PeakWindow *window; VkSurfaceKHR surface; RendVkPipeline pipelines[REND_VK_MAX_PIPELINES]; RendVkFrameResources frame_resources[REND_MAX_FRAMES_IN_FLIGHT]; @@ -97,12 +97,12 @@ typedef struct RendVk14Context { extern bool rend_vk_init(); extern void rend_vk_quit(); -extern RendContextHandle rend_vk_renderer_create(P_Window *window); +extern RendContextHandle rend_vk_renderer_create(PeakWindow *window, RendBindingInfo *bind_info); extern void rend_vk_renderer_destroy(RendContextHandle handle); extern bool rend_vk_renderer_frame_begin(RendContextHandle handle); extern void rend_vk_renderer_frame_end(RendContextHandle handle, float *delta); -static inline void rend_vk__renderer_render_pass_begin_internal(RendContextHandle handle, float r, float g, float b, float a, uint64_t view_handle, uint64_t depth_attachment_view_handle, uint32_t offset_x, uint32_t offset_y, uint32_t width, uint32_t height); +static inline void rend_vk__renderer_render_pass_begin_internal(RendRenderer renderer, float r, float g, float b, float a, uint64_t view_handle, uint64_t depth_attachment_view_handle, uint32_t offset_x, uint32_t offset_y, uint32_t width, uint32_t height); extern void rend_vk_renderer_render_pass_begin(RendRenderer renderer, float r, float g, float b, float a); extern void rend_vk_renderer_render_pass_begin_texture(RendRenderer renderer, RendTexture *texture); @@ -167,25 +167,29 @@ rend_vk_init() vk_app_info.pEngineName = "REND Renderer"; vk_app_info.engineVersion = VK_MAKE_VERSION(REND_MAJOR, REND_MINOR, REND_PATCH); - const char **extensions_darray = (const char **)p_vulkan_get_extensions(); - p_darray_push(extensions_darray, VK_KHR_SURFACE_EXTENSION_NAME); + uint32_t peak_ext_count = 0; + const char **peak_exts = peak_vulkan_get_extensions(&peak_ext_count); + const char *extensions[8]; + uint32_t ext_count = 0; + uint32_t ei; + for (ei = 0; ei < peak_ext_count && ext_count < 8; ei++) + extensions[ext_count++] = peak_exts[ei]; #ifdef REND_DEBUG - p_darray_push(extensions_darray, VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - // p_darray_push(extensions_darray, VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME); + if (ext_count < 8) + extensions[ext_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; PDEBUG("Vulkan Extensions: "); - for (int i = 0; i < p_darray_len(extensions_darray); i++) - PDEBUG("%s", extensions_darray[i]); + for (ei = 0; ei < ext_count; ei++) + PDEBUG("%s", extensions[ei]); #endif - const char **required_validation_layers = NULL; - #ifdef REND_DEBUG - p_darray_push(required_validation_layers, "VK_LAYER_KHRONOS_validation"); + const char *required_validation_layers[] = { "VK_LAYER_KHRONOS_validation" }; + uint32_t layer_count = 1; PDEBUG("Required Validation Layers: "); - for (uint32_t i = 0; i < p_darray_len(required_validation_layers); i++) { - PDEBUG(" - %s", required_validation_layers[i]); + for (ei = 0; ei < layer_count; ei++) { + PDEBUG(" - %s", required_validation_layers[ei]); } VkValidationFeatureEnableEXT enabled_validation_features[] = { @@ -203,11 +207,11 @@ rend_vk_init() VkInstanceCreateInfo vk_create_info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO}; vk_create_info.pApplicationInfo = &vk_app_info; - vk_create_info.ppEnabledExtensionNames = extensions_darray; - vk_create_info.enabledExtensionCount = p_darray_len(extensions_darray); + vk_create_info.ppEnabledExtensionNames = extensions; + vk_create_info.enabledExtensionCount = ext_count; vk_create_info.pApplicationInfo = &vk_app_info; #ifdef REND_DEBUG - vk_create_info.enabledLayerCount = p_darray_len(required_validation_layers); + vk_create_info.enabledLayerCount = layer_count; vk_create_info.ppEnabledLayerNames = required_validation_layers; vk_create_info.pNext = &validation_features; #else @@ -219,10 +223,6 @@ rend_vk_init() CHECK_VK_RESULT(res); PDEBUG("Vulkan instance created!"); - p_darray_destroy(extensions_darray); -#ifdef REND_DEBUG - p_darray_destroy(required_validation_layers); -#endif #ifdef REND_DEBUG uint32_t log_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; @@ -265,7 +265,7 @@ rend_vk_quit() } extern RendContextHandle -rend_vk_renderer_create(P_Window *window) +rend_vk_renderer_create(PeakWindow *window, RendBindingInfo *bind_info) { RendVk14Context *ctx = rmalloc(sizeof(*ctx)); if (!ctx) { @@ -274,9 +274,10 @@ rend_vk_renderer_create(P_Window *window) } memset(ctx, 0, sizeof *ctx); + ctx->window = window; /* get surface from window */ - if (!p_vulkan_create_surface(window, vk_instance, vk_allocator, &ctx->surface)) { + if (!peak_vulkan_create_surface(window, vk_instance, vk_allocator, &ctx->surface)) { PERROR("Failed to create vulkan surface!"); rfree(ctx); return NULL; @@ -395,7 +396,9 @@ rend_vk_renderer_create(P_Window *window) * Descriptor Pool */ { - RendBindingInfo bind_info = renderer->bind_info; + RendBindingInfo bind_info_local = {0}; + if (bind_info) bind_info_local = *bind_info; + RendBindingInfo bind_info = bind_info_local; uint32_t total_ubos = 0; for (uint32_t i = 0; i < bind_info.ubo_binding_count; ++i) { @@ -575,61 +578,76 @@ rend_vk_renderer_destroy(RendContextHandle handle) extern bool rend_vk_renderer_frame_begin(RendContextHandle handle) { + RendVk14Context *ctx; + VkDevice dev; + uint32_t retries; + RASSERT(handle, "Invalid handle."); - RendVk14Context *ctx = (RendVk14Context *) handle; + ctx = (RendVk14Context *) handle; + dev = vk_device.logical_device; - /* check if swapchain needs recreation */ - if (ctx->require_swapchain_recreation) { - PDEBUG("[REND] Awaiting device..."); - vkDeviceWaitIdle(vk_device.logical_device); - PDEBUG("[REND] Recreating swapchain..."); - rend_vk_swapchain_destroy(ctx, &ctx->swapchain); - rend_vk_swapchain_create(ctx, &ctx->swapchain); - ctx->require_swapchain_recreation = false; - } + for (retries = 0; retries < 4; retries++) { + uint64_t frame_res_index; + uint64_t wait_value; + VkResult acquire_image; + RendVkFrameResources frame_resource; + VkSemaphoreWaitInfo timeline_wait_info; - VkDevice dev = vk_device.logical_device; + if (ctx->require_swapchain_recreation) { + if (ctx->window && (ctx->window->width == 0 || ctx->window->height == 0)) + return false; + PDEBUG("[REND] Awaiting device..."); + vkDeviceWaitIdle(vk_device.logical_device); + PDEBUG("[REND] Recreating swapchain..."); + rend_vk_swapchain_destroy(ctx, &ctx->swapchain); + rend_vk_swapchain_create(ctx, &ctx->swapchain); + ctx->require_swapchain_recreation = false; + } - /* wait on timeline semaphore */ - const uint64_t frame_res_index = ctx->frame % ctx->max_frames_in_flight; - ctx->frame_index = frame_res_index; + if (ctx->max_frames_in_flight == 0) + return false; - const uint64_t signal_value = ctx->next_signal_value++; - const uint64_t wait_value = signal_value - ctx->max_frames_in_flight; - ctx->signal_value = signal_value; + /* wait on timeline semaphore */ + frame_res_index = ctx->frame % ctx->max_frames_in_flight; + ctx->frame_index = frame_res_index; - VkSemaphoreWaitInfo timeline_wait_info = { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, - .semaphoreCount = 1, - .pSemaphores = &ctx->timeline_semaphore, - .pValues = &wait_value - }; + wait_value = ctx->next_signal_value - ctx->max_frames_in_flight; - vkWaitSemaphores(vk_device.logical_device, &timeline_wait_info, UINT64_MAX); + timeline_wait_info = (VkSemaphoreWaitInfo) { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, + .semaphoreCount = 1, + .pSemaphores = &ctx->timeline_semaphore, + .pValues = &wait_value + }; - RendVkFrameResources frame_resource = ctx->frame_resources[frame_res_index]; - vkResetCommandPool(dev, frame_resource.command_pool, 0); + vkWaitSemaphores(vk_device.logical_device, &timeline_wait_info, UINT64_MAX); - /* acquire next image */ - VkResult acquire_image = vkAcquireNextImageKHR( - vk_device.logical_device, - ctx->swapchain.handle, - UINT64_MAX, - frame_resource.image_acquired_semaphore, - VK_NULL_HANDLE, - &ctx->image_index); + frame_resource = ctx->frame_resources[frame_res_index]; + vkResetCommandPool(dev, frame_resource.command_pool, 0); - if (acquire_image == VK_ERROR_OUT_OF_DATE_KHR) { - /* out of date images, may need recreating */ - ctx->require_swapchain_recreation = true; - ctx->frame++; - return false; - } else if (acquire_image == VK_SUBOPTIMAL_KHR) { - /* requires recreation but CAN CONTINUE RENDERING */ - ctx->require_swapchain_recreation = true; - } + /* acquire next image */ + acquire_image = vkAcquireNextImageKHR( + vk_device.logical_device, + ctx->swapchain.handle, + UINT64_MAX, + frame_resource.image_acquired_semaphore, + VK_NULL_HANDLE, + &ctx->image_index); + + if (acquire_image == VK_ERROR_OUT_OF_DATE_KHR) { + ctx->require_swapchain_recreation = true; + continue; + } + if (acquire_image == VK_SUBOPTIMAL_KHR) { + ctx->require_swapchain_recreation = true; + } else if (acquire_image != VK_SUCCESS) { + return false; + } + ctx->signal_value = ctx->next_signal_value++; + + { VkCommandBufferBeginInfo cmd_begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT @@ -702,21 +720,20 @@ rend_vk_renderer_frame_begin(RendContextHandle handle) dep_info.pImageMemoryBarriers = layout_barriers; vkCmdPipelineBarrier2(frame_resource.command_buffer, &dep_info); + } - ctx->in_frame = true; - return true; + ctx->in_frame = true; + return true; + } + + return false; } extern void -rend_vk_renderer_frame_end(RendRenderer renderer, float *delta) +rend_vk_renderer_frame_end(RendContextHandle handle, float *delta) { - - RASSERT(renderer && (uintptr_t)renderer != 0xffffffff00000000 && "Invalid renderer. (Possible stack corruption.)"); - RASSERT(renderer->context, "Uninitialized renderer."); - RASSERT(renderer->in_frame, "Must be called inside a frame."); - RASSERT(!renderer->in_pass, "Must not be called inside a render pass."); - - RendVk14Context *ctx = (RendVk14Context *)renderer->context; + RendVk14Context *ctx = (RendVk14Context *)handle; + RASSERT(ctx, "Uninitialized renderer."); RendVkFrameResources res = ctx->frame_resources[ctx->frame_index]; @@ -1909,11 +1926,23 @@ rend_vk_swapchain_create(RendVk14Context *ctx, RendVkSwapchain *swapchain) RASSERT(swapchain && "No swapchain provided."); VkSurfaceCapabilitiesKHR surface_caps; + uint32_t width; + uint32_t height; + VkExtent2D swapchain_extent; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_device.physical_device, ctx->surface, &surface_caps); - uint32_t width = surface_caps.currentExtent.width; - uint32_t height = surface_caps.currentExtent.height; + width = surface_caps.currentExtent.width; + height = surface_caps.currentExtent.height; - VkExtent2D swapchain_extent = {width, height}; + /* 0xffffffff means the surface size is defined by the swapchain extent */ + if (width == 0xffffffffu || height == 0xffffffffu) { + width = ctx->window ? ctx->window->width : 0; + height = ctx->window ? ctx->window->height : 0; + } + if (width == 0) width = 1; + if (height == 0) height = 1; + + swapchain_extent = (VkExtent2D){width, height}; ctx->max_frames_in_flight = REND_MIN_FRAMES_IN_FLIGHT; @@ -1921,9 +1950,16 @@ rend_vk_swapchain_create(RendVk14Context *ctx, RendVkSwapchain *swapchain) ctx->max_frames_in_flight = surface_caps.minImageCount; } - if (surface_caps.maxImageCount < ctx->max_frames_in_flight) { + /* maxImageCount == 0 means there is no maximum */ + if (surface_caps.maxImageCount > 0 && surface_caps.maxImageCount < ctx->max_frames_in_flight) { ctx->max_frames_in_flight = surface_caps.maxImageCount; } + if (ctx->max_frames_in_flight > REND_MAX_FRAMES_IN_FLIGHT) { + ctx->max_frames_in_flight = REND_MAX_FRAMES_IN_FLIGHT; + } + if (ctx->max_frames_in_flight == 0) { + ctx->max_frames_in_flight = 1; + } bool found = false; for (uint32_t i = 0; i < vk_device.swapchain_support.format_count; i++) { diff --git a/Rend/rend_vk_device.c b/Rend/rend_vk_device.c index cb9f8f8..6f25557 100644 --- a/Rend/rend_vk_device.c +++ b/Rend/rend_vk_device.c @@ -10,7 +10,7 @@ static bool -rend_vk_device_create(VkSurfaceKHR surface, RendSpecs specs, RendVkDevice *out_device, uint32_t (*score_devices)(RendVkDevice *, RendSpecs, char **)) +rend_vk_device_create(VkSurfaceKHR surface, RendSpecs specs, RendVkDevice *out_device, uint32_t (*score_devices)(RendVkDevice *, RendSpecs, const char **, uint32_t)) { assert(out_device); @@ -24,8 +24,7 @@ rend_vk_device_create(VkSurfaceKHR surface, RendSpecs specs, RendVkDevice *out_d VkPhysicalDevice physical_devices[device_count]; CHECK_VK_RESULT(vkEnumeratePhysicalDevices(vk_instance, &device_count, physical_devices)); - char **extension_names_darray = NULL; - p_darray_push(extension_names_darray, VK_KHR_SWAPCHAIN_EXTENSION_NAME); + const char *extension_names[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; uint32_t best_score = 1; // 1 so that devices that do not meet specs get ignored RendVkDevice best_device = {0}; @@ -42,7 +41,7 @@ rend_vk_device_create(VkSurfaceKHR surface, RendSpecs specs, RendVkDevice *out_d vkGetPhysicalDeviceMemoryProperties(physical_devices[i], &scoring.memory); /* score device */ - uint32_t dev_score = score_devices(&scoring, specs, extension_names_darray); + uint32_t dev_score = score_devices(&scoring, specs, extension_names, 1); PDEBUG("%-20.20s %5d", scoring.properties.deviceName, dev_score); if (dev_score >= best_score) { if (best_device.swapchain_support.format) { @@ -64,9 +63,6 @@ rend_vk_device_create(VkSurfaceKHR surface, RendSpecs specs, RendVkDevice *out_d } - /* free extensions array after scoring */ - p_darray_destroy(extension_names_darray); - if (best_score > 1) { PDEBUG("Driver version %d.%d.%d", VK_VERSION_MAJOR(best_device.properties.driverVersion), VK_VERSION_MINOR(best_device.properties.driverVersion), VK_VERSION_PATCH(best_device.properties.driverVersion)); PDEBUG("Vulkan API version %d.%d.%d", VK_VERSION_MAJOR(best_device.properties.apiVersion), VK_VERSION_MINOR(best_device.properties.apiVersion), VK_VERSION_PATCH(best_device.properties.apiVersion)); @@ -105,7 +101,7 @@ rend_vk_device_create(VkSurfaceKHR surface, RendSpecs specs, RendVkDevice *out_d VkDeviceQueueCreateInfo q_create_info[index_count]; - f32 queue_priority[2] = {1.0f, 1.0f}; + float queue_priority[2] = {1.0f, 1.0f}; for (uint32_t i = 0; i < index_count; i++) { q_create_info[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; q_create_info[i].queueFamilyIndex = indices[i]; @@ -267,7 +263,7 @@ rend_vk_device_detect_depth_format(RendVkDevice *device) } static uint32_t -rend_vk_device_score_default(RendVkDevice *device, RendSpecs minimum_specs, char **required_extensions) +rend_vk_device_score_default(RendVkDevice *device, RendSpecs minimum_specs, const char **required_extensions, uint32_t required_extension_count) { /* NOTE: When we score the device we will also check for specs. Not meeting a spec * leads to 0 score and continue. We will assume device already contains some information @@ -346,10 +342,8 @@ rend_vk_device_score_default(RendVkDevice *device, RendSpecs minimum_specs, char available_extentions = rmalloc(available_extentions_count * sizeof(*available_extentions)); CHECK_VK_RESULT(vkEnumerateDeviceExtensionProperties(device->physical_device, VK_NULL_HANDLE, &available_extentions_count, available_extentions)); - uint32_t required_extention_count = p_darray_len(required_extensions); - bool overall_found = true; - for (uint32_t i = 0; i < required_extention_count; ++i) { + for (uint32_t i = 0; i < required_extension_count; ++i) { bool found = false; for (uint32_t j = 0; j < available_extentions_count; ++j) { if (strcmp(required_extensions[i], available_extentions[j].extensionName) == 0) { diff --git a/Rend/rend_vk_internal.h b/Rend/rend_vk_internal.h index 5039c61..5a03aea 100644 --- a/Rend/rend_vk_internal.h +++ b/Rend/rend_vk_internal.h @@ -82,8 +82,8 @@ static RendVkDevice vk_device = {0}; static int32_t rend_vk_memory_find_index(VkPhysicalDevice device, uint32_t type_filter, VkMemoryPropertyFlags property_flags); /* rend_vk_device.c */ -static bool rend_vk_device_create(VkSurfaceKHR surface, RendSpecs specs, RendVkDevice *out_device, uint32_t (*score_devices)(RendVkDevice *, RendSpecs, char **)); -static uint32_t rend_vk_device_score_default(RendVkDevice *device, RendSpecs minimum_specs, char **required_extensions); +static bool rend_vk_device_create(VkSurfaceKHR surface, RendSpecs specs, RendVkDevice *out_device, uint32_t (*score_devices)(RendVkDevice *, RendSpecs, const char **, uint32_t)); +static uint32_t rend_vk_device_score_default(RendVkDevice *device, RendSpecs minimum_specs, const char **required_extensions, uint32_t required_extension_count); static void rend_vk_physical_device_query_swapchain_support(RendVkDevice *device); static bool rend_vk_device_detect_depth_format(RendVkDevice *device); static void rend_vk_device_destroy(RendVkDevice *device); diff --git a/archive/Podium/README.md b/archive/Podium/README.md deleted file mode 100644 index fccb70d..0000000 --- a/archive/Podium/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Podium - -Single header platform library with batteries included. - -NOTE TO SELF: Stop trying to redesign your own library! -This shit works! Podium stands on business!!! - -The library does it's best to auto-detect your operating system -and the include the correct OS code for that platform. diff --git a/archive/Podium/p_allocators.c b/archive/Podium/p_allocators.c deleted file mode 100644 index 7e59807..0000000 --- a/archive/Podium/p_allocators.c +++ /dev/null @@ -1,301 +0,0 @@ -PODEF bool -is_power_of_two(uintptr x) { - return (x & (x-1)) == 0; -} - -PODEF uintptr -align_forward(uintptr ptr, usize align) -{ - uintptr p, a, remainder; - assert(is_power_of_two(align)); - - p = ptr; - a = (uintptr)align; - remainder = p & (a-1); - - return (remainder != 0) ? p + a - remainder : p; -} - -PODEF void -p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length) -{ - a->buf = (unsigned char *)backing_buffer; - a->buf_len = backing_buffer_length; - a->curr_offset = 0; - a->prev_offset = 0; -} - -PODEF void* -p_arena_alloc_align(Arena *a, usize s, usize align) -{ - - // Align 'curr_offset' forward to the specified alignment - uintptr curr_ptr = (uintptr)a->buf + (uintptr)a->curr_offset; - uintptr offset = align_forward(curr_ptr, align); - offset -= (uintptr)a->buf; // Change to relative offset - - // Check to see if the backing memory has space left - if (offset+s <= a->buf_len) { - void *ptr = &a->buf[offset]; - a->prev_offset = offset; - a->curr_offset = offset+s; - - memset(ptr, 0, s); - return ptr; - } - - return NULL; -} - -PODEF void* -p_arena_alloc(Arena *a, usize s) { - return p_arena_alloc_align(a, s, DEFAULT_ALIGNMENT); -} - -PODEF void* -p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align) -{ - assert(is_power_of_two(align)); - unsigned char *old_mem = (unsigned char *)old_memory; - - if (old_mem == NULL || old_size == 0) { - return p_arena_alloc_align(a, new_size, align); - } else if (a->buf <= old_mem && old_mem < a->buf+a->buf_len) { - if (a->buf+a->prev_offset == old_mem) { - a->curr_offset = a->prev_offset + new_size; - if (new_size > old_size) { - // Zero the new memory by default - memset(&a->buf[a->curr_offset], 0, new_size-old_size); - } - return old_memory; - } else { - void *new_memory = p_arena_alloc_align(a, new_size, align); - size_t copy_size = old_size < new_size ? old_size : new_size; - memmove(new_memory, old_memory, copy_size); - return new_memory; - } - - } else { - assert(0 && "Memory is out of bounds of the buffer in this arena"); - return NULL; - } -} - -PODEF void* -p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size) -{ - return p_arena_resize_align(a, old_memory, old_size, new_size, DEFAULT_ALIGNMENT); -} - -PODEF void -p_arena_free_all(Arena *a) -{ - a->curr_offset = 0; - a->prev_offset = 0; -} - -PODEF void -p_arena_destroy(Arena* a) -{ - a->buf_len = 0; - a->curr_offset = 0; - a->prev_offset = 0; -} - -PODEF void -p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length) -{ - s->buf = (unsigned char*) backing_buffer; - s->buf_len = backing_buffer_length; - s->offset = 0; -} - -PODEF usize -calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size) -{ - uintptr p, a, modulo, padding, needed_space; - - assert(is_power_of_two(alignment)); - - p = ptr; - a = alignment; - modulo = p & (a-1); // (p % a) as it assumes alignment is a power of two - - padding = 0; - needed_space = 0; - - if (modulo != 0) { // Same logic as 'align_forward' - padding = a - modulo; - } - - needed_space = (uintptr)header_size; - - if (padding < needed_space) { - needed_space -= padding; - - if ((needed_space & (a-1)) != 0) { - padding += a * (1+(needed_space/a)); - } else { - padding += a * (needed_space/a); - } - } - - return (usize)padding; -} - -PODEF void* -p_stack_alloc(Stack *s, usize len, usize alignment) -{ - uintptr curr_addr, next_addr; - usize padding; - Stack_Allocation_Header *header; - - assert(is_power_of_two(alignment)); - - if (alignment > 128) { - // As the padding is 8 bits (1 byte), the largest alignment that can - // be used is 128 bytes - alignment = 128; - } - - curr_addr = (uintptr)s->buf + (uintptr)s->offset; - padding = calc_padding_with_header(curr_addr, (uintptr)alignment, sizeof(Stack_Allocation_Header)); - if (s->offset + padding + len > s->buf_len) { - // Stack allocator is out of memory - return NULL; - } - s->offset += padding; - - next_addr = curr_addr + (uintptr)padding; - header = (Stack_Allocation_Header *)(next_addr - sizeof(Stack_Allocation_Header)); - header->padding = (u8)padding; - - s->offset += len; - - return memset((void *)next_addr, 0, len); -} - -PODEF void -p_stack_free(Stack *s, void *ptr) -{ - if (ptr != NULL) { - uintptr start, end, curr_addr; - Stack_Allocation_Header *header; - usize prev_offset; - - start = (uintptr)s->buf; - end = start + (uintptr)s->buf_len; - curr_addr = (uintptr)ptr; - - if (!(start <= curr_addr && curr_addr < end)) { - assert(0 && "Out of bounds memory address passed to stack allocator (free)"); - return; - } - - if (curr_addr >= start+(uintptr)s->offset) { - // Allow double frees - return; - } - - header = (Stack_Allocation_Header *)(curr_addr - sizeof(Stack_Allocation_Header)); - prev_offset = (usize)(curr_addr - (uintptr)header->padding - start); - - s->offset = prev_offset; - } -} - -PODEF void -p_stack_free_all(Stack* s) -{ - s->offset = 0; -} - -PODEF void -p_stack_destroy(Stack* s) -{ - s->buf = 0; - s->buf_len = 0; - s->offset = 0; -} - -PODEF void -p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align) -{ - /* align start */ - uintptr start = (uintptr) buf; - uintptr aligned = align_forward(start, chunk_align); - buf_len -= (aligned - start); - - /* align chunk usize */ - chunk_size = (usize) align_forward(chunk_size, chunk_align); - - assert(chunk_size >= sizeof(Pool_Free_Node) && "Chunk usize is too small"); - assert(buf_len >= chunk_size && "Backing buffer length is smaller than the chunk size"); - - p->buf = (unsigned char *) buf; - p->pool_size = buf_len; - p->chunk_size = chunk_size; - p->head = NULL; - - p_pool_free_all(p); -} - -PODEF void* -p_pool_alloc(Pool *p) -{ - Pool_Free_Node *node = p->head; - - assert(node != NULL && "Pool allocator has no free memory"); - - p->head = p->head->next; - - return memset(node, 0 , p->chunk_size); -} - -PODEF void -p_pool_free(Pool *p, void *ptr) -{ - Pool_Free_Node *node; - - void *start = p->buf; - void *end = &p->buf[p->pool_size]; - - if (ptr == NULL) { - return; - } - - if (!(start <= ptr && ptr < end)) { - assert(0 && "Memory is out of bounds of the buffer in this pool"); - return; - } - - node = (Pool_Free_Node *)ptr; - node->next = p->head; - p->head = node; -} - -PODEF void -p_pool_free_all(Pool *p) -{ - usize chunk_count = p->pool_size / p->chunk_size; - usize i; - - for (i = 0; i < chunk_count; i++) { - void *ptr = &p->buf[i * p->chunk_size]; - Pool_Free_Node *node = (Pool_Free_Node *)ptr; - - // Push free node onto thte free list - node->next = p->head; - p->head = node; - } -} - -PODEF void -p_pool_destroy(Pool *p) -{ - p->buf = NULL; - p->pool_size = 0; - p->chunk_size = 0; - p->head = NULL; -} - diff --git a/archive/Podium/p_allocators.h b/archive/Podium/p_allocators.h deleted file mode 100644 index 72bae56..0000000 --- a/archive/Podium/p_allocators.h +++ /dev/null @@ -1,44 +0,0 @@ -typedef struct Arena { - unsigned char *buf; - usize buf_len; - usize curr_offset; - usize prev_offset; -} Arena; - -PODEF void p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length); -PODEF void* p_arena_alloc_align(Arena *a, usize s, usize align); -PODEF void* p_arena_alloc(Arena *a, usize s); -PODEF void* p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align); -PODEF void* p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size); -PODEF void p_arena_free_all(Arena *a); -PODEF void p_arena_destroy(Arena* a); - -typedef struct { u8 padding; } Stack_Allocation_Header; -typedef struct Stack { - unsigned char *buf; - usize buf_len; - usize offset; -} Stack; - -PODEF void p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length); -PODEF usize calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size); -PODEF void* p_stack_alloc(Stack *s, usize len, usize alignment); -PODEF void p_stack_free(Stack *s, void *ptr); -PODEF void p_stack_free_all(Stack* s); -PODEF void p_stack_destroy(Stack* s); - -typedef struct Pool_Free_Node Pool_Free_Node; - -typedef struct Pool { - unsigned char *buf; - usize chunk_size; - usize pool_size; - Pool_Free_Node *head; -} Pool; - -struct Pool_Free_Node { struct Pool_Free_Node *next; }; -PODEF void p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align); -PODEF void* p_pool_alloc(Pool *p); -PODEF void p_pool_free(Pool *p, void *ptr); -PODEF void p_pool_free_all(Pool *p); -PODEF void p_pool_destroy(Pool *p); diff --git a/archive/Podium/p_ds.h b/archive/Podium/p_ds.h deleted file mode 100644 index 8731975..0000000 --- a/archive/Podium/p_ds.h +++ /dev/null @@ -1,52 +0,0 @@ -#define DEFAULT_ALIGNMENT (2 * sizeof(void *)) -#define ARRAY_GROW_FACTOR 2 - -typedef struct { - u32 size; - u32 capacity; -} DynamicArrayHeader; - -#define DARRAY_INIT_CAPACITY 64 -#define p_darray_push(array, var) \ - do { \ - if (array == NULL) { \ - DynamicArrayHeader *header = \ - malloc(DARRAY_INIT_CAPACITY * sizeof *array + sizeof *header); \ - header->size = 0; \ - header->capacity = DARRAY_INIT_CAPACITY; \ - array = (void *)(header + 1); \ - } \ - DynamicArrayHeader *header = (DynamicArrayHeader *)(array) - 1; \ - if (header->size >= header->capacity - 1) { \ - unsigned long new_capacity = header->capacity * 2; \ - DynamicArrayHeader *new_header = \ - realloc(header, new_capacity * sizeof *array + sizeof *header); \ - if (!new_header) \ - break; \ - header = new_header; \ - array = (void *)(header + 1); \ - header->capacity = new_capacity; \ - } \ - (array)[header->size++] = var; \ - } while (0) - -#define p_darray_len(array) ((array) ? ((DynamicArrayHeader *)(array) - 1)->size : 0) -#define p_darray_destroy(array) \ -{ \ - free((DynamicArrayHeader *)(array) - 1); \ -} - -#define p_darray_shrink(array) \ - do { \ - DynamicArrayHeader *header = (array) ? ((DynamicArrayHeader *)(array) - 1) : 0;\ - if (header) {\ - if (header->capacity > 4) {\ - unsigned long new_capacity = (header->capacity >> 1) + (header->capacity & 1); \ - void *new_header = realloc(header, new_capacity * sizeof *array + sizeof *header); \ - if (!new_header) break; \ - header = new_header; \ - array = (void *)(header + 1); \ - header->capacity = new_capacity; \ - }\ - }\ - } while (0) diff --git a/archive/Podium/p_log.h b/archive/Podium/p_log.h deleted file mode 100644 index 0f6ce6c..0000000 --- a/archive/Podium/p_log.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Logging! - */ - -#include <stdlib.h> - -#ifdef DEBUG -#define LOG_DEBUG(...) PRINTF(__VA_ARGS__) -#else -#define LOG_DEBUG(...) ((void)0) -#endif - -/* fatal and error always enabled */ -#define PFATAL(message, ...) p_log_printf(P_LOG_LEVEL_FATAL, message, ##__VA_ARGS__) -#define PERROR(message, ...) p_log_printf(P_LOG_LEVEL_ERROR, message, ##__VA_ARGS__) - -/* warn and info enabled by default but can be disabled */ -#define P_LOG_WARN_ENABLED 1 -#define P_LOG_INFO_ENABLED 1 - -#if P_LOG_WARN_ENABLED == 1 -#define PWARN(message, ...) p_log_printf(P_LOG_LEVEL_WARN, message, ##__VA_ARGS__) -#else -#define PWARN(message, ...) -#endif - -#if P_LOG_INFO_ENABLED == 1 -#define PINFO(message, ...) p_log_printf(P_LOG_LEVEL_INFO, message, ##__VA_ARGS__) -#else -#define PINFO(message, ...) -#endif - -#if P_LOG_DEBUG_ENABLED == 1 -#define PDEBUG(message, ...) p_log_printf(P_LOG_LEVEL_DEBUG, message, ##__VA_ARGS__) -#else -#define PDEBUG(message, ...) -#endif - -#if P_LOG_TRACE_ENABLED == 1 -#define PTRACE(message, ...) p_log_printf(P_LOG_LEVEL_TRACE, message, ##__VA_ARGS__) -#else -#define PTRACE(message, ...) -#endif - -typedef enum P_Log_Level { - P_LOG_LEVEL_FATAL = 0, - P_LOG_LEVEL_ERROR, - P_LOG_LEVEL_WARN, - P_LOG_LEVEL_INFO, - P_LOG_LEVEL_DEBUG, - P_LOG_LEVEL_TRACE, - P_COUNT_LOG_LEVEL -} P_Log_Level; - -#define P_PREFIX_LEN 7 -static const char *p_prefix[P_COUNT_LOG_LEVEL] = { - [P_LOG_LEVEL_FATAL] = "[FATAL]", - [P_LOG_LEVEL_ERROR] = "[ERROR]", - [P_LOG_LEVEL_WARN] = "[WARNI]", - [P_LOG_LEVEL_INFO] = "[INFOR]", - [P_LOG_LEVEL_DEBUG] = "[DEBUG]", - [P_LOG_LEVEL_TRACE] = "[TRACE]", -}; - -#define P_MAX_ALLOCS 512 - -typedef struct P_DebugMemoryInfo { - void *ptr; - size_t size; - const char *file; - const char *func; - int line; -} P_DebugMemoryInfo; - -static uint64_t p_alloc_count = 0; -static P_DebugMemoryInfo p_ptr_array[P_MAX_ALLOCS]; - -static inline void * -p_debug_malloc_impl(size_t size, const char *file, int line, const char *func) -{ - void *ptr = malloc(size); - printf("[ALLOC] %p (%zu bytes) -> %s:%d %s()\n", ptr, size, file, line, func); - - if (ptr) { - if (p_alloc_count < P_MAX_ALLOCS) { - p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){ - .ptr = ptr, - .size = size, - .file = file, - .func = func, - .line = line - }; - } else { - fprintf(stderr, "[ERROR] Debug allocator tracking capacity (%d) exceeded!\n", P_MAX_ALLOCS); - } - } - return ptr; -} - -static inline void -p_debug_free_impl(void *ptr, const char *file, int line, const char *func) -{ - printf("[FREE] %p -> %s:%d %s()\n", ptr, file, line, func); - - if (!ptr) return; // Standard C allows free(NULL) - - bool found = false; - uint64_t index = 0; - - /* Find allocation metadata */ - for (index = 0; index < p_alloc_count; ++index) { - if (p_ptr_array[index].ptr == ptr) { - found = true; - break; - } - } - - if (found) { - /* Shift array left to remove tracked struct */ - for (uint64_t j = index; j < p_alloc_count - 1; ++j) { - p_ptr_array[j] = p_ptr_array[j + 1]; - } - p_alloc_count--; - } else { - fprintf(stderr, "[WARNING] Attempted to free untracked/double-freed pointer %p at %s:%d %s()\n", - ptr, file, line, func); - } - - free(ptr); -} - -static inline void * -p_debug_realloc_impl(void *ptr, size_t size, const char *file, int line, const char *func) -{ - if (!ptr) { - return p_debug_malloc_impl(size, file, line, func); - } - if (size == 0) { - p_debug_free_impl(ptr, file, line, func); - return NULL; - } - - void *new_ptr = realloc(ptr, size); - printf("[REALLOC] %p -> %p (%zu bytes) -> %s:%d %s()\n", ptr, new_ptr, size, file, line, func); - - if (new_ptr) { - bool found = false; - for (uint64_t i = 0; i < p_alloc_count; ++i) { - if (p_ptr_array[i].ptr == ptr) { - p_ptr_array[i].ptr = new_ptr; - p_ptr_array[i].size = size; - p_ptr_array[i].file = file; - p_ptr_array[i].line = line; - p_ptr_array[i].func = func; - found = true; - break; - } - } - if (!found) { - if (p_alloc_count < P_MAX_ALLOCS) { - p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){ - .ptr = new_ptr, - .size = size, - .file = file, - .func = func, - .line = line - }; - } - } - } - return new_ptr; -} - -static void -p_debug_memory_report(void) -{ - printf("\n==================== MEMORY REPORT ====================\n"); - printf("Remaining unfreed allocations: %lu\n", (unsigned long)p_alloc_count); - - for (uint64_t i = 0; i < p_alloc_count; ++i) { - P_DebugMemoryInfo *info = &p_ptr_array[i]; - printf("[LEAK] %p (%zu bytes) allocated at %s:%d in %s()\n", - info->ptr, info->size, info->file, info->line, info->func); - } - printf("=======================================================\n"); -} diff --git a/archive/Podium/p_math.c b/archive/Podium/p_math.c deleted file mode 100644 index 928738b..0000000 --- a/archive/Podium/p_math.c +++ /dev/null @@ -1,255 +0,0 @@ -PODEF -int p_ceil(float x) -{ - int base = (int) x; - return (x > base) ? base + 1 : base; -} - -PODEF float -p_cosf(float x) -{ - x += 1.57079632679f; - - while (x > P_PI) x -= P_PI2; - while (x < -P_PI) x += P_PI2; - - const float B = 4.0f / P_PI; - const float C = -4.0f / P_PI_POW2; - - float y = B * x + C * x * (x < 0 ? -x : x); - - const float P = 0.225f; - y = P * (y * (y < 0 ? -y : y) - y) + y; - - return y; -} - -PODEF float -p_sinf(float x) -{ - while (x > P_PI) x -= P_PI2; - while (x < -P_PI) x += P_PI2; - - const float B = 4.0f / P_PI; - const float C = -4.0f / (P_PI_POW2); - - float y = B * x + C * x * (x < 0 ? -x : x); - - const float P = 0.225f; - y = P * (y * (y < 0 ? -y : y) - y) + y; - - return y; -} - -PODEF Vec2f -p_vec2f(float x, float y) -{ - return (Vec2f) { .x = x, .y = y }; -} - -PODEF Vec2f -p_vec2f_add(Vec2f a, Vec2f b) -{ - return (Vec2f) { - .x = a.x + b.x, - .y = a.y + b.y, - }; -} - -PODEF float -p_vec2f_cross(Vec2f a, Vec2f b) -{ - // axby − aybx - return a.x * b.y - a.y * b.x; -} - -PODEF float -p_vec2f_dot(Vec2f a, Vec2f b) -{ - // a · b = ax × bx + ay × by - return a.x * b.x + a.y * b.y; -} - -PODEF Vec2f -p_vec2f_mult(Vec2f a, float b) -{ - return (Vec2f) { - .x = a.x * b, - .y = a.y * b, - }; -} - -PODEF Vec2f -p_vec2f_sub(Vec2f a, Vec2f b) -{ - return (Vec2f) { - .x = a.x - b.x, - .y = a.y - b.y, - }; -} - -PODEF Vec2i -p_vec2i(float x, float y) -{ - return (Vec2i) { .x = x, .y = y }; -} - -PODEF Vec2i -p_vec2i_add(Vec2i a, Vec2i b) -{ - return (Vec2i) { - .x = a.x + b.x, - .y = a.y + b.y, - }; -} - -PODEF float -p_vec2i_cross(Vec2i a, Vec2i b) -{ - // axby − aybx - return a.x * b.y - a.y * b.x; -} - -PODEF float -p_vec2i_dot(Vec2i a, Vec2i b) -{ - // a · b = ax × bx + ay × by - return a.x * b.x + a.y * b.y; -} - -PODEF Vec2i -p_vec2i_mult(Vec2i a, float b) -{ - return (Vec2i) { - .x = a.x * b, - .y = a.y * b, - }; -} - -PODEF Vec2i -p_vec2i_sub(Vec2i a, Vec2i b) -{ - return (Vec2i) { - .x = a.x - b.x, - .y = a.y - b.y, - }; -} - -PODEF Vec3f -p_vec3f(float x, float y, float z) -{ - return (Vec3f) { .x = x, .y = y, .z = z }; -} - -PODEF P_Mat4 -p_mat4_identity(void) -{ - P_Mat4 res = {0}; - res.m[0] = 1.0f; - res.m[5] = 1.0f; - res.m[10] = 1.0f; - res.m[15] = 1.0f; - return res; -} - -PODEF P_Mat4 -p_mat4_mul(P_Mat4 a, P_Mat4 b) -{ - P_Mat4 res = {0}; - for (int col = 0; col < 4; ++col) { - for (int row = 0; row < 4; ++row) { - float sum = 0.0f; - for (int k = 0; k < 4; ++k) { - sum += a.m[k * 4 + row] * b.m[col * 4 + k]; - } - res.m[col * 4 + row] = sum; - } - } - return res; -} - -PODEF P_Mat4 -p_mat4_translate(Vec3f v) -{ - P_Mat4 res = p_mat4_identity(); - res.m[12] = v.x; - res.m[13] = v.y; - res.m[14] = v.z; - return res; -} - -PODEF P_Mat4 -p_mat4_translate_by(P_Mat4 m, Vec3f v) -{ - P_Mat4 res = m; - res.m[12] = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12]; - res.m[13] = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13]; - res.m[14] = m.m[2] * v.x + m.m[6] * v.y + m.m[10] * v.z + m.m[14]; - res.m[15] = m.m[3] * v.x + m.m[7] * v.y + m.m[11] * v.z + m.m[15]; - return res; -} - -PODEF P_Mat4 -p_mat4_rotate_x(float rad) -{ - P_Mat4 res = p_mat4_identity(); - float c = p_cosf(rad); - float s = p_sinf(rad); - res.m[5] = c; - res.m[6] = s; - res.m[9] = -s; - res.m[10] = c; - return res; -} - -PODEF P_Mat4 -p_mat4_rotate_x_by(P_Mat4 m, float rad) -{ - return p_mat4_mul(m, p_mat4_rotate_x(rad)); -} - -PODEF P_Mat4 -p_mat4_rotate_y(float rad) -{ - P_Mat4 res = p_mat4_identity(); - float c = p_cosf(rad); - float s = p_sinf(rad); - res.m[0] = c; - res.m[2] = -s; - res.m[8] = s; - res.m[10] = c; - return res; -} - -PODEF P_Mat4 -p_mat4_rotate_y_by(P_Mat4 m, float rad) -{ - return p_mat4_mul(m, p_mat4_rotate_y(rad)); -} - -PODEF P_Mat4 -p_mat4_rotate_z(float rad) -{ - P_Mat4 res = p_mat4_identity(); - float c = p_cosf(rad); - float s = p_sinf(rad); - res.m[0] = c; - res.m[1] = s; - res.m[4] = -s; - res.m[5] = c; - return res; -} - -PODEF P_Mat4 -p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z) -{ - P_Mat4 res = {0}; - float tan_half_fov = p_sinf(fov_rad * 0.5f) / p_cosf(fov_rad * 0.5f); - - res.m[0] = 1.0f / (aspect * tan_half_fov); - res.m[5] = 1.0f / tan_half_fov; - res.m[10] = far_z / (near_z - far_z); - res.m[11] = -1.0f; - res.m[14] = (near_z * far_z) / (near_z - far_z); - return res; -} diff --git a/archive/Podium/p_math.h b/archive/Podium/p_math.h deleted file mode 100644 index 1dad6ec..0000000 --- a/archive/Podium/p_math.h +++ /dev/null @@ -1,33 +0,0 @@ -typedef struct {int x, y; } Vec2i; -typedef struct {float x, y; } Vec2f; -typedef struct {float x, y, z; } Vec3f; -typedef struct { float m[16];} P_Mat4; // column-major: m[col * 4 + row] -PODEF int p_ceil(float x); -PODEF float p_cosf(float x); -PODEF float p_sinf(float x); - -PODEF Vec2f p_vec2f(float, float); -PODEF Vec2f p_vec2f_add(Vec2f, Vec2f); -PODEF float p_vec2f_cross(Vec2f, Vec2f); -PODEF float p_vec2f_dot(Vec2f, Vec2f); -PODEF Vec2f p_vec2f_mult(Vec2f a, float b); -PODEF Vec2f p_vec2f_sub(Vec2f, Vec2f); -PODEF Vec2i p_vec2i(float, float); -PODEF Vec2i p_vec2i_add(Vec2i, Vec2i); -PODEF float p_vec2i_cross(Vec2i, Vec2i); -PODEF float p_vec2i_dot(Vec2i, Vec2i); -PODEF Vec2i p_vec2i_mult(Vec2i a, float b); -PODEF Vec2i p_vec2i_sub(Vec2i, Vec2i); - -PODEF Vec3f p_vec3f(float, float, float); - -PODEF P_Mat4 p_mat4_identity(void); -PODEF P_Mat4 p_mat4_mul(P_Mat4 a, P_Mat4 b); -PODEF P_Mat4 p_mat4_translate(Vec3f v); -PODEF P_Mat4 p_mat4_translate_by(P_Mat4 m, Vec3f v); -PODEF P_Mat4 p_mat4_rotate_x(float rad); -PODEF P_Mat4 p_mat4_rotate_x_by(P_Mat4 m, float rad); -PODEF P_Mat4 p_mat4_rotate_y(float rad); -PODEF P_Mat4 p_mat4_rotate_y_by(P_Mat4 m, float rad); -PODEF P_Mat4 p_mat4_rotate_z(float rad); -PODEF P_Mat4 p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z); diff --git a/archive/Podium/p_platform_linux.c b/archive/Podium/p_platform_linux.c deleted file mode 100644 index 366e3d7..0000000 --- a/archive/Podium/p_platform_linux.c +++ /dev/null @@ -1,404 +0,0 @@ -/* linux */ -#include <time.h> -#include <fcntl.h> -#include <unistd.h> -#include <dirent.h> -#include <sys/stat.h> -#include <errno.h> -/* x11 */ -#include <X11/Xlib.h> -#include <X11/keysym.h> -/* pulse audio */ -#include <pulse/pulseaudio.h> -#include <pulse/simple.h> -#include <pulse/error.h> -/* joystick */ -#include <linux/joystick.h> - -typedef struct { - pa_simple *audio_stream; -} P_LinuxCtx; - -struct P_Window_Impl { - Display *display; - XImage *image; - Window window; - GC gc; - int screen; - int width; - int height; -}; - -P_LinuxCtx p_ctx = {0}; - -static inline P_KeyCode -_x11_translate_keycode(XEvent xev) -{ - static KeySym ks; - ks = XLookupKeysym(&xev.xkey, 0); - switch (ks) { - /* Arrow keys */ - case XK_Left: return P_KEY_LEFT; - case XK_Right: return P_KEY_RIGHT; - case XK_Up: return P_KEY_UP; - case XK_Down: return P_KEY_DOWN; - /* Common keys */ - case XK_space: return P_KEY_SPACE; - case XK_Escape: return P_KEY_ESCAPE; - case XK_Return: return P_KEY_ENTER; - case XK_A: case XK_a: return P_KEY_A; - case XK_B: case XK_b: return P_KEY_B; - case XK_C: case XK_c: return P_KEY_C; - case XK_D: case XK_d: return P_KEY_D; - case XK_E: case XK_e: return P_KEY_E; - case XK_F: case XK_f: return P_KEY_F; - case XK_G: case XK_g: return P_KEY_G; - case XK_H: case XK_h: return P_KEY_H; - case XK_I: case XK_i: return P_KEY_I; - case XK_J: case XK_j: return P_KEY_J; - case XK_K: case XK_k: return P_KEY_K; - case XK_L: case XK_l: return P_KEY_L; - case XK_M: case XK_m: return P_KEY_M; - case XK_N: case XK_n: return P_KEY_N; - case XK_O: case XK_o: return P_KEY_O; - case XK_P: case XK_p: return P_KEY_P; - case XK_Q: case XK_q: return P_KEY_Q; - case XK_R: case XK_r: return P_KEY_R; - case XK_S: case XK_s: return P_KEY_S; - case XK_T: case XK_t: return P_KEY_T; - case XK_U: case XK_u: return P_KEY_U; - case XK_V: case XK_v: return P_KEY_V; - case XK_W: case XK_w: return P_KEY_W; - case XK_X: case XK_x: return P_KEY_X; - case XK_Y: case XK_y: return P_KEY_Y; - case XK_Z: case XK_z: return P_KEY_Z; - default: return P_KEY_UNKNOWN; - } -} - -POAPI u64 -p_get_time(void) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return NANOS_PER_SEC * ts.tv_sec + ts.tv_nsec; -} - -POAPI void -p_sleep_ns(i64 ns) -{ - struct timespec ts; - ts.tv_sec = ns / 1000000000L; - ts.tv_nsec = ns % 1000000000L; - clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); -} - -POAPI void -p_stdout(void *msg, usize bytes) -{ - write(1, msg, bytes); -} - -POAPI bool -p_file_exists(const char *path) -{ - return (access(path, F_OK) == 0); -} - -POAPI usize -p_file_size(const char *path) -{ - struct stat st; - if (stat(path, &st) == 0) { - return st.st_size; - } - return 0; -} - -PODEF void -p_file_load(const char *path, void *buf_ptr, unsigned long buf_size) -{ - int fd = open(path, O_RDONLY); - if (fd >= 0) { - read(fd, buf_ptr, (size_t)buf_size); - close(fd); - } -} - -PODEF void -p_file_write(const char *path, void *buf_ptr, unsigned long buf_size) -{ - int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0644); - if (fd >= 0) { - write(fd, buf_ptr, (size_t)buf_size); - close(fd); - } -} - -POAPI bool -p_window_open(P_Window *win, int width, int height, const char *title) -{ - win->width = width; - win->height = height; - - XInitThreads(); - - win->display = XOpenDisplay(NULL); - if (win->display == NULL) { - PERROR("Cannot open display!"); - return false; - } - - int screen = DefaultScreen(win->display); - win->window = XCreateSimpleWindow( - win->display, - RootWindow(win->display, screen), - 0, 0, width, height, - 1, - BlackPixel(win->display, screen), - BlackPixel(win->display, screen) - ); - - win->screen = screen; - - XStoreName(win->display, win->window, title); - XSelectInput(win->display, win->window, - KeyPressMask | KeyReleaseMask | - ButtonPressMask | ButtonReleaseMask | - PointerMotionMask | StructureNotifyMask); - XMapWindow(win->display, win->window); - - win->gc = DefaultGC(win->display, screen); - - Atom wm_delete = XInternAtom(win->display, "WM_DELETE_WINDOW", False); - XSetWMProtocols(win->display, win->window, &wm_delete, 1); - - return true; -} - -POAPI void -p_window_close(P_Window *win) -{ - if (!win) return; - - if (win->image) { - // XDestroyImage(win->image); - win->image = NULL; - } - - if (win->display) { - Display *dpy = win->display; - Window w = win->window; - win->display = NULL; - win->window = 0; - - if (w) { - XDestroyWindow(dpy, w); - } - XSync(dpy, False); - } -} - -POAPI bool -p_window_is_open(P_Window *win) -{ - return win && win->display != NULL; -} - -POAPI void -p_window_size(P_Window *win, int *window_width, int *window_height) -{ - if (!win) return; - - XWindowAttributes attr; - XGetWindowAttributes(win->display, win->window, &attr); - *window_width = attr.width; - *window_height = attr.height; - - win->width = attr.width; - win->height = attr.height; -} - -POAPI void -p_window_draw(P_Window *win, u32 *pixels, int width, int height) -{ - int screen = win->screen; - - win->image = XCreateImage( - win->display, - DefaultVisual(win->display, screen), - DefaultDepth(win->display, screen), - ZPixmap, - 0, - (char*)pixels, - width, - height, - 32, - width * 4 - ); - - int window_width, window_height; - p_window_size(win, &window_width, &window_height); - - XPutImage( - win->display, - win->window, - win->gc, - win->image, - 0, 0, - 0, 0, - win->width, - win->height); - - XFlush(win->display); -} - -POAPI bool -p_window_poll_event(P_Window *win, P_Event *ev) -{ - if (XPending(win->display)) { - static XEvent xev; - XNextEvent(win->display, &xev); - - switch (xev.type) { - - case KeyPress: - ev->type = P_EVENT_KEY_DOWN; - ev->key.key = _x11_translate_keycode(xev); - return true; - - case KeyRelease: - ev->type = P_EVENT_KEY_UP; - ev->key.key = _x11_translate_keycode(xev); - return true; - - case ConfigureNotify: - ev->type = P_EVENT_WINDOW_RESIZE; - ev->resize.width = xev.xconfigure.width; - ev->resize.height = xev.xconfigure.height; - return true; - - case ClientMessage: - if ((Atom)xev.xclient.data.l[0] == - XInternAtom(win->display, "WM_DELETE_WINDOW", False)) { - ev->type = P_EVENT_WINDOW_CLOSE; - return true; - } - break; - - case MotionNotify: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_MOVED; - ev->pointer.button = 0; - ev->pointer.x = xev.xmotion.x; - ev->pointer.y = xev.xmotion.y; - return true; - - case ButtonPress: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_PRESSED; - ev->pointer.button = xev.xbutton.button; /* 1=LMB, 2=MMB, 3=RMB */ - ev->pointer.x = xev.xbutton.x; - ev->pointer.y = xev.xbutton.y; - return true; - - case ButtonRelease: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_RELEASED; - ev->pointer.button = xev.xbutton.button; - ev->pointer.x = xev.xbutton.x; - ev->pointer.y = xev.xbutton.y; - return true; - } - } - - ev->type = P_EVENT_NONE; - return false; -} - -POAPI void -p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc) -{ - pa_sample_spec ss = { - .format = PA_SAMPLE_S16LE, - .rate = sample_rate, - .channels = channels, - }; - - int error; - p_ctx.audio_stream = pa_simple_new( - NULL, - name, - PA_STREAM_PLAYBACK, - NULL, - desc, - &ss, - NULL, - NULL, - &error - ); - - if (!p_ctx.audio_stream) { - PERROR("PulseAudio init failed: %s\n", pa_strerror(error)); - exit(1); - } -} - -POAPI void -p_audio_quit() -{ - if (p_ctx.audio_stream) { - pa_simple_free(p_ctx.audio_stream); - p_ctx.audio_stream = NULL; - } -} - -POAPI void -p_audio_write(const i16 *samples, usize count) -{ - if (!p_ctx.audio_stream) return; - - int error; - if (pa_simple_write(p_ctx.audio_stream, samples, count * sizeof(i16), &error) < 0) { - PERROR("PulseAudio write failed: %s\n", pa_strerror(error)); - } - - pa_simple_drain(p_ctx.audio_stream, &error); -} - -#ifdef P_MODULE_VULKAN - -#ifndef VK_USE_PLATFORM_XLIB_KHR -#define VK_USE_PLATFORM_XLIB_KHR -#endif -#include <vulkan/vulkan_xlib.h> - -POAPI char** -p_vulkan_get_extensions() -{ - char **darr = NULL; - p_darray_push(darr, "VK_KHR_surface"); - p_darray_push(darr, "VK_KHR_xcb_surface"); - p_darray_push(darr, "VK_KHR_xlib_surface"); - return darr; -} - -POAPI bool -p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface) -{ - if (!instance || !window || !window->display || !window->window || !out_surface) { - return false; - } - - VkXlibSurfaceCreateInfoKHR create_info = {0}; - create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - create_info.pNext = NULL; - create_info.flags = 0; - create_info.dpy = window->display; - create_info.window = window->window; - - VkResult result = vkCreateXlibSurfaceKHR(instance, &create_info, allocator, out_surface); - - return (result == VK_SUCCESS); -} -#endif diff --git a/archive/Podium/p_platform_win32.c b/archive/Podium/p_platform_win32.c deleted file mode 100644 index 556b43b..0000000 --- a/archive/Podium/p_platform_win32.c +++ /dev/null @@ -1,402 +0,0 @@ -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#include <mmsystem.h> -#include <xinput.h> /* gamepad support */ - -typedef struct { - HWAVEOUT wave_out; - WAVEHDR wave_header; -} P_Win32Ctx; - -struct P_Window_Impl { - HWND hwnd; - HDC hdc; - BITMAPINFO bitmap_info; - int width; - int height; - bool closed; -}; - -P_Win32Ctx p_ctx = {0}; - -static inline P_KeyCode -_win32_translate_vkey(WPARAM vk) -{ - switch (vk) { - case VK_LEFT: return P_KEY_LEFT; - case VK_RIGHT: return P_KEY_RIGHT; - case VK_UP: return P_KEY_UP; - case VK_DOWN: return P_KEY_DOWN; - case VK_SPACE: return P_KEY_SPACE; - case VK_ESCAPE: return P_KEY_ESCAPE; - case VK_RETURN: return P_KEY_ENTER; - case 'A': return P_KEY_A; - case 'B': return P_KEY_B; - case 'C': return P_KEY_C; - case 'D': return P_KEY_D; - case 'E': return P_KEY_E; - case 'F': return P_KEY_F; - case 'G': return P_KEY_G; - case 'H': return P_KEY_H; - case 'I': return P_KEY_I; - case 'J': return P_KEY_J; - case 'K': return P_KEY_K; - case 'L': return P_KEY_L; - case 'M': return P_KEY_M; - case 'N': return P_KEY_N; - case 'O': return P_KEY_O; - case 'P': return P_KEY_P; - case 'Q': return P_KEY_Q; - case 'R': return P_KEY_R; - case 'S': return P_KEY_S; - case 'T': return P_KEY_T; - case 'U': return P_KEY_U; - case 'V': return P_KEY_V; - case 'W': return P_KEY_W; - case 'X': return P_KEY_X; - case 'Y': return P_KEY_Y; - case 'Z': return P_KEY_Z; - default: return P_KEY_UNKNOWN; - } -} - -POAPI u64 -p_get_time(void) -{ - LARGE_INTEGER count, freq; - QueryPerformanceCounter(&count); - QueryPerformanceFrequency(&freq); - return (u64)((count.QuadPart * 1000000000ULL) / freq.QuadPart); -} - -POAPI void -p_sleep_ns(i64 ns) -{ - /* windows sleep is millisecond-based........ */ - Sleep((DWORD)(ns / 1000000)); -} - -POAPI void -p_stdout(void *msg, usize bytes) -{ - HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD bytes_written; - WriteFile(stdout_handle, msg, bytes, &bytes_written, NULL); -} - -POAPI bool -p_file_exists(const char *path) -{ - DWORD attr = GetFileAttributesA(path); - return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY)); -} - -POAPI usize -p_file_size(const char *path) -{ - WIN32_FILE_ATTRIBUTE_DATA data; - if (GetFileAttributesExA(path, GetFileExInfoStandard, &data)) { - return data.nFileSizeLow; - } - return 0; -} - -PODEF void -p_file_load(const char *path, void *buf_ptr, unsigned long buf_size) -{ - HANDLE file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if (file != INVALID_HANDLE_VALUE) { - DWORD bytes_read; - ReadFile(file, buf_ptr, buf_size, &bytes_read, NULL); - CloseHandle(file); - } -} - -PODEF void -p_file_write(const char *path, void *buf_ptr, unsigned long buf_size) -{ - HANDLE file = CreateFileA(path, GENERIC_WRITE, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (file != INVALID_HANDLE_VALUE) { - DWORD bytes_written; - WriteFile(file, buf_ptr, buf_size, &bytes_written, NULL); - CloseHandle(file); - } -} - -static LRESULT CALLBACK -_win32_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - P_Window *win = (P_Window*)GetWindowLongPtrA(hwnd, GWLP_USERDATA); - switch (msg) { - case WM_CLOSE: - /* Handled in poll_event; mark as closed */ - if (win) win->closed = true; - return 0; - } - return DefWindowProcA(hwnd, msg, wparam, lparam); -} - -POAPI bool -p_window_open(P_Window *win, int width, int height, const char *title) -{ - HINSTANCE instance = GetModuleHandleA(NULL); - - WNDCLASSA wc = {0}; - wc.lpfnWndProc = _win32_wnd_proc; - wc.hInstance = instance; - wc.lpszClassName = "P_Window_Class"; - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - - RegisterClassA(&wc); - - win->hwnd = CreateWindowExA(0, wc.lpszClassName, title, - WS_OVERLAPPEDWINDOW | WS_VISIBLE, - CW_USEDEFAULT, CW_USEDEFAULT, width, height, - NULL, NULL, instance, NULL); - - if (!win->hwnd) return false; - - SetWindowLongPtrA(win->hwnd, GWLP_USERDATA, (LONG_PTR)win); - win->hdc = GetDC(win->hwnd); - win->width = width; - win->height = height; - win->closed = false; - - return true; -} - -POAPI void -p_window_close(P_Window *win) -{ - if (!win) return; - if (win->hdc) ReleaseDC(win->hwnd, win->hdc); - if (win->hwnd) DestroyWindow(win->hwnd); - win->hdc = NULL; - win->hwnd = NULL; -} - -POAPI bool -p_window_is_open(P_Window *win) -{ - return win && win->hwnd != NULL; -} - -POAPI void -p_window_size(P_Window *win, int *window_width, int *window_height) -{ - if (!win) return; - RECT rect; - GetClientRect(win->hwnd, &rect); - *window_width = rect.right - rect.left; - *window_height = rect.bottom - rect.top; - win->width = *window_width; - win->height = *window_height; -} - -POAPI void -p_window_draw(P_Window *win, u32 *pixels, int width, int height) -{ - BITMAPINFO bmi = {0}; - bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); - bmi.bmiHeader.biWidth = width; - bmi.bmiHeader.biHeight = -height; /* top-down */ - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - - StretchDIBits(win->hdc, - 0, 0, win->width, win->height, - 0, 0, width, height, - pixels, &bmi, DIB_RGB_COLORS, SRCCOPY); -} - -POAPI bool -p_window_poll_event(P_Window *win, P_Event *ev) -{ - if (win->closed) { - win->closed = false; - ev->type = P_EVENT_WINDOW_CLOSE; - return true; - } - - MSG msg; - while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) { - switch (msg.message) { - - case WM_QUIT: - ev->type = P_EVENT_WINDOW_CLOSE; - return true; - - /* Key press */ - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - ev->type = P_EVENT_KEY_DOWN; - ev->key.key = _win32_translate_vkey(msg.wParam); - return true; - - /* Key release */ - case WM_KEYUP: - case WM_SYSKEYUP: - ev->type = P_EVENT_KEY_UP; - ev->key.key = _win32_translate_vkey(msg.wParam); - return true; - - /* Mouse movement */ - case WM_MOUSEMOVE: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_MOVED; - ev->pointer.button = 0; - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - /* Mouse buttons */ - case WM_LBUTTONDOWN: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_PRESSED; - ev->pointer.button = 1; /* 1 = LMB, matching X11 convention */ - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_LBUTTONUP: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_RELEASED; - ev->pointer.button = 1; - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_RBUTTONDOWN: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_PRESSED; - ev->pointer.button = 3; /* 3 = RMB */ - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_RBUTTONUP: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_RELEASED; - ev->pointer.button = 3; - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_MBUTTONDOWN: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_PRESSED; - ev->pointer.button = 2; /* 2 = MMB */ - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_MBUTTONUP: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_RELEASED; - ev->pointer.button = 2; - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - /* Window resize */ - case WM_SIZE: - ev->type = P_EVENT_WINDOW_RESIZE; - ev->resize.width = (u32)(msg.lParam & 0xFFFF); - ev->resize.height = (u32)((msg.lParam >> 16) & 0xFFFF); - win->width = ev->resize.width; - win->height = ev->resize.height; - return true; - - default: - TranslateMessage(&msg); - DispatchMessageA(&msg); - break; - } - } - - ev->type = P_EVENT_NONE; - return false; -} - - -POAPI void -p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc) -{ - (void)name; (void)desc; /* WinMM doesn't use a stream name */ - - WAVEFORMATEX wfx = {0}; - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nChannels = channels; - wfx.nSamplesPerSec = sample_rate; - wfx.wBitsPerSample = 16; - wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample) / 8; - wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; - - if (waveOutOpen(&p_ctx.wave_out, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) { - exit(1); - } -} - -POAPI void -p_audio_quit() -{ - if (p_ctx.wave_out) { - waveOutReset(p_ctx.wave_out); - waveOutClose(p_ctx.wave_out); - p_ctx.wave_out = NULL; - } -} - -POAPI void -p_audio_write(const i16 *samples, usize count) -{ - if (!p_ctx.wave_out) return; - - WAVEHDR header = {0}; - header.lpData = (LPSTR)samples; - header.dwBufferLength = (DWORD)(count * sizeof(i16)); - - waveOutPrepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR)); - waveOutWrite(p_ctx.wave_out, &header, sizeof(WAVEHDR)); - - /* wait for playback to finish (synchronous, mirrors pa_simple_drain) */ - while (!(header.dwFlags & WHDR_DONE)) Sleep(1); - - waveOutUnprepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR)); -} - -#ifdef P_MODULE_VULKAN - -#ifndef VK_USE_PLATFORM_WIN32_KHR -#define VK_USE_PLATFORM_WIN32_KHR -#endif -#include <vulkan/vulkan_win32.h> - -POAPI char** -p_vulkan_get_extensions() -{ - char **darr = NULL; - p_darray_push(darr, "VK_KHR_surface"); - p_darray_push(darr, "VK_KHR_win32_surface"); - return darr; -} - -POAPI bool -p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface) -{ - if (!instance || !window || !window->hwnd || !out_surface) { - return false; - } - - VkWin32SurfaceCreateInfoKHR create_info = {0}; - create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - create_info.pNext = NULL; - create_info.flags = 0; - create_info.hwnd = window->hwnd; - create_info.hinstance = GetModuleHandleA(NULL); - - VkResult result = vkCreateWin32SurfaceKHR(instance, &create_info, allocator, out_surface); - return (result == VK_SUCCESS); -} -#endif diff --git a/archive/Podium/p_string.c b/archive/Podium/p_string.c deleted file mode 100644 index 73cb0ea..0000000 --- a/archive/Podium/p_string.c +++ /dev/null @@ -1,201 +0,0 @@ -PODEF char* -p_find_char(char *buf, char needle) -{ - while (buf && *buf != needle) buf++; - return buf; -} - -PODEF bool -p_strcmp(const char *a, const char *b) -{ - if (p_strlen(a) != p_strlen(b)) - return false; - - while (a && b && *a != '\0') { - if (*(a++) != *(b++)) return false; - } - - return true; -} - -PODEF int -p_strlen(const char *a) -{ - int i = 0; - while (*a++ != '\0') i++; - return i; -} - -PODEF int -p_strtoi(const char *str) -{ - int res = 0; - - while (*str) { - if (*str >= '0' && *str <= '9') { - res = res * 10 + (*str - '0'); - } else { - return res; - } - str++; - } - - return res; -} - -PODEF char* -p_strtok(const char *str, char c) -{ - while (*str != c && *str++ != '\0'); - return (*str == c) ? (char*) ++str : 0; -} - -PODEF P_StringView -p_strview(const char *cstr) -{ - P_StringView sv = {0}; - sv.cstr = cstr; - sv.len = p_strlen(cstr); - return sv; -} - -PODEF void -p_strview_chop_left(P_StringView *sv, u32 n) -{ - if (n > sv->len) n = sv->len; - sv->cstr += n; - sv->len -= n; -} - -PODEF void -p_strview_chop_right(P_StringView *sv, u32 n) -{ - if (n > sv->len) n = sv->len; - sv->len -= n; -} - -PODEF P_StringView -p_strview_chop_delim(P_StringView *sv, char delim) -{ - u32 i = 0; - while (i < sv->len && sv->cstr[i] != delim) { - i++; - } - - P_StringView tok; - if (i < sv->len) { - tok.cstr = sv->cstr; - tok.len = i; - p_strview_chop_left(sv, i + 1); - return tok; - } - - tok = *sv; - p_strview_chop_left(sv, sv->len); - return tok; -} - -PODEF P_StringView -p_strview_chop_type(P_StringView *sv, int(*istype)(int)) -{ - u32 i = 0; - while (i < sv->len && istype(sv->cstr[i])) { - i++; - } - - P_StringView tok; - if (i < sv->len) { - tok.cstr = sv->cstr; - tok.len = i; - p_strview_chop_left(sv, i + 1); - return tok; - } - - tok = *sv; - p_strview_chop_left(sv, sv->len); - return tok; -} - - -PODEF void -p_strview_trim_left(P_StringView *sv) -{ - while (sv->len > 0 && p_is_space(sv->cstr[0])) { - p_strview_chop_left(sv, 1); - } -} - -PODEF void -p_strview_trim_right(P_StringView *sv) -{ - while (sv->len > 0 && p_is_space(sv->cstr[sv->len-1])) { - p_strview_chop_right(sv, 1); - } -} - -PODEF void -p_strview_trim(P_StringView *sv) -{ - p_strview_trim_left(sv); - p_strview_trim_right(sv); -} - -PODEF int -p_is_space(int c) -{ - /* Checks for: space, form feed (\f), line feed (\n), - * carriage return (\r), horizontal tab (\t), vertical tab (\v) */ - return (c == ' ' || (c >= '\t' && c <= '\r')); -} - -PODEF int -p_is_digit(int c) -{ - return (c >= '0' && c <= '9'); -} - -PODEF int -p_is_alpha(int c) -{ - return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); -} - -PODEF int -p_is_alnum(int c) -{ - return (p_is_alpha(c) || p_is_digit(c)); -} - -PODEF int -p_is_hex(int c) -{ - return (p_is_digit(c) || - (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F')); -} - -PODEF int -p_is_upper(int c) -{ - return (c >= 'A' && c <= 'Z'); -} - -PODEF int -p_is_lower(int c) -{ - return (c >= 'a' && c <= 'z'); -} - -PODEF int -p_to_lower(int c) -{ - if (p_is_upper(c)) return (c + ('a' - 'A')); - return c; -} - -PODEF int -p_to_upper(int c) -{ - if (p_is_lower(c)) return (c - ('a' - 'A')); - return c; -} diff --git a/archive/Podium/p_string.h b/archive/Podium/p_string.h deleted file mode 100644 index 5c4077b..0000000 --- a/archive/Podium/p_string.h +++ /dev/null @@ -1,38 +0,0 @@ -typedef struct { - const char *cstr; - u32 len; -} P_StringView; - -typedef struct { - P_StringView sv; -} P_StringBuilder; - -/* Classic C string functions */ -PODEF char* p_find_char(char *buf, char needle); -PODEF bool p_strcmp(const char *a, const char *b); -PODEF int p_strlen(const char *a); -PODEF int p_strtoi(const char *str); -PODEF char* p_strtok(const char *str, char c); - -/* String views */ -PODEF P_StringView p_strview(const char *cstr); -PODEF void p_strview_chop_left(P_StringView *sv, u32 n); -PODEF void p_strview_chop_right(P_StringView *sv, u32 n); -PODEF P_StringView p_strview_chop_delim(P_StringView *sv, char delim); -PODEF P_StringView p_strview_chop_type(P_StringView *sv, int(*istype)(int)); -PODEF void p_strview_trim_left(P_StringView *sv); -PODEF void p_strview_trim_right(P_StringView *sv); -PODEF void p_strview_trim(P_StringView *sv); - -/* Classification */ -PODEF int p_is_space(int c); -PODEF int p_is_digit(int c); -PODEF int p_is_alpha(int c); -PODEF int p_is_alnum(int c); -PODEF int p_is_upper(int c); -PODEF int p_is_lower(int c); -PODEF int p_is_hex(int c); - -/* Transformation */ -PODEF int p_to_lower(int c); -PODEF int p_to_upper(int c); diff --git a/archive/Podium/p_types.h b/archive/Podium/p_types.h deleted file mode 100644 index 1da81be..0000000 --- a/archive/Podium/p_types.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef _PODIUM_TYPES_ -#define _PODIUM_TYPES_ - -#define NANOS_PER_SEC (1000*1000*1000) -#define P_PI_HALF 1.57079632679f -#define P_PI 3.14159265f -#define P_PI2 6.28318531f -#define P_PI_POW2 9.86960440f - -#ifdef NDEBUG - #define assert(x) ((void)0) -#else - #ifndef assert - #define assert(x) \ - do { \ - if (!(x)) { \ - __builtin_trap(); \ - } \ - } while (0) - #endif -#endif - -#ifndef BUFSIZ - #define BUFSIZ 8192 -#endif - -typedef unsigned char u8; -typedef signed char i8; -typedef unsigned short u16; -typedef short i16; -typedef unsigned int u32; -typedef int i32; -typedef unsigned long long u64; -typedef long long i64; -typedef float f32; -typedef double f64; - -#ifndef __cplusplus -#define bool _Bool -#define false 0 -#define true 1 -#endif - -/* C++ uses static_assert, C11 uses _Static_assert */ -#ifdef __cplusplus - #define STATIC_ASSERT static_assert -#else - #define STATIC_ASSERT _Static_assert -#endif - -STATIC_ASSERT(sizeof(u8) == 1, "u8 must be 1 byte"); -STATIC_ASSERT(sizeof(u16) == 2, "u16 must be 2 bytes"); -STATIC_ASSERT(sizeof(u32) == 4, "u32 must be 4 bytes"); -STATIC_ASSERT(sizeof(u64) == 8, "u64 must be 8 bytes"); - -STATIC_ASSERT(sizeof(i8) == 1, "i8 must be 1 byte"); -STATIC_ASSERT(sizeof(i16) == 2, "i16 must be 2 bytes"); -STATIC_ASSERT(sizeof(i32) == 4, "i32 must be 4 bytes"); -STATIC_ASSERT(sizeof(i64) == 8, "i64 must be 8 bytes"); - -STATIC_ASSERT(sizeof(f32) == 4, "f32 must be 4 bytes"); -STATIC_ASSERT(sizeof(f64) == 8, "f64 must be 8 bytes"); -#define U8_MAX 255 -#define U16_MAX 65535 -#define U32_MAX 4294967295 - -/* Architecture specific sizing */ -#if defined(__x86_64__) || defined(__aarch64__) || defined(_M_X64) - typedef u64 usize; - typedef u64 uintptr; - typedef u32 idx; - STATIC_ASSERT(sizeof(usize) == 8, "usize must be 8 bytes on 64-bit"); -#else - typedef u32 usize; - typedef u32 uintptr; - typedef u16 idx; - STATIC_ASSERT(sizeof(usize) == 4, "usize must be 4 bytes on 32-bit"); -#endif - -#ifndef NULL - #ifdef __cplusplus - #define NULL 0 - #else - #define NULL ((void*)0) - #endif -#endif - -#endif // _PODIUM_TYPES_ diff --git a/archive/Podium/podium.c b/archive/Podium/podium.c deleted file mode 100644 index 306e337..0000000 --- a/archive/Podium/podium.c +++ /dev/null @@ -1 +0,0 @@ -#define PODIUM_IMPLEMENTATION diff --git a/archive/Podium/podium.h b/archive/Podium/podium.h deleted file mode 100644 index d94b4e5..0000000 --- a/archive/Podium/podium.h +++ /dev/null @@ -1,2269 +0,0 @@ -/* ===========================================================================
- * PODIUM - Kitchen Sink - Copyright (c) 2026 Vasco Alves
- *
- * ---------------------------------------------------------------------------
- * To stand on the shoulders of giants...
- * Giants must stand still!"
- * - Eskil Steenberg - "You should finish your software" – BSC 2025
- * ---------------------------------------------------------------------------
- *
- * PREFIX: P_ (types) or p_ (functions & variables)
- *
- * COMPILE TIME FLAGS
- * | Name | Description |
- * |-----------------|---------------------------------------------------------|
- * | PODIUM_WIN32 | Used to explicitly compile for Windows. |
- * | PODIUM_LINUX | Used to explicitly compile for Linux and FreeBSD. |
- *
- * MODULES
- * | Name | Description |
- * |-----------------|---------------------------------------------------------|
- * | P_MODULE_VULKAN | Gives access to vulkan related utilities. |
- * | P_MODULE_STRING | Include string manipulation utilities. |
- * | P_MODULE_MATH | Trigonometry and linear algebra functions. |
- * | P_MODULE_ALLOC | Memory allocators: Arena, Stack & Pool. |
- *
- * =========================================================================== */
-
-#ifndef _PODIUM_H_
-#define _PODIUM_H_
-
-#define PODIUM_MAJOR "0" // needs more revisions and use to reach 1.0 status
-#define PODIUM_MINOR "2" // vulkan module
-#define PODIUM_PATCH "2" // gamepad
-
-#define POAPI static inline
-#define PODEF static inline
-
-/*
- * Auto-detect platforms.
- */
-
-/* Windows */
-#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__)
- #define PODIUM_WIN32
- #ifndef WIN32_LEAN_AND_MEAN
- #define WIN32_LEAN_AND_MEAN
- #endif
-/* Apple */
-#elif defined(__APPLE__) || defined(__MACH__)
- #include <TargetConditionals.h>
- #define PODIUM_APPLE
- #if TARGET_OS_IPHONE
- #define PODIUM_IOS
- #else
- #define PODIUM_MACOS
- #endif
-/* Linux / BSD */
-#elif defined(__linux__) || defined(__FreeBSD__)
- #define PODIUM_LINUX
- #ifndef _POSIX_C_SOURCE
- #define _POSIX_C_SOURCE 200112L
- #endif
-#endif
-
-#include <stdarg.h>
-#include <stdint.h>
-#include <stddef.h>
-#include <string.h> /* memcpy */
-#include <stdio.h> /* vsnprintf */
-
-/*
- * NOTE: The <REPLACE> comments are used by the build
- * script to know what includes to inline into the
- * final single header file!
- */
-
-/* --- Start of p_types.h --- */
-#ifndef _PODIUM_TYPES_
-#define _PODIUM_TYPES_
-
-#define NANOS_PER_SEC (1000*1000*1000)
-#define P_PI_HALF 1.57079632679f
-#define P_PI 3.14159265f
-#define P_PI2 6.28318531f
-#define P_PI_POW2 9.86960440f
-
-#ifdef NDEBUG
- #define assert(x) ((void)0)
-#else
- #ifndef assert
- #define assert(x) \
- do { \
- if (!(x)) { \
- __builtin_trap(); \
- } \
- } while (0)
- #endif
-#endif
-
-#ifndef BUFSIZ
- #define BUFSIZ 8192
-#endif
-
-typedef unsigned char u8;
-typedef signed char i8;
-typedef unsigned short u16;
-typedef short i16;
-typedef unsigned int u32;
-typedef int i32;
-typedef unsigned long long u64;
-typedef long long i64;
-typedef float f32;
-typedef double f64;
-
-#ifndef __cplusplus
-#define bool _Bool
-#define false 0
-#define true 1
-#endif
-
-/* C++ uses static_assert, C11 uses _Static_assert */
-#ifdef __cplusplus
- #define STATIC_ASSERT static_assert
-#else
- #define STATIC_ASSERT _Static_assert
-#endif
-
-STATIC_ASSERT(sizeof(u8) == 1, "u8 must be 1 byte");
-STATIC_ASSERT(sizeof(u16) == 2, "u16 must be 2 bytes");
-STATIC_ASSERT(sizeof(u32) == 4, "u32 must be 4 bytes");
-STATIC_ASSERT(sizeof(u64) == 8, "u64 must be 8 bytes");
-
-STATIC_ASSERT(sizeof(i8) == 1, "i8 must be 1 byte");
-STATIC_ASSERT(sizeof(i16) == 2, "i16 must be 2 bytes");
-STATIC_ASSERT(sizeof(i32) == 4, "i32 must be 4 bytes");
-STATIC_ASSERT(sizeof(i64) == 8, "i64 must be 8 bytes");
-
-STATIC_ASSERT(sizeof(f32) == 4, "f32 must be 4 bytes");
-STATIC_ASSERT(sizeof(f64) == 8, "f64 must be 8 bytes");
-#define U8_MAX 255
-#define U16_MAX 65535
-#define U32_MAX 4294967295
-
-/* Architecture specific sizing */
-#if defined(__x86_64__) || defined(__aarch64__) || defined(_M_X64)
- typedef u64 usize;
- typedef u64 uintptr;
- typedef u32 idx;
- STATIC_ASSERT(sizeof(usize) == 8, "usize must be 8 bytes on 64-bit");
-#else
- typedef u32 usize;
- typedef u32 uintptr;
- typedef u16 idx;
- STATIC_ASSERT(sizeof(usize) == 4, "usize must be 4 bytes on 32-bit");
-#endif
-
-#ifndef NULL
- #ifdef __cplusplus
- #define NULL 0
- #else
- #define NULL ((void*)0)
- #endif
-#endif
-
-#endif // _PODIUM_TYPES_
-/* --- End of p_types.h --- */
-/* --- Start of p_log.h --- */
-/*
- * Logging!
- */
-
-#include <stdlib.h>
-
-#ifdef DEBUG
-#define LOG_DEBUG(...) PRINTF(__VA_ARGS__)
-#else
-#define LOG_DEBUG(...) ((void)0)
-#endif
-
-/* fatal and error always enabled */
-#define PFATAL(message, ...) p_log_printf(P_LOG_LEVEL_FATAL, message, ##__VA_ARGS__)
-#define PERROR(message, ...) p_log_printf(P_LOG_LEVEL_ERROR, message, ##__VA_ARGS__)
-
-/* warn and info enabled by default but can be disabled */
-#define P_LOG_WARN_ENABLED 1
-#define P_LOG_INFO_ENABLED 1
-
-#if P_LOG_WARN_ENABLED == 1
-#define PWARN(message, ...) p_log_printf(P_LOG_LEVEL_WARN, message, ##__VA_ARGS__)
-#else
-#define PWARN(message, ...)
-#endif
-
-#if P_LOG_INFO_ENABLED == 1
-#define PINFO(message, ...) p_log_printf(P_LOG_LEVEL_INFO, message, ##__VA_ARGS__)
-#else
-#define PINFO(message, ...)
-#endif
-
-#if P_LOG_DEBUG_ENABLED == 1
-#define PDEBUG(message, ...) p_log_printf(P_LOG_LEVEL_DEBUG, message, ##__VA_ARGS__)
-#else
-#define PDEBUG(message, ...)
-#endif
-
-#if P_LOG_TRACE_ENABLED == 1
-#define PTRACE(message, ...) p_log_printf(P_LOG_LEVEL_TRACE, message, ##__VA_ARGS__)
-#else
-#define PTRACE(message, ...)
-#endif
-
-typedef enum P_Log_Level {
- P_LOG_LEVEL_FATAL = 0,
- P_LOG_LEVEL_ERROR,
- P_LOG_LEVEL_WARN,
- P_LOG_LEVEL_INFO,
- P_LOG_LEVEL_DEBUG,
- P_LOG_LEVEL_TRACE,
- P_COUNT_LOG_LEVEL
-} P_Log_Level;
-
-#define P_PREFIX_LEN 7
-static const char *p_prefix[P_COUNT_LOG_LEVEL] = {
- [P_LOG_LEVEL_FATAL] = "[FATAL]",
- [P_LOG_LEVEL_ERROR] = "[ERROR]",
- [P_LOG_LEVEL_WARN] = "[WARNI]",
- [P_LOG_LEVEL_INFO] = "[INFOR]",
- [P_LOG_LEVEL_DEBUG] = "[DEBUG]",
- [P_LOG_LEVEL_TRACE] = "[TRACE]",
-};
-
-#define P_MAX_ALLOCS 512
-
-typedef struct P_DebugMemoryInfo {
- void *ptr;
- size_t size;
- const char *file;
- const char *func;
- int line;
-} P_DebugMemoryInfo;
-
-static uint64_t p_alloc_count = 0;
-static P_DebugMemoryInfo p_ptr_array[P_MAX_ALLOCS];
-
-static inline void *
-p_debug_malloc_impl(size_t size, const char *file, int line, const char *func)
-{
- void *ptr = malloc(size);
- printf("[ALLOC] %p (%zu bytes) -> %s:%d %s()\n", ptr, size, file, line, func);
-
- if (ptr) {
- if (p_alloc_count < P_MAX_ALLOCS) {
- p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){
- .ptr = ptr,
- .size = size,
- .file = file,
- .func = func,
- .line = line
- };
- } else {
- fprintf(stderr, "[ERROR] Debug allocator tracking capacity (%d) exceeded!\n", P_MAX_ALLOCS);
- }
- }
- return ptr;
-}
-
-static inline void
-p_debug_free_impl(void *ptr, const char *file, int line, const char *func)
-{
- printf("[FREE] %p -> %s:%d %s()\n", ptr, file, line, func);
-
- if (!ptr) return; // Standard C allows free(NULL)
-
- bool found = false;
- uint64_t index = 0;
-
- /* Find allocation metadata */
- for (index = 0; index < p_alloc_count; ++index) {
- if (p_ptr_array[index].ptr == ptr) {
- found = true;
- break;
- }
- }
-
- if (found) {
- /* Shift array left to remove tracked struct */
- for (uint64_t j = index; j < p_alloc_count - 1; ++j) {
- p_ptr_array[j] = p_ptr_array[j + 1];
- }
- p_alloc_count--;
- } else {
- fprintf(stderr, "[WARNING] Attempted to free untracked/double-freed pointer %p at %s:%d %s()\n",
- ptr, file, line, func);
- }
-
- free(ptr);
-}
-
-static inline void *
-p_debug_realloc_impl(void *ptr, size_t size, const char *file, int line, const char *func)
-{
- if (!ptr) {
- return p_debug_malloc_impl(size, file, line, func);
- }
- if (size == 0) {
- p_debug_free_impl(ptr, file, line, func);
- return NULL;
- }
-
- void *new_ptr = realloc(ptr, size);
- printf("[REALLOC] %p -> %p (%zu bytes) -> %s:%d %s()\n", ptr, new_ptr, size, file, line, func);
-
- if (new_ptr) {
- bool found = false;
- for (uint64_t i = 0; i < p_alloc_count; ++i) {
- if (p_ptr_array[i].ptr == ptr) {
- p_ptr_array[i].ptr = new_ptr;
- p_ptr_array[i].size = size;
- p_ptr_array[i].file = file;
- p_ptr_array[i].line = line;
- p_ptr_array[i].func = func;
- found = true;
- break;
- }
- }
- if (!found) {
- if (p_alloc_count < P_MAX_ALLOCS) {
- p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){
- .ptr = new_ptr,
- .size = size,
- .file = file,
- .func = func,
- .line = line
- };
- }
- }
- }
- return new_ptr;
-}
-
-static void
-p_debug_memory_report(void)
-{
- printf("\n==================== MEMORY REPORT ====================\n");
- printf("Remaining unfreed allocations: %lu\n", (unsigned long)p_alloc_count);
-
- for (uint64_t i = 0; i < p_alloc_count; ++i) {
- P_DebugMemoryInfo *info = &p_ptr_array[i];
- printf("[LEAK] %p (%zu bytes) allocated at %s:%d in %s()\n",
- info->ptr, info->size, info->file, info->line, info->func);
- }
- printf("=======================================================\n");
-}
-/* --- End of p_log.h --- */
-/* --- Start of p_ds.h --- */
-#define DEFAULT_ALIGNMENT (2 * sizeof(void *))
-#define ARRAY_GROW_FACTOR 2
-
-typedef struct {
- u32 size;
- u32 capacity;
-} DynamicArrayHeader;
-
-#define DARRAY_INIT_CAPACITY 64
-#define p_darray_push(array, var) \
- do { \
- if (array == NULL) { \
- DynamicArrayHeader *header = \
- malloc(DARRAY_INIT_CAPACITY * sizeof *array + sizeof *header); \
- header->size = 0; \
- header->capacity = DARRAY_INIT_CAPACITY; \
- array = (void *)(header + 1); \
- } \
- DynamicArrayHeader *header = (DynamicArrayHeader *)(array) - 1; \
- if (header->size >= header->capacity - 1) { \
- unsigned long new_capacity = header->capacity * 2; \
- DynamicArrayHeader *new_header = \
- realloc(header, new_capacity * sizeof *array + sizeof *header); \
- if (!new_header) \
- break; \
- header = new_header; \
- array = (void *)(header + 1); \
- header->capacity = new_capacity; \
- } \
- (array)[header->size++] = var; \
- } while (0)
-
-#define p_darray_len(array) ((array) ? ((DynamicArrayHeader *)(array) - 1)->size : 0)
-#define p_darray_destroy(array) \
-{ \
- free((DynamicArrayHeader *)(array) - 1); \
-}
-
-#define p_darray_shrink(array) \
- do { \
- DynamicArrayHeader *header = (array) ? ((DynamicArrayHeader *)(array) - 1) : 0;\
- if (header) {\
- if (header->capacity > 4) {\
- unsigned long new_capacity = (header->capacity >> 1) + (header->capacity & 1); \
- void *new_header = realloc(header, new_capacity * sizeof *array + sizeof *header); \
- if (!new_header) break; \
- header = new_header; \
- array = (void *)(header + 1); \
- header->capacity = new_capacity; \
- }\
- }\
- } while (0)
-/* --- End of p_ds.h --- */
-
-/*
- * Main Platform Types (Keyboard, Events, Context, etc.)
- */
-
-typedef enum {
- P_KEYMOD_ALT = 0,
- P_KEYMOD_SHIFT,
- P_KEYMOD_CTRL,
- P_KEYMOD_CAPS,
-} P_KeyMod;
-
-typedef enum {
- P_KEY_UNKNOWN = 0,
- P_KEY_UP, P_KEY_DOWN, P_KEY_LEFT, P_KEY_RIGHT,
- P_KEY_SPACE, P_KEY_ESCAPE, P_KEY_ENTER,
- P_KEY_A, P_KEY_B, P_KEY_C, P_KEY_D, P_KEY_E, P_KEY_F, P_KEY_G, P_KEY_H, P_KEY_I,
- P_KEY_J, P_KEY_K, P_KEY_L, P_KEY_M, P_KEY_N, P_KEY_O, P_KEY_P, P_KEY_Q, P_KEY_R,
- P_KEY_S, P_KEY_T, P_KEY_U, P_KEY_V, P_KEY_W, P_KEY_X, P_KEY_Y, P_KEY_Z,
-} P_KeyCode;
-
-/* Platform event type */
-typedef enum {
- P_EVENT_NONE = 0,
- P_EVENT_KEY_DOWN,
- P_EVENT_KEY_UP,
- P_EVENT_WINDOW_CLOSE,
- P_EVENT_WINDOW_RESIZE,
- P_EVENT_POINTER,
- P_EVENT_CONTROLLER_CONNECTED,
- P_EVENT_CONTROLLER_DISCONNECTED,
-} P_EvenType;
-
-typedef enum {
- P_POINTER_MOVED = 0,
- P_POINTER_PRESSED,
- P_POINTER_RELEASED
-} P_PointerState;
-
-typedef struct {
- P_EvenType type;
- union {
- struct {P_KeyCode key; P_KeyMod mod; } key;
- struct { u32 width, height; } resize;
- struct { P_PointerState state; u32 button; u32 x, y; } pointer;
- };
-} P_Event;
-
-/*
- * Platform dependent functions to be implemented by each layer.
- */
-
-POAPI u64 p_get_time(void); // Get time in nanoseconds.
-POAPI void p_sleep_ns(i64 ns); // Sleep for nanoseconds.
-
-POAPI void p_stdout(void *msg, usize bytes); // Print buffer to stdout.
-PODEF void p_log_printf(P_Log_Level level, const char* src, ...); // Print buffer to stdout.
-
-
-POAPI void* p_file_alloc(const char *path, unsigned long *buf_size);
-POAPI bool p_file_exists(const char *path); // Check if file exists.
-POAPI usize p_file_size(const char *path); // Check file size.
-POAPI void p_file_load(const char *path, void *buf_ptr, unsigned long buf_size); // Load file to buffer.
-PODEF void p_file_write(const char *path, void *buf_ptr, unsigned long buf_size); // Write buffer to file.
-
-typedef struct P_Window_Impl P_Window;
-POAPI bool p_window_open(P_Window *win, int width, int height, const char *title); // Opens a window. Returns false on failure.
-POAPI void p_window_close(P_Window *win); // Close window.
-POAPI bool p_window_is_open(P_Window *win); // Check if window is open.
-POAPI void p_window_size(P_Window *win, int *window_width, int *window_height); // Get window size.
-POAPI void p_window_draw(P_Window *win, u32 *pixels, int width, int height); // Draw directly to the window's buffer. Normally much slower than using the GPU.
-POAPI bool p_window_poll_event(P_Window *win, P_Event *ev); // Returns true while there are events to poll. Write event data to a pointer.
-
-POAPI void p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc);
-POAPI void p_audio_quit();
-POAPI void p_audio_write(const i16 *samples, usize count);
-
-/*
- * The Vulkan Module is platform specific
- */
-
-#ifdef P_MODULE_VULKAN
-#include <vulkan/vulkan.h>
-POAPI char** p_vulkan_get_extensions();
-POAPI bool p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface);
-#endif
-
-/*
- * The Remaining Modules are PLATFORM INDEPENDENT!
- */
-
-#ifdef P_MODULE_STRING
-/* --- Start of p_string.h --- */
-typedef struct {
- const char *cstr;
- u32 len;
-} P_StringView;
-
-typedef struct {
- P_StringView sv;
-} P_StringBuilder;
-
-/* Classic C string functions */
-PODEF char* p_find_char(char *buf, char needle);
-PODEF bool p_strcmp(const char *a, const char *b);
-PODEF int p_strlen(const char *a);
-PODEF int p_strtoi(const char *str);
-PODEF char* p_strtok(const char *str, char c);
-
-/* String views */
-PODEF P_StringView p_strview(const char *cstr);
-PODEF void p_strview_chop_left(P_StringView *sv, u32 n);
-PODEF void p_strview_chop_right(P_StringView *sv, u32 n);
-PODEF P_StringView p_strview_chop_delim(P_StringView *sv, char delim);
-PODEF P_StringView p_strview_chop_type(P_StringView *sv, int(*istype)(int));
-PODEF void p_strview_trim_left(P_StringView *sv);
-PODEF void p_strview_trim_right(P_StringView *sv);
-PODEF void p_strview_trim(P_StringView *sv);
-
-/* Classification */
-PODEF int p_is_space(int c);
-PODEF int p_is_digit(int c);
-PODEF int p_is_alpha(int c);
-PODEF int p_is_alnum(int c);
-PODEF int p_is_upper(int c);
-PODEF int p_is_lower(int c);
-PODEF int p_is_hex(int c);
-
-/* Transformation */
-PODEF int p_to_lower(int c);
-PODEF int p_to_upper(int c);
-/* --- End of p_string.h --- */
-#endif
-
-#ifdef P_MODULE_MATH
-/* --- Start of p_math.h --- */
-typedef struct {int x, y; } Vec2i;
-typedef struct {float x, y; } Vec2f;
-typedef struct {float x, y, z; } Vec3f;
-typedef struct { float m[16];} P_Mat4; // column-major: m[col * 4 + row]
-PODEF int p_ceil(float x);
-PODEF float p_cosf(float x);
-PODEF float p_sinf(float x);
-
-PODEF Vec2f p_vec2f(float, float);
-PODEF Vec2f p_vec2f_add(Vec2f, Vec2f);
-PODEF float p_vec2f_cross(Vec2f, Vec2f);
-PODEF float p_vec2f_dot(Vec2f, Vec2f);
-PODEF Vec2f p_vec2f_mult(Vec2f a, float b);
-PODEF Vec2f p_vec2f_sub(Vec2f, Vec2f);
-PODEF Vec2i p_vec2i(float, float);
-PODEF Vec2i p_vec2i_add(Vec2i, Vec2i);
-PODEF float p_vec2i_cross(Vec2i, Vec2i);
-PODEF float p_vec2i_dot(Vec2i, Vec2i);
-PODEF Vec2i p_vec2i_mult(Vec2i a, float b);
-PODEF Vec2i p_vec2i_sub(Vec2i, Vec2i);
-
-PODEF Vec3f p_vec3f(float, float, float);
-
-PODEF P_Mat4 p_mat4_identity(void);
-PODEF P_Mat4 p_mat4_mul(P_Mat4 a, P_Mat4 b);
-PODEF P_Mat4 p_mat4_translate(Vec3f v);
-PODEF P_Mat4 p_mat4_translate_by(P_Mat4 m, Vec3f v);
-PODEF P_Mat4 p_mat4_rotate_x(float rad);
-PODEF P_Mat4 p_mat4_rotate_x_by(P_Mat4 m, float rad);
-PODEF P_Mat4 p_mat4_rotate_y(float rad);
-PODEF P_Mat4 p_mat4_rotate_y_by(P_Mat4 m, float rad);
-PODEF P_Mat4 p_mat4_rotate_z(float rad);
-PODEF P_Mat4 p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z);
-/* --- End of p_math.h --- */
-#endif
-
-#ifdef P_MODULE_ALLOC
-/* --- Start of p_allocators.h --- */
-typedef struct Arena {
- unsigned char *buf;
- usize buf_len;
- usize curr_offset;
- usize prev_offset;
-} Arena;
-
-PODEF void p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length);
-PODEF void* p_arena_alloc_align(Arena *a, usize s, usize align);
-PODEF void* p_arena_alloc(Arena *a, usize s);
-PODEF void* p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align);
-PODEF void* p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size);
-PODEF void p_arena_free_all(Arena *a);
-PODEF void p_arena_destroy(Arena* a);
-
-typedef struct { u8 padding; } Stack_Allocation_Header;
-typedef struct Stack {
- unsigned char *buf;
- usize buf_len;
- usize offset;
-} Stack;
-
-PODEF void p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length);
-PODEF usize calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size);
-PODEF void* p_stack_alloc(Stack *s, usize len, usize alignment);
-PODEF void p_stack_free(Stack *s, void *ptr);
-PODEF void p_stack_free_all(Stack* s);
-PODEF void p_stack_destroy(Stack* s);
-
-typedef struct Pool_Free_Node Pool_Free_Node;
-
-typedef struct Pool {
- unsigned char *buf;
- usize chunk_size;
- usize pool_size;
- Pool_Free_Node *head;
-} Pool;
-
-struct Pool_Free_Node { struct Pool_Free_Node *next; };
-PODEF void p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align);
-PODEF void* p_pool_alloc(Pool *p);
-PODEF void p_pool_free(Pool *p, void *ptr);
-PODEF void p_pool_free_all(Pool *p);
-PODEF void p_pool_destroy(Pool *p);
-/* --- End of p_allocators.h --- */
-#endif
-
-#endif /* _PODIUM_H_ */
-
-#ifdef PODIUM_IMPLEMENTATION
-#undef PODIUM_IMPLEMENTATION
-
-#if defined(PODIUM_SDL)
-/* #include "p_platform_sdl.c" */
-#elif defined(PODIUM_WIN32)
-/* --- Start of p_platform_win32.c --- */
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <mmsystem.h>
-#include <xinput.h> /* gamepad support */
-
-typedef struct {
- HWAVEOUT wave_out;
- WAVEHDR wave_header;
-} P_Win32Ctx;
-
-struct P_Window_Impl {
- HWND hwnd;
- HDC hdc;
- BITMAPINFO bitmap_info;
- int width;
- int height;
- bool closed;
-};
-
-P_Win32Ctx p_ctx = {0};
-
-static inline P_KeyCode
-_win32_translate_vkey(WPARAM vk)
-{
- switch (vk) {
- case VK_LEFT: return P_KEY_LEFT;
- case VK_RIGHT: return P_KEY_RIGHT;
- case VK_UP: return P_KEY_UP;
- case VK_DOWN: return P_KEY_DOWN;
- case VK_SPACE: return P_KEY_SPACE;
- case VK_ESCAPE: return P_KEY_ESCAPE;
- case VK_RETURN: return P_KEY_ENTER;
- case 'A': return P_KEY_A;
- case 'B': return P_KEY_B;
- case 'C': return P_KEY_C;
- case 'D': return P_KEY_D;
- case 'E': return P_KEY_E;
- case 'F': return P_KEY_F;
- case 'G': return P_KEY_G;
- case 'H': return P_KEY_H;
- case 'I': return P_KEY_I;
- case 'J': return P_KEY_J;
- case 'K': return P_KEY_K;
- case 'L': return P_KEY_L;
- case 'M': return P_KEY_M;
- case 'N': return P_KEY_N;
- case 'O': return P_KEY_O;
- case 'P': return P_KEY_P;
- case 'Q': return P_KEY_Q;
- case 'R': return P_KEY_R;
- case 'S': return P_KEY_S;
- case 'T': return P_KEY_T;
- case 'U': return P_KEY_U;
- case 'V': return P_KEY_V;
- case 'W': return P_KEY_W;
- case 'X': return P_KEY_X;
- case 'Y': return P_KEY_Y;
- case 'Z': return P_KEY_Z;
- default: return P_KEY_UNKNOWN;
- }
-}
-
-POAPI u64
-p_get_time(void)
-{
- LARGE_INTEGER count, freq;
- QueryPerformanceCounter(&count);
- QueryPerformanceFrequency(&freq);
- return (u64)((count.QuadPart * 1000000000ULL) / freq.QuadPart);
-}
-
-POAPI void
-p_sleep_ns(i64 ns)
-{
- /* windows sleep is millisecond-based........ */
- Sleep((DWORD)(ns / 1000000));
-}
-
-POAPI void
-p_stdout(void *msg, usize bytes)
-{
- HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
- DWORD bytes_written;
- WriteFile(stdout_handle, msg, bytes, &bytes_written, NULL);
-}
-
-POAPI bool
-p_file_exists(const char *path)
-{
- DWORD attr = GetFileAttributesA(path);
- return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY));
-}
-
-POAPI usize
-p_file_size(const char *path)
-{
- WIN32_FILE_ATTRIBUTE_DATA data;
- if (GetFileAttributesExA(path, GetFileExInfoStandard, &data)) {
- return data.nFileSizeLow;
- }
- return 0;
-}
-
-PODEF void
-p_file_load(const char *path, void *buf_ptr, unsigned long buf_size)
-{
- HANDLE file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
- if (file != INVALID_HANDLE_VALUE) {
- DWORD bytes_read;
- ReadFile(file, buf_ptr, buf_size, &bytes_read, NULL);
- CloseHandle(file);
- }
-}
-
-PODEF void
-p_file_write(const char *path, void *buf_ptr, unsigned long buf_size)
-{
- HANDLE file = CreateFileA(path, GENERIC_WRITE, 0, NULL,
- CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- if (file != INVALID_HANDLE_VALUE) {
- DWORD bytes_written;
- WriteFile(file, buf_ptr, buf_size, &bytes_written, NULL);
- CloseHandle(file);
- }
-}
-
-static LRESULT CALLBACK
-_win32_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
- P_Window *win = (P_Window*)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
- switch (msg) {
- case WM_CLOSE:
- /* Handled in poll_event; mark as closed */
- if (win) win->closed = true;
- return 0;
- }
- return DefWindowProcA(hwnd, msg, wparam, lparam);
-}
-
-POAPI bool
-p_window_open(P_Window *win, int width, int height, const char *title)
-{
- HINSTANCE instance = GetModuleHandleA(NULL);
-
- WNDCLASSA wc = {0};
- wc.lpfnWndProc = _win32_wnd_proc;
- wc.hInstance = instance;
- wc.lpszClassName = "P_Window_Class";
- wc.hCursor = LoadCursor(NULL, IDC_ARROW);
-
- RegisterClassA(&wc);
-
- win->hwnd = CreateWindowExA(0, wc.lpszClassName, title,
- WS_OVERLAPPEDWINDOW | WS_VISIBLE,
- CW_USEDEFAULT, CW_USEDEFAULT, width, height,
- NULL, NULL, instance, NULL);
-
- if (!win->hwnd) return false;
-
- SetWindowLongPtrA(win->hwnd, GWLP_USERDATA, (LONG_PTR)win);
- win->hdc = GetDC(win->hwnd);
- win->width = width;
- win->height = height;
- win->closed = false;
-
- return true;
-}
-
-POAPI void
-p_window_close(P_Window *win)
-{
- if (!win) return;
- if (win->hdc) ReleaseDC(win->hwnd, win->hdc);
- if (win->hwnd) DestroyWindow(win->hwnd);
- win->hdc = NULL;
- win->hwnd = NULL;
-}
-
-POAPI bool
-p_window_is_open(P_Window *win)
-{
- return win && win->hwnd != NULL;
-}
-
-POAPI void
-p_window_size(P_Window *win, int *window_width, int *window_height)
-{
- if (!win) return;
- RECT rect;
- GetClientRect(win->hwnd, &rect);
- *window_width = rect.right - rect.left;
- *window_height = rect.bottom - rect.top;
- win->width = *window_width;
- win->height = *window_height;
-}
-
-POAPI void
-p_window_draw(P_Window *win, u32 *pixels, int width, int height)
-{
- BITMAPINFO bmi = {0};
- bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
- bmi.bmiHeader.biWidth = width;
- bmi.bmiHeader.biHeight = -height; /* top-down */
- bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = 32;
- bmi.bmiHeader.biCompression = BI_RGB;
-
- StretchDIBits(win->hdc,
- 0, 0, win->width, win->height,
- 0, 0, width, height,
- pixels, &bmi, DIB_RGB_COLORS, SRCCOPY);
-}
-
-POAPI bool
-p_window_poll_event(P_Window *win, P_Event *ev)
-{
- if (win->closed) {
- win->closed = false;
- ev->type = P_EVENT_WINDOW_CLOSE;
- return true;
- }
-
- MSG msg;
- while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
- switch (msg.message) {
-
- case WM_QUIT:
- ev->type = P_EVENT_WINDOW_CLOSE;
- return true;
-
- /* Key press */
- case WM_KEYDOWN:
- case WM_SYSKEYDOWN:
- ev->type = P_EVENT_KEY_DOWN;
- ev->key.key = _win32_translate_vkey(msg.wParam);
- return true;
-
- /* Key release */
- case WM_KEYUP:
- case WM_SYSKEYUP:
- ev->type = P_EVENT_KEY_UP;
- ev->key.key = _win32_translate_vkey(msg.wParam);
- return true;
-
- /* Mouse movement */
- case WM_MOUSEMOVE:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_MOVED;
- ev->pointer.button = 0;
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- /* Mouse buttons */
- case WM_LBUTTONDOWN:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_PRESSED;
- ev->pointer.button = 1; /* 1 = LMB, matching X11 convention */
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- case WM_LBUTTONUP:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_RELEASED;
- ev->pointer.button = 1;
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- case WM_RBUTTONDOWN:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_PRESSED;
- ev->pointer.button = 3; /* 3 = RMB */
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- case WM_RBUTTONUP:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_RELEASED;
- ev->pointer.button = 3;
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- case WM_MBUTTONDOWN:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_PRESSED;
- ev->pointer.button = 2; /* 2 = MMB */
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- case WM_MBUTTONUP:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_RELEASED;
- ev->pointer.button = 2;
- ev->pointer.x = (u32)(msg.lParam & 0xFFFF);
- ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF);
- return true;
-
- /* Window resize */
- case WM_SIZE:
- ev->type = P_EVENT_WINDOW_RESIZE;
- ev->resize.width = (u32)(msg.lParam & 0xFFFF);
- ev->resize.height = (u32)((msg.lParam >> 16) & 0xFFFF);
- win->width = ev->resize.width;
- win->height = ev->resize.height;
- return true;
-
- default:
- TranslateMessage(&msg);
- DispatchMessageA(&msg);
- break;
- }
- }
-
- ev->type = P_EVENT_NONE;
- return false;
-}
-
-
-POAPI void
-p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc)
-{
- (void)name; (void)desc; /* WinMM doesn't use a stream name */
-
- WAVEFORMATEX wfx = {0};
- wfx.wFormatTag = WAVE_FORMAT_PCM;
- wfx.nChannels = channels;
- wfx.nSamplesPerSec = sample_rate;
- wfx.wBitsPerSample = 16;
- wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample) / 8;
- wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
-
- if (waveOutOpen(&p_ctx.wave_out, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) {
- exit(1);
- }
-}
-
-POAPI void
-p_audio_quit()
-{
- if (p_ctx.wave_out) {
- waveOutReset(p_ctx.wave_out);
- waveOutClose(p_ctx.wave_out);
- p_ctx.wave_out = NULL;
- }
-}
-
-POAPI void
-p_audio_write(const i16 *samples, usize count)
-{
- if (!p_ctx.wave_out) return;
-
- WAVEHDR header = {0};
- header.lpData = (LPSTR)samples;
- header.dwBufferLength = (DWORD)(count * sizeof(i16));
-
- waveOutPrepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR));
- waveOutWrite(p_ctx.wave_out, &header, sizeof(WAVEHDR));
-
- /* wait for playback to finish (synchronous, mirrors pa_simple_drain) */
- while (!(header.dwFlags & WHDR_DONE)) Sleep(1);
-
- waveOutUnprepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR));
-}
-
-#ifdef P_MODULE_VULKAN
-
-#ifndef VK_USE_PLATFORM_WIN32_KHR
-#define VK_USE_PLATFORM_WIN32_KHR
-#endif
-#include <vulkan/vulkan_win32.h>
-
-POAPI char**
-p_vulkan_get_extensions()
-{
- char **darr = NULL;
- p_darray_push(darr, "VK_KHR_surface");
- p_darray_push(darr, "VK_KHR_win32_surface");
- return darr;
-}
-
-POAPI bool
-p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface)
-{
- if (!instance || !window || !window->hwnd || !out_surface) {
- return false;
- }
-
- VkWin32SurfaceCreateInfoKHR create_info = {0};
- create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
- create_info.pNext = NULL;
- create_info.flags = 0;
- create_info.hwnd = window->hwnd;
- create_info.hinstance = GetModuleHandleA(NULL);
-
- VkResult result = vkCreateWin32SurfaceKHR(instance, &create_info, allocator, out_surface);
- return (result == VK_SUCCESS);
-}
-#endif
-/* --- End of p_platform_win32.c --- */
-#elif defined(PODIUM_LINUX)
-/* --- Start of p_platform_linux.c --- */
-/* linux */
-#include <time.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <errno.h>
-/* x11 */
-#include <X11/Xlib.h>
-#include <X11/keysym.h>
-/* pulse audio */
-#include <pulse/pulseaudio.h>
-#include <pulse/simple.h>
-#include <pulse/error.h>
-/* joystick */
-#include <linux/joystick.h>
-
-typedef struct {
- pa_simple *audio_stream;
-} P_LinuxCtx;
-
-struct P_Window_Impl {
- Display *display;
- XImage *image;
- Window window;
- GC gc;
- int screen;
- int width;
- int height;
-};
-
-P_LinuxCtx p_ctx = {0};
-
-static inline P_KeyCode
-_x11_translate_keycode(XEvent xev)
-{
- static KeySym ks;
- ks = XLookupKeysym(&xev.xkey, 0);
- switch (ks) {
- /* Arrow keys */
- case XK_Left: return P_KEY_LEFT;
- case XK_Right: return P_KEY_RIGHT;
- case XK_Up: return P_KEY_UP;
- case XK_Down: return P_KEY_DOWN;
- /* Common keys */
- case XK_space: return P_KEY_SPACE;
- case XK_Escape: return P_KEY_ESCAPE;
- case XK_Return: return P_KEY_ENTER;
- case XK_A: case XK_a: return P_KEY_A;
- case XK_B: case XK_b: return P_KEY_B;
- case XK_C: case XK_c: return P_KEY_C;
- case XK_D: case XK_d: return P_KEY_D;
- case XK_E: case XK_e: return P_KEY_E;
- case XK_F: case XK_f: return P_KEY_F;
- case XK_G: case XK_g: return P_KEY_G;
- case XK_H: case XK_h: return P_KEY_H;
- case XK_I: case XK_i: return P_KEY_I;
- case XK_J: case XK_j: return P_KEY_J;
- case XK_K: case XK_k: return P_KEY_K;
- case XK_L: case XK_l: return P_KEY_L;
- case XK_M: case XK_m: return P_KEY_M;
- case XK_N: case XK_n: return P_KEY_N;
- case XK_O: case XK_o: return P_KEY_O;
- case XK_P: case XK_p: return P_KEY_P;
- case XK_Q: case XK_q: return P_KEY_Q;
- case XK_R: case XK_r: return P_KEY_R;
- case XK_S: case XK_s: return P_KEY_S;
- case XK_T: case XK_t: return P_KEY_T;
- case XK_U: case XK_u: return P_KEY_U;
- case XK_V: case XK_v: return P_KEY_V;
- case XK_W: case XK_w: return P_KEY_W;
- case XK_X: case XK_x: return P_KEY_X;
- case XK_Y: case XK_y: return P_KEY_Y;
- case XK_Z: case XK_z: return P_KEY_Z;
- default: return P_KEY_UNKNOWN;
- }
-}
-
-POAPI u64
-p_get_time(void)
-{
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return NANOS_PER_SEC * ts.tv_sec + ts.tv_nsec;
-}
-
-POAPI void
-p_sleep_ns(i64 ns)
-{
- struct timespec ts;
- ts.tv_sec = ns / 1000000000L;
- ts.tv_nsec = ns % 1000000000L;
- clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
-}
-
-POAPI void
-p_stdout(void *msg, usize bytes)
-{
- write(1, msg, bytes);
-}
-
-POAPI bool
-p_file_exists(const char *path)
-{
- return (access(path, F_OK) == 0);
-}
-
-POAPI usize
-p_file_size(const char *path)
-{
- struct stat st;
- if (stat(path, &st) == 0) {
- return st.st_size;
- }
- return 0;
-}
-
-PODEF void
-p_file_load(const char *path, void *buf_ptr, unsigned long buf_size)
-{
- int fd = open(path, O_RDONLY);
- if (fd >= 0) {
- read(fd, buf_ptr, (size_t)buf_size);
- close(fd);
- }
-}
-
-PODEF void
-p_file_write(const char *path, void *buf_ptr, unsigned long buf_size)
-{
- int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0644);
- if (fd >= 0) {
- write(fd, buf_ptr, (size_t)buf_size);
- close(fd);
- }
-}
-
-POAPI bool
-p_window_open(P_Window *win, int width, int height, const char *title)
-{
- win->width = width;
- win->height = height;
-
- XInitThreads();
-
- win->display = XOpenDisplay(NULL);
- if (win->display == NULL) {
- PERROR("Cannot open display!");
- return false;
- }
-
- int screen = DefaultScreen(win->display);
- win->window = XCreateSimpleWindow(
- win->display,
- RootWindow(win->display, screen),
- 0, 0, width, height,
- 1,
- BlackPixel(win->display, screen),
- BlackPixel(win->display, screen)
- );
-
- win->screen = screen;
-
- XStoreName(win->display, win->window, title);
- XSelectInput(win->display, win->window,
- KeyPressMask | KeyReleaseMask |
- ButtonPressMask | ButtonReleaseMask |
- PointerMotionMask | StructureNotifyMask);
- XMapWindow(win->display, win->window);
-
- win->gc = DefaultGC(win->display, screen);
-
- Atom wm_delete = XInternAtom(win->display, "WM_DELETE_WINDOW", False);
- XSetWMProtocols(win->display, win->window, &wm_delete, 1);
-
- return true;
-}
-
-POAPI void
-p_window_close(P_Window *win)
-{
- if (!win) return;
-
- if (win->image) {
- // XDestroyImage(win->image);
- win->image = NULL;
- }
-
- if (win->display) {
- Display *dpy = win->display;
- Window w = win->window;
- win->display = NULL;
- win->window = 0;
-
- if (w) {
- XDestroyWindow(dpy, w);
- }
- XSync(dpy, False);
- }
-}
-
-POAPI bool
-p_window_is_open(P_Window *win)
-{
- return win && win->display != NULL;
-}
-
-POAPI void
-p_window_size(P_Window *win, int *window_width, int *window_height)
-{
- if (!win) return;
-
- XWindowAttributes attr;
- XGetWindowAttributes(win->display, win->window, &attr);
- *window_width = attr.width;
- *window_height = attr.height;
-
- win->width = attr.width;
- win->height = attr.height;
-}
-
-POAPI void
-p_window_draw(P_Window *win, u32 *pixels, int width, int height)
-{
- int screen = win->screen;
-
- win->image = XCreateImage(
- win->display,
- DefaultVisual(win->display, screen),
- DefaultDepth(win->display, screen),
- ZPixmap,
- 0,
- (char*)pixels,
- width,
- height,
- 32,
- width * 4
- );
-
- int window_width, window_height;
- p_window_size(win, &window_width, &window_height);
-
- XPutImage(
- win->display,
- win->window,
- win->gc,
- win->image,
- 0, 0,
- 0, 0,
- win->width,
- win->height);
-
- XFlush(win->display);
-}
-
-POAPI bool
-p_window_poll_event(P_Window *win, P_Event *ev)
-{
- if (XPending(win->display)) {
- static XEvent xev;
- XNextEvent(win->display, &xev);
-
- switch (xev.type) {
-
- case KeyPress:
- ev->type = P_EVENT_KEY_DOWN;
- ev->key.key = _x11_translate_keycode(xev);
- return true;
-
- case KeyRelease:
- ev->type = P_EVENT_KEY_UP;
- ev->key.key = _x11_translate_keycode(xev);
- return true;
-
- case ConfigureNotify:
- ev->type = P_EVENT_WINDOW_RESIZE;
- ev->resize.width = xev.xconfigure.width;
- ev->resize.height = xev.xconfigure.height;
- return true;
-
- case ClientMessage:
- if ((Atom)xev.xclient.data.l[0] ==
- XInternAtom(win->display, "WM_DELETE_WINDOW", False)) {
- ev->type = P_EVENT_WINDOW_CLOSE;
- return true;
- }
- break;
-
- case MotionNotify:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_MOVED;
- ev->pointer.button = 0;
- ev->pointer.x = xev.xmotion.x;
- ev->pointer.y = xev.xmotion.y;
- return true;
-
- case ButtonPress:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_PRESSED;
- ev->pointer.button = xev.xbutton.button; /* 1=LMB, 2=MMB, 3=RMB */
- ev->pointer.x = xev.xbutton.x;
- ev->pointer.y = xev.xbutton.y;
- return true;
-
- case ButtonRelease:
- ev->type = P_EVENT_POINTER;
- ev->pointer.state = P_POINTER_RELEASED;
- ev->pointer.button = xev.xbutton.button;
- ev->pointer.x = xev.xbutton.x;
- ev->pointer.y = xev.xbutton.y;
- return true;
- }
- }
-
- ev->type = P_EVENT_NONE;
- return false;
-}
-
-POAPI void
-p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc)
-{
- pa_sample_spec ss = {
- .format = PA_SAMPLE_S16LE,
- .rate = sample_rate,
- .channels = channels,
- };
-
- int error;
- p_ctx.audio_stream = pa_simple_new(
- NULL,
- name,
- PA_STREAM_PLAYBACK,
- NULL,
- desc,
- &ss,
- NULL,
- NULL,
- &error
- );
-
- if (!p_ctx.audio_stream) {
- PERROR("PulseAudio init failed: %s\n", pa_strerror(error));
- exit(1);
- }
-}
-
-POAPI void
-p_audio_quit()
-{
- if (p_ctx.audio_stream) {
- pa_simple_free(p_ctx.audio_stream);
- p_ctx.audio_stream = NULL;
- }
-}
-
-POAPI void
-p_audio_write(const i16 *samples, usize count)
-{
- if (!p_ctx.audio_stream) return;
-
- int error;
- if (pa_simple_write(p_ctx.audio_stream, samples, count * sizeof(i16), &error) < 0) {
- PERROR("PulseAudio write failed: %s\n", pa_strerror(error));
- }
-
- pa_simple_drain(p_ctx.audio_stream, &error);
-}
-
-#ifdef P_MODULE_VULKAN
-
-#ifndef VK_USE_PLATFORM_XLIB_KHR
-#define VK_USE_PLATFORM_XLIB_KHR
-#endif
-#include <vulkan/vulkan_xlib.h>
-
-POAPI char**
-p_vulkan_get_extensions()
-{
- char **darr = NULL;
- p_darray_push(darr, "VK_KHR_surface");
- p_darray_push(darr, "VK_KHR_xcb_surface");
- p_darray_push(darr, "VK_KHR_xlib_surface");
- return darr;
-}
-
-POAPI bool
-p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface)
-{
- if (!instance || !window || !window->display || !window->window || !out_surface) {
- return false;
- }
-
- VkXlibSurfaceCreateInfoKHR create_info = {0};
- create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
- create_info.pNext = NULL;
- create_info.flags = 0;
- create_info.dpy = window->display;
- create_info.window = window->window;
-
- VkResult result = vkCreateXlibSurfaceKHR(instance, &create_info, allocator, out_surface);
-
- return (result == VK_SUCCESS);
-}
-#endif
-/* --- End of p_platform_linux.c --- */
-#endif /* linux */
-
-
-POAPI void* p_file_alloc(const char *path, unsigned long *buf_size) {
- if (p_file_exists(path) == false) {
- return NULL;
- }
-
- usize file_size = p_file_size(path);
- *buf_size = file_size;
-
- void *ptr = malloc(file_size);
- if (ptr) {
- p_file_load(path, ptr, file_size); // Load file to buffer.
- }
- return ptr;
-}
-
-
-/*
- * Import Module Code
- */
-
-#ifdef P_MODULE_STRING
-/* --- Start of p_string.c --- */
-PODEF char*
-p_find_char(char *buf, char needle)
-{
- while (buf && *buf != needle) buf++;
- return buf;
-}
-
-PODEF bool
-p_strcmp(const char *a, const char *b)
-{
- if (p_strlen(a) != p_strlen(b))
- return false;
-
- while (a && b && *a != '\0') {
- if (*(a++) != *(b++)) return false;
- }
-
- return true;
-}
-
-PODEF int
-p_strlen(const char *a)
-{
- int i = 0;
- while (*a++ != '\0') i++;
- return i;
-}
-
-PODEF int
-p_strtoi(const char *str)
-{
- int res = 0;
-
- while (*str) {
- if (*str >= '0' && *str <= '9') {
- res = res * 10 + (*str - '0');
- } else {
- return res;
- }
- str++;
- }
-
- return res;
-}
-
-PODEF char*
-p_strtok(const char *str, char c)
-{
- while (*str != c && *str++ != '\0');
- return (*str == c) ? (char*) ++str : 0;
-}
-
-PODEF P_StringView
-p_strview(const char *cstr)
-{
- P_StringView sv = {0};
- sv.cstr = cstr;
- sv.len = p_strlen(cstr);
- return sv;
-}
-
-PODEF void
-p_strview_chop_left(P_StringView *sv, u32 n)
-{
- if (n > sv->len) n = sv->len;
- sv->cstr += n;
- sv->len -= n;
-}
-
-PODEF void
-p_strview_chop_right(P_StringView *sv, u32 n)
-{
- if (n > sv->len) n = sv->len;
- sv->len -= n;
-}
-
-PODEF P_StringView
-p_strview_chop_delim(P_StringView *sv, char delim)
-{
- u32 i = 0;
- while (i < sv->len && sv->cstr[i] != delim) {
- i++;
- }
-
- P_StringView tok;
- if (i < sv->len) {
- tok.cstr = sv->cstr;
- tok.len = i;
- p_strview_chop_left(sv, i + 1);
- return tok;
- }
-
- tok = *sv;
- p_strview_chop_left(sv, sv->len);
- return tok;
-}
-
-PODEF P_StringView
-p_strview_chop_type(P_StringView *sv, int(*istype)(int))
-{
- u32 i = 0;
- while (i < sv->len && istype(sv->cstr[i])) {
- i++;
- }
-
- P_StringView tok;
- if (i < sv->len) {
- tok.cstr = sv->cstr;
- tok.len = i;
- p_strview_chop_left(sv, i + 1);
- return tok;
- }
-
- tok = *sv;
- p_strview_chop_left(sv, sv->len);
- return tok;
-}
-
-
-PODEF void
-p_strview_trim_left(P_StringView *sv)
-{
- while (sv->len > 0 && p_is_space(sv->cstr[0])) {
- p_strview_chop_left(sv, 1);
- }
-}
-
-PODEF void
-p_strview_trim_right(P_StringView *sv)
-{
- while (sv->len > 0 && p_is_space(sv->cstr[sv->len-1])) {
- p_strview_chop_right(sv, 1);
- }
-}
-
-PODEF void
-p_strview_trim(P_StringView *sv)
-{
- p_strview_trim_left(sv);
- p_strview_trim_right(sv);
-}
-
-PODEF int
-p_is_space(int c)
-{
- /* Checks for: space, form feed (\f), line feed (\n),
- * carriage return (\r), horizontal tab (\t), vertical tab (\v) */
- return (c == ' ' || (c >= '\t' && c <= '\r'));
-}
-
-PODEF int
-p_is_digit(int c)
-{
- return (c >= '0' && c <= '9');
-}
-
-PODEF int
-p_is_alpha(int c)
-{
- return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
-}
-
-PODEF int
-p_is_alnum(int c)
-{
- return (p_is_alpha(c) || p_is_digit(c));
-}
-
-PODEF int
-p_is_hex(int c)
-{
- return (p_is_digit(c) ||
- (c >= 'a' && c <= 'f') ||
- (c >= 'A' && c <= 'F'));
-}
-
-PODEF int
-p_is_upper(int c)
-{
- return (c >= 'A' && c <= 'Z');
-}
-
-PODEF int
-p_is_lower(int c)
-{
- return (c >= 'a' && c <= 'z');
-}
-
-PODEF int
-p_to_lower(int c)
-{
- if (p_is_upper(c)) return (c + ('a' - 'A'));
- return c;
-}
-
-PODEF int
-p_to_upper(int c)
-{
- if (p_is_lower(c)) return (c - ('a' - 'A'));
- return c;
-}
-/* --- End of p_string.c --- */
-#endif
-
-#ifdef P_MODULE_MATH
-/* --- Start of p_math.c --- */
-PODEF
-int p_ceil(float x)
-{
- int base = (int) x;
- return (x > base) ? base + 1 : base;
-}
-
-PODEF float
-p_cosf(float x)
-{
- x += 1.57079632679f;
-
- while (x > P_PI) x -= P_PI2;
- while (x < -P_PI) x += P_PI2;
-
- const float B = 4.0f / P_PI;
- const float C = -4.0f / P_PI_POW2;
-
- float y = B * x + C * x * (x < 0 ? -x : x);
-
- const float P = 0.225f;
- y = P * (y * (y < 0 ? -y : y) - y) + y;
-
- return y;
-}
-
-PODEF float
-p_sinf(float x)
-{
- while (x > P_PI) x -= P_PI2;
- while (x < -P_PI) x += P_PI2;
-
- const float B = 4.0f / P_PI;
- const float C = -4.0f / (P_PI_POW2);
-
- float y = B * x + C * x * (x < 0 ? -x : x);
-
- const float P = 0.225f;
- y = P * (y * (y < 0 ? -y : y) - y) + y;
-
- return y;
-}
-
-PODEF Vec2f
-p_vec2f(float x, float y)
-{
- return (Vec2f) { .x = x, .y = y };
-}
-
-PODEF Vec2f
-p_vec2f_add(Vec2f a, Vec2f b)
-{
- return (Vec2f) {
- .x = a.x + b.x,
- .y = a.y + b.y,
- };
-}
-
-PODEF float
-p_vec2f_cross(Vec2f a, Vec2f b)
-{
- // axby − aybx
- return a.x * b.y - a.y * b.x;
-}
-
-PODEF float
-p_vec2f_dot(Vec2f a, Vec2f b)
-{
- // a · b = ax × bx + ay × by
- return a.x * b.x + a.y * b.y;
-}
-
-PODEF Vec2f
-p_vec2f_mult(Vec2f a, float b)
-{
- return (Vec2f) {
- .x = a.x * b,
- .y = a.y * b,
- };
-}
-
-PODEF Vec2f
-p_vec2f_sub(Vec2f a, Vec2f b)
-{
- return (Vec2f) {
- .x = a.x - b.x,
- .y = a.y - b.y,
- };
-}
-
-PODEF Vec2i
-p_vec2i(float x, float y)
-{
- return (Vec2i) { .x = x, .y = y };
-}
-
-PODEF Vec2i
-p_vec2i_add(Vec2i a, Vec2i b)
-{
- return (Vec2i) {
- .x = a.x + b.x,
- .y = a.y + b.y,
- };
-}
-
-PODEF float
-p_vec2i_cross(Vec2i a, Vec2i b)
-{
- // axby − aybx
- return a.x * b.y - a.y * b.x;
-}
-
-PODEF float
-p_vec2i_dot(Vec2i a, Vec2i b)
-{
- // a · b = ax × bx + ay × by
- return a.x * b.x + a.y * b.y;
-}
-
-PODEF Vec2i
-p_vec2i_mult(Vec2i a, float b)
-{
- return (Vec2i) {
- .x = a.x * b,
- .y = a.y * b,
- };
-}
-
-PODEF Vec2i
-p_vec2i_sub(Vec2i a, Vec2i b)
-{
- return (Vec2i) {
- .x = a.x - b.x,
- .y = a.y - b.y,
- };
-}
-
-PODEF Vec3f
-p_vec3f(float x, float y, float z)
-{
- return (Vec3f) { .x = x, .y = y, .z = z };
-}
-
-PODEF P_Mat4
-p_mat4_identity(void)
-{
- P_Mat4 res = {0};
- res.m[0] = 1.0f;
- res.m[5] = 1.0f;
- res.m[10] = 1.0f;
- res.m[15] = 1.0f;
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_mul(P_Mat4 a, P_Mat4 b)
-{
- P_Mat4 res = {0};
- for (int col = 0; col < 4; ++col) {
- for (int row = 0; row < 4; ++row) {
- float sum = 0.0f;
- for (int k = 0; k < 4; ++k) {
- sum += a.m[k * 4 + row] * b.m[col * 4 + k];
- }
- res.m[col * 4 + row] = sum;
- }
- }
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_translate(Vec3f v)
-{
- P_Mat4 res = p_mat4_identity();
- res.m[12] = v.x;
- res.m[13] = v.y;
- res.m[14] = v.z;
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_translate_by(P_Mat4 m, Vec3f v)
-{
- P_Mat4 res = m;
- res.m[12] = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12];
- res.m[13] = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13];
- res.m[14] = m.m[2] * v.x + m.m[6] * v.y + m.m[10] * v.z + m.m[14];
- res.m[15] = m.m[3] * v.x + m.m[7] * v.y + m.m[11] * v.z + m.m[15];
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_rotate_x(float rad)
-{
- P_Mat4 res = p_mat4_identity();
- float c = p_cosf(rad);
- float s = p_sinf(rad);
- res.m[5] = c;
- res.m[6] = s;
- res.m[9] = -s;
- res.m[10] = c;
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_rotate_x_by(P_Mat4 m, float rad)
-{
- return p_mat4_mul(m, p_mat4_rotate_x(rad));
-}
-
-PODEF P_Mat4
-p_mat4_rotate_y(float rad)
-{
- P_Mat4 res = p_mat4_identity();
- float c = p_cosf(rad);
- float s = p_sinf(rad);
- res.m[0] = c;
- res.m[2] = -s;
- res.m[8] = s;
- res.m[10] = c;
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_rotate_y_by(P_Mat4 m, float rad)
-{
- return p_mat4_mul(m, p_mat4_rotate_y(rad));
-}
-
-PODEF P_Mat4
-p_mat4_rotate_z(float rad)
-{
- P_Mat4 res = p_mat4_identity();
- float c = p_cosf(rad);
- float s = p_sinf(rad);
- res.m[0] = c;
- res.m[1] = s;
- res.m[4] = -s;
- res.m[5] = c;
- return res;
-}
-
-PODEF P_Mat4
-p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z)
-{
- P_Mat4 res = {0};
- float tan_half_fov = p_sinf(fov_rad * 0.5f) / p_cosf(fov_rad * 0.5f);
-
- res.m[0] = 1.0f / (aspect * tan_half_fov);
- res.m[5] = 1.0f / tan_half_fov;
- res.m[10] = far_z / (near_z - far_z);
- res.m[11] = -1.0f;
- res.m[14] = (near_z * far_z) / (near_z - far_z);
- return res;
-}
-/* --- End of p_math.c --- */
-#endif
-
-#ifdef P_MODULE_ALLOC
-/* --- Start of p_allocators.c --- */
-PODEF bool
-is_power_of_two(uintptr x) {
- return (x & (x-1)) == 0;
-}
-
-PODEF uintptr
-align_forward(uintptr ptr, usize align)
-{
- uintptr p, a, remainder;
- assert(is_power_of_two(align));
-
- p = ptr;
- a = (uintptr)align;
- remainder = p & (a-1);
-
- return (remainder != 0) ? p + a - remainder : p;
-}
-
-PODEF void
-p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length)
-{
- a->buf = (unsigned char *)backing_buffer;
- a->buf_len = backing_buffer_length;
- a->curr_offset = 0;
- a->prev_offset = 0;
-}
-
-PODEF void*
-p_arena_alloc_align(Arena *a, usize s, usize align)
-{
-
- // Align 'curr_offset' forward to the specified alignment
- uintptr curr_ptr = (uintptr)a->buf + (uintptr)a->curr_offset;
- uintptr offset = align_forward(curr_ptr, align);
- offset -= (uintptr)a->buf; // Change to relative offset
-
- // Check to see if the backing memory has space left
- if (offset+s <= a->buf_len) {
- void *ptr = &a->buf[offset];
- a->prev_offset = offset;
- a->curr_offset = offset+s;
-
- memset(ptr, 0, s);
- return ptr;
- }
-
- return NULL;
-}
-
-PODEF void*
-p_arena_alloc(Arena *a, usize s) {
- return p_arena_alloc_align(a, s, DEFAULT_ALIGNMENT);
-}
-
-PODEF void*
-p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align)
-{
- assert(is_power_of_two(align));
- unsigned char *old_mem = (unsigned char *)old_memory;
-
- if (old_mem == NULL || old_size == 0) {
- return p_arena_alloc_align(a, new_size, align);
- } else if (a->buf <= old_mem && old_mem < a->buf+a->buf_len) {
- if (a->buf+a->prev_offset == old_mem) {
- a->curr_offset = a->prev_offset + new_size;
- if (new_size > old_size) {
- // Zero the new memory by default
- memset(&a->buf[a->curr_offset], 0, new_size-old_size);
- }
- return old_memory;
- } else {
- void *new_memory = p_arena_alloc_align(a, new_size, align);
- size_t copy_size = old_size < new_size ? old_size : new_size;
- memmove(new_memory, old_memory, copy_size);
- return new_memory;
- }
-
- } else {
- assert(0 && "Memory is out of bounds of the buffer in this arena");
- return NULL;
- }
-}
-
-PODEF void*
-p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size)
-{
- return p_arena_resize_align(a, old_memory, old_size, new_size, DEFAULT_ALIGNMENT);
-}
-
-PODEF void
-p_arena_free_all(Arena *a)
-{
- a->curr_offset = 0;
- a->prev_offset = 0;
-}
-
-PODEF void
-p_arena_destroy(Arena* a)
-{
- a->buf_len = 0;
- a->curr_offset = 0;
- a->prev_offset = 0;
-}
-
-PODEF void
-p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length)
-{
- s->buf = (unsigned char*) backing_buffer;
- s->buf_len = backing_buffer_length;
- s->offset = 0;
-}
-
-PODEF usize
-calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size)
-{
- uintptr p, a, modulo, padding, needed_space;
-
- assert(is_power_of_two(alignment));
-
- p = ptr;
- a = alignment;
- modulo = p & (a-1); // (p % a) as it assumes alignment is a power of two
-
- padding = 0;
- needed_space = 0;
-
- if (modulo != 0) { // Same logic as 'align_forward'
- padding = a - modulo;
- }
-
- needed_space = (uintptr)header_size;
-
- if (padding < needed_space) {
- needed_space -= padding;
-
- if ((needed_space & (a-1)) != 0) {
- padding += a * (1+(needed_space/a));
- } else {
- padding += a * (needed_space/a);
- }
- }
-
- return (usize)padding;
-}
-
-PODEF void*
-p_stack_alloc(Stack *s, usize len, usize alignment)
-{
- uintptr curr_addr, next_addr;
- usize padding;
- Stack_Allocation_Header *header;
-
- assert(is_power_of_two(alignment));
-
- if (alignment > 128) {
- // As the padding is 8 bits (1 byte), the largest alignment that can
- // be used is 128 bytes
- alignment = 128;
- }
-
- curr_addr = (uintptr)s->buf + (uintptr)s->offset;
- padding = calc_padding_with_header(curr_addr, (uintptr)alignment, sizeof(Stack_Allocation_Header));
- if (s->offset + padding + len > s->buf_len) {
- // Stack allocator is out of memory
- return NULL;
- }
- s->offset += padding;
-
- next_addr = curr_addr + (uintptr)padding;
- header = (Stack_Allocation_Header *)(next_addr - sizeof(Stack_Allocation_Header));
- header->padding = (u8)padding;
-
- s->offset += len;
-
- return memset((void *)next_addr, 0, len);
-}
-
-PODEF void
-p_stack_free(Stack *s, void *ptr)
-{
- if (ptr != NULL) {
- uintptr start, end, curr_addr;
- Stack_Allocation_Header *header;
- usize prev_offset;
-
- start = (uintptr)s->buf;
- end = start + (uintptr)s->buf_len;
- curr_addr = (uintptr)ptr;
-
- if (!(start <= curr_addr && curr_addr < end)) {
- assert(0 && "Out of bounds memory address passed to stack allocator (free)");
- return;
- }
-
- if (curr_addr >= start+(uintptr)s->offset) {
- // Allow double frees
- return;
- }
-
- header = (Stack_Allocation_Header *)(curr_addr - sizeof(Stack_Allocation_Header));
- prev_offset = (usize)(curr_addr - (uintptr)header->padding - start);
-
- s->offset = prev_offset;
- }
-}
-
-PODEF void
-p_stack_free_all(Stack* s)
-{
- s->offset = 0;
-}
-
-PODEF void
-p_stack_destroy(Stack* s)
-{
- s->buf = 0;
- s->buf_len = 0;
- s->offset = 0;
-}
-
-PODEF void
-p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align)
-{
- /* align start */
- uintptr start = (uintptr) buf;
- uintptr aligned = align_forward(start, chunk_align);
- buf_len -= (aligned - start);
-
- /* align chunk usize */
- chunk_size = (usize) align_forward(chunk_size, chunk_align);
-
- assert(chunk_size >= sizeof(Pool_Free_Node) && "Chunk usize is too small");
- assert(buf_len >= chunk_size && "Backing buffer length is smaller than the chunk size");
-
- p->buf = (unsigned char *) buf;
- p->pool_size = buf_len;
- p->chunk_size = chunk_size;
- p->head = NULL;
-
- p_pool_free_all(p);
-}
-
-PODEF void*
-p_pool_alloc(Pool *p)
-{
- Pool_Free_Node *node = p->head;
-
- assert(node != NULL && "Pool allocator has no free memory");
-
- p->head = p->head->next;
-
- return memset(node, 0 , p->chunk_size);
-}
-
-PODEF void
-p_pool_free(Pool *p, void *ptr)
-{
- Pool_Free_Node *node;
-
- void *start = p->buf;
- void *end = &p->buf[p->pool_size];
-
- if (ptr == NULL) {
- return;
- }
-
- if (!(start <= ptr && ptr < end)) {
- assert(0 && "Memory is out of bounds of the buffer in this pool");
- return;
- }
-
- node = (Pool_Free_Node *)ptr;
- node->next = p->head;
- p->head = node;
-}
-
-PODEF void
-p_pool_free_all(Pool *p)
-{
- usize chunk_count = p->pool_size / p->chunk_size;
- usize i;
-
- for (i = 0; i < chunk_count; i++) {
- void *ptr = &p->buf[i * p->chunk_size];
- Pool_Free_Node *node = (Pool_Free_Node *)ptr;
-
- // Push free node onto thte free list
- node->next = p->head;
- p->head = node;
- }
-}
-
-PODEF void
-p_pool_destroy(Pool *p)
-{
- p->buf = NULL;
- p->pool_size = 0;
- p->chunk_size = 0;
- p->head = NULL;
-}
-
-/* --- End of p_allocators.c --- */
-#endif
-
-#define P_MAX_PRINTF_SIZE 1024
-PODEF void
-p_log_printf(P_Log_Level level, const char* src, ...)
-{
- static char out_message[P_MAX_PRINTF_SIZE];
- static u32 offset = P_PREFIX_LEN + 1;
-
- memcpy(out_message, p_prefix[level], P_PREFIX_LEN); /* prefixes have a fixed len */
- out_message[P_PREFIX_LEN] = ' '; /* write space */
-
- __builtin_va_list arg_ptr;
- va_start(arg_ptr, src);
- int len = vsnprintf(out_message+offset, P_MAX_PRINTF_SIZE-offset, src, arg_ptr); // append message
- va_end(arg_ptr);
-
- if (len < 0) len = 0;
- usize total_len = offset + len;
- if (total_len > P_MAX_PRINTF_SIZE) {
- total_len = P_MAX_PRINTF_SIZE;
- }
-
- p_stdout(out_message, total_len);
- p_stdout("\n", 1);
-}
-
-#endif // PODIUM_IMPLEMENTATION
diff --git a/archive/Podium/podium_header.h b/archive/Podium/podium_header.h deleted file mode 100644 index 6407f62..0000000 --- a/archive/Podium/podium_header.h +++ /dev/null @@ -1,258 +0,0 @@ -/* =========================================================================== - * PODIUM - Kitchen Sink - Copyright (c) 2026 Vasco Alves - * --------------------------------------------------------------------------- - * To stand on the shoulders of giants... Giants must stand still!" - * --------------------------------------------------------------------------- - * PREFIX: P_ (types) or p_ (functions & variables) - * --------------------------------------------------------------------------- - * COMPILE TIME FLAGS - * ._________________________________________________________________________, - * | Name | Description | - * |-----------------|-------------------------------------------------------| - * | PODIUM_WIN32 | Used to explicitly compile for Windows | - * | PODIUM_LINUX | Used to explicitly compile for Linux and FreeBSD. | - * '-------------------------------------------------------------------------' - * MODULES - * ._________________________________________________________________________, - * | Name | Description | - * |-----------------|-------------------------------------------------------| - * | P_MODULE_VULKAN | Gives access to vulkan related utilities. | - * | P_MODULE_STRING | Include string manipulation utilities. | - * | P_MODULE_MATH | Trigonometry and linear algebra functions. | - * | P_MODULE_ALLOC | Memory allocators: Arena, Stack & Pool. | - * '-------------------------------------------------------------------------' - * =========================================================================== */ - -#ifndef _PODIUM_H_ -#define _PODIUM_H_ - -#define PODIUM_MAJOR "0" // needs more revisions and use to reach 1.0 status -#define PODIUM_MINOR "2" // vulkan module -#define PODIUM_PATCH "2" // gamepad - -#define POAPI static inline -#define PODEF static inline - -/* - * Auto-detect platforms. - */ - -/* Windows */ -#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) - #ifndef PODIUM_WIN32 - #define PODIUM_WIN32 - #endif - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif -/* Apple */ -#elif defined(__APPLE__) || defined(__MACH__) - #include <TargetConditionals.h> - #ifndef PODIUM_APPLE - #define PODIUM_APPLE - #endif - #if TARGET_OS_IPHONE - #define PODIUM_IOS - #else - #define PODIUM_MACOS - #endif -/* Linux / BSD */ -#elif defined(__linux__) || defined(__FreeBSD__) - #ifndef PODIUM_LINUX - #define PODIUM_LINUX - #endif - #ifndef _POSIX_C_SOURCE - #define _POSIX_C_SOURCE 200112L - #endif -#endif - -#include <stdarg.h> -#include <stdint.h> -#include <stddef.h> -#include <string.h> /* memcpy */ -#include <stdio.h> /* vsnprintf */ - -/* - * NOTE: The <REPLACE> comments are used by the build - * script to know what includes to inline into the - * final single header file! - */ - -#include "p_types.h" // <REPLACE> -#include "p_log.h" // <REPLACE> -#include "p_ds.h" // <REPLACE> - -/* - * Main Platform Types (Keyboard, Events, Context, etc.) - */ - -typedef enum { - P_KEYMOD_ALT = 0, - P_KEYMOD_SHIFT, - P_KEYMOD_CTRL, - P_KEYMOD_CAPS, -} P_KeyMod; - -typedef enum { - P_KEY_UNKNOWN = 0, - P_KEY_UP, P_KEY_DOWN, P_KEY_LEFT, P_KEY_RIGHT, - P_KEY_SPACE, P_KEY_ESCAPE, P_KEY_ENTER, - P_KEY_A, P_KEY_B, P_KEY_C, P_KEY_D, P_KEY_E, P_KEY_F, P_KEY_G, P_KEY_H, P_KEY_I, - P_KEY_J, P_KEY_K, P_KEY_L, P_KEY_M, P_KEY_N, P_KEY_O, P_KEY_P, P_KEY_Q, P_KEY_R, - P_KEY_S, P_KEY_T, P_KEY_U, P_KEY_V, P_KEY_W, P_KEY_X, P_KEY_Y, P_KEY_Z, -} P_KeyCode; - -/* Platform event type */ -typedef enum { - P_EVENT_NONE = 0, - P_EVENT_KEY_DOWN, - P_EVENT_KEY_UP, - P_EVENT_WINDOW_CLOSE, - P_EVENT_WINDOW_RESIZE, - P_EVENT_POINTER, - P_EVENT_CONTROLLER_CONNECTED, - P_EVENT_CONTROLLER_DISCONNECTED, -} P_EvenType; - -typedef enum { - P_POINTER_MOVED = 0, - P_POINTER_PRESSED, - P_POINTER_RELEASED -} P_PointerState; - -typedef struct { - P_EvenType type; - union { - struct {P_KeyCode key; P_KeyMod mod; } key; - struct { u32 width, height; } resize; - struct { P_PointerState state; u32 button; u32 x, y; } pointer; - }; -} P_Event; - -/* - * Platform dependent functions to be implemented by each layer. - */ - -POAPI u64 p_get_time(void); // Get time in nanoseconds. -POAPI void p_sleep_ns(i64 ns); // Sleep for nanoseconds. - -POAPI void p_stdout(void *msg, usize bytes); // Print buffer to stdout. -PODEF void p_log_printf(P_Log_Level level, const char* src, ...); // Print buffer to stdout. - - -POAPI void* p_file_alloc(const char *path, unsigned long *buf_size); -POAPI bool p_file_exists(const char *path); // Check if file exists. -POAPI usize p_file_size(const char *path); // Check file size. -POAPI void p_file_load(const char *path, void *buf_ptr, unsigned long buf_size); // Load file to buffer. -PODEF void p_file_write(const char *path, void *buf_ptr, unsigned long buf_size); // Write buffer to file. - -typedef struct P_Window_Impl P_Window; -POAPI bool p_window_open(P_Window *win, int width, int height, const char *title); // Opens a window. Returns false on failure. -POAPI void p_window_close(P_Window *win); // Close window. -POAPI bool p_window_is_open(P_Window *win); // Check if window is open. -POAPI void p_window_size(P_Window *win, int *window_width, int *window_height); // Get window size. -POAPI void p_window_draw(P_Window *win, u32 *pixels, int width, int height); // Draw directly to the window's buffer. Normally much slower than using the GPU. -POAPI bool p_window_poll_event(P_Window *win, P_Event *ev); // Returns true while there are events to poll. Write event data to a pointer. - -POAPI void p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc); -POAPI void p_audio_quit(); -POAPI void p_audio_write(const i16 *samples, usize count); - -/* - * The Vulkan Module is platform specific - */ - -#ifdef P_MODULE_VULKAN -#include <vulkan/vulkan.h> -POAPI char** p_vulkan_get_extensions(); -POAPI bool p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface); -#endif - -/* - * The Remaining Modules are PLATFORM INDEPENDENT! - */ - -#ifdef P_MODULE_STRING -#include <p_string.h> // <REPLACE> -#endif - -#ifdef P_MODULE_MATH -#include <p_math.h> // <REPLACE> -#endif - -#ifdef P_MODULE_ALLOC -#include <p_allocators.h> // <REPLACE> -#endif - -#endif /* _PODIUM_H_ */ - -#ifdef PODIUM_IMPLEMENTATION -#undef PODIUM_IMPLEMENTATION - -#if defined(PODIUM_SDL) -/* #include "p_platform_sdl.c" */ -#elif defined(PODIUM_WIN32) -#include "p_platform_win32.c" // <REPLACE> -#elif defined(PODIUM_LINUX) -#include "p_platform_linux.c" // <REPLACE> -#endif /* linux */ - - -POAPI void* p_file_alloc(const char *path, unsigned long *buf_size) { - if (p_file_exists(path) == false) { - return NULL; - } - - usize file_size = p_file_size(path); - *buf_size = file_size; - - void *ptr = malloc(file_size); - if (ptr) { - p_file_load(path, ptr, file_size); // Load file to buffer. - } - return ptr; -} - - -/* - * Import Module Code - */ - -#ifdef P_MODULE_STRING -#include <p_string.c> // <REPLACE> -#endif - -#ifdef P_MODULE_MATH -#include <p_math.c> // <REPLACE> -#endif - -#ifdef P_MODULE_ALLOC -#include <p_allocators.c> // <REPLACE> -#endif - -#define P_MAX_PRINTF_SIZE 1024 -PODEF void -p_log_printf(P_Log_Level level, const char* src, ...) -{ - static char out_message[P_MAX_PRINTF_SIZE]; - static u32 offset = P_PREFIX_LEN + 1; - - memcpy(out_message, p_prefix[level], P_PREFIX_LEN); /* prefixes have a fixed len */ - out_message[P_PREFIX_LEN] = ' '; /* write space */ - - __builtin_va_list arg_ptr; - va_start(arg_ptr, src); - int len = vsnprintf(out_message+offset, P_MAX_PRINTF_SIZE-offset, src, arg_ptr); // append message - va_end(arg_ptr); - - if (len < 0) len = 0; - usize total_len = offset + len; - if (total_len > P_MAX_PRINTF_SIZE) { - total_len = P_MAX_PRINTF_SIZE; - } - - p_stdout(out_message, total_len); - p_stdout("\n", 1); -} - -#endif // PODIUM_IMPLEMENTATION @@ -1,11 +1,129 @@ #define POOF_IMPLEMENTATION #include "Poof/poof.h" -int main(int argc, char **argv) { - POOF_GO_REBUILD_URSELF(argc, argv); - +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static void +add_peak_config(Poof_CC *cc) +{ + poof_cmd_append(&cc->includes, "Peak"); + +#if defined(_WIN32) + poof_cc_append_win32(cc, "-luser32", "-lgdi32"); + { + const char *vulkan_sdk = getenv("VULKAN_SDK"); + if (vulkan_sdk) { + char inc_buf[512], lib_buf[512]; + snprintf(inc_buf, sizeof(inc_buf), "-I%s/Include", vulkan_sdk); + snprintf(lib_buf, sizeof(lib_buf), "-L%s/Lib", vulkan_sdk); + poof_cc_append_win32(cc, strdup(inc_buf), strdup(lib_buf)); + } + } + poof_cc_append_win32(cc, "-lvulkan-1"); +#else + poof_cc_append_linux(cc, "-pthread", "-ldl", "-lvulkan"); +#endif +} + +static void +slangc_entry(Poof_Batch *batch, const char *src, const char *entry, const char *stage, const char *out) +{ + Poof_Cmd cmd = {0}; + poof_cmd_append(&cmd, "slangc", src, "-target", "spirv", "-entry", entry, "-stage", stage, "-o", out); + poof_batch_append_cmd(batch, cmd); +} + +static void +add_rend_demo(Poof_Batch *batch, const char *src, const char *out, uint32_t opt, const char *define) +{ + Poof_CC cc; + poof_cc_init(&cc, POOF_CC_GCC | POOF_CC_CLANG, POOF_TARGET_HOST); + cc.debug_mode = true; + cc.optimization = opt; + cc.output = out; + poof_cmd_append(&cc.inputs, src); + poof_cmd_append(&cc.includes, ".", "Rend", "Peak", "demos", "demos/teapot"); + poof_cmd_append(&cc.defines, "PEAK_VULKAN"); + if (define) poof_cmd_append(&cc.defines, define); + poof_cmd_append(&cc.libs, "m"); + poof_cmd_append(&cc.extra_flags, "-std=c99", "-Wall"); + add_peak_config(&cc); + poof_batch_append_cc(batch, &cc); +} + +static bool +build_peak_header(void) +{ + Poof_Cmd cmd = {0}; + poof_cmd_append(&cmd, "python", "bundle_header.py", "Peak/src/peak_header.h"); + if (!poof_cmd_run(&cmd)) return false; + return poof_copy_file("Peak/src/peak_header.h.out", "Peak/peak.h"); +} + +static bool +build_peak_demos(void) +{ + Poof_CC cc = {0}; + poof_cc_init(&cc, POOF_CC_GCC | POOF_CC_CLANG, POOF_TARGET_HOST); + cc.debug_mode = true; + cc.optimization = POOF_O0; + cc.output = "demos/demo"; + poof_cc_append(&cc.inputs, "demos/demo.c"); + poof_cc_append(&cc.extra_flags, "-std=c99", "-Wall", "-Wno-deprecated-declarations", "-pthread"); + poof_cc_append(&cc.libs, "m", "pthread"); + if (!poof_cc_run(&cc)) return false; + + poof_cc_free(&cc); + poof_cc_init(&cc, POOF_CC_GCC | POOF_CC_CLANG, POOF_TARGET_HOST); + cc.debug_mode = true; + cc.optimization = POOF_O0; + cc.output = "demos/demo_run"; + poof_cc_append(&cc.inputs, "demos/demo_run.c"); + poof_cc_append(&cc.extra_flags, "-std=c99", "-Wall", "-Wno-deprecated-declarations", "-pthread"); + poof_cc_append(&cc.libs, "m", "pthread"); + if (!poof_cc_run(&cc)) return false; + Poof_Cmd cmd = {0}; - poof_cmd_append(&cmd, "./Peak/build"); - poof_cmd_run(&cmd); + poof_cmd_append(&cmd, "emcc", "demos/demo_run.c", "-o", "demos/demo.js", + "-std=c99", "-Wall", "-Wno-deprecated-declarations", + "-sALLOW_MEMORY_GROWTH=1", "-sENVIRONMENT=web"); + return poof_cmd_run(&cmd); +} + +static bool +build_rend_demos(void) +{ + poof_mkdir("demos/compute"); + poof_mkdir("demos/teapot"); + + Poof_Batch batch = {0}; + + slangc_entry(&batch, "demos/rend_compute.slang", "particleMain", "compute", "demos/compute/particle.spv"); + slangc_entry(&batch, "demos/rend_compute.slang", "sandMain", "compute", "demos/compute/sand.spv"); + slangc_entry(&batch, "demos/rend_compute.slang", "vertMain", "vertex", "demos/compute/vert.spv"); + slangc_entry(&batch, "demos/rend_compute.slang", "fragMain", "fragment", "demos/compute/frag.spv"); + + slangc_entry(&batch, "demos/teapot/shaders/basic.slang", "vertMain", "vertex", "demos/teapot/basic.vert.spv"); + slangc_entry(&batch, "demos/teapot/shaders/basic.slang", "fragMain", "fragment", "demos/teapot/basic.frag.spv"); + slangc_entry(&batch, "demos/teapot/shaders/texture.slang", "vertMain", "vertex", "demos/teapot/texture.vert.spv"); + slangc_entry(&batch, "demos/teapot/shaders/texture.slang", "fragMain", "fragment", "demos/teapot/texture.frag.spv"); + + add_rend_demo(&batch, "demos/rend_compute.c", "demos/compute/rend_compute_demo", POOF_O0, "REND_DEBUG"); + add_rend_demo(&batch, "demos/rend_compute.c", "demos/compute/rend_compute_fast", POOF_O3 | POOF_MSSE2, NULL); + add_rend_demo(&batch, "demos/rend_teapot.c", "demos/teapot/rend_teapot", POOF_O0, "REND_DEBUG"); + + return poof_batch_run(&batch, "Rend Demos"); +} + +int +main(int argc, char **argv) +{ + POOF_GO_REBUILD_URSELF(argc, argv); + + if (!build_peak_header()) return 1; + if (!build_peak_demos()) return 1; + if (!build_rend_demos()) return 1; return 0; } diff --git a/Rend/demos/compute/frag.spv b/demos/compute/frag.spv Binary files differindex 5366c51..5366c51 100644 --- a/Rend/demos/compute/frag.spv +++ b/demos/compute/frag.spv diff --git a/Rend/demos/compute/particle.spv b/demos/compute/particle.spv Binary files differindex 6547772..6547772 100644 --- a/Rend/demos/compute/particle.spv +++ b/demos/compute/particle.spv diff --git a/demos/compute/rend_compute_demo b/demos/compute/rend_compute_demo Binary files differnew file mode 100755 index 0000000..1aecc00 --- /dev/null +++ b/demos/compute/rend_compute_demo diff --git a/demos/compute/rend_compute_fast b/demos/compute/rend_compute_fast Binary files differnew file mode 100755 index 0000000..d9583f6 --- /dev/null +++ b/demos/compute/rend_compute_fast diff --git a/Rend/demos/compute/sand.spv b/demos/compute/sand.spv Binary files differindex 9dcac4d..9dcac4d 100644 --- a/Rend/demos/compute/sand.spv +++ b/demos/compute/sand.spv diff --git a/Rend/demos/compute/vert.spv b/demos/compute/vert.spv Binary files differindex e5d2970..e5d2970 100644 --- a/Rend/demos/compute/vert.spv +++ b/demos/compute/vert.spv diff --git a/demos/demo b/demos/demo Binary files differnew file mode 100755 index 0000000..9f9b6ee --- /dev/null +++ b/demos/demo diff --git a/Peak/demos/demo.c b/demos/demo.c index cb5212d..c095fdb 100644 --- a/Peak/demos/demo.c +++ b/demos/demo.c @@ -1,7 +1,7 @@ #include <stdio.h> #define PEAK_IMPLEMENTATION -#include "../peak.h" +#include "../Peak/peak.h" void print_window_resize(PeakEvent ev) { printf("window: %u %u\n", ev.resize.width, ev.resize.height); diff --git a/Peak/demos/demo.html b/demos/demo.html index a10e85e..a10e85e 100644 --- a/Peak/demos/demo.html +++ b/demos/demo.html diff --git a/Peak/demos/demo.js b/demos/demo.js index 6ebf1b1..1de6b03 100644 --- a/Peak/demos/demo.js +++ b/demos/demo.js @@ -436,7 +436,7 @@ function updateMemoryViews() { HEAPU32 = new Uint32Array(b); HEAPF64 = new Float64Array(b); - HEAP64 = new BigInt64Array(b); + } @@ -833,44 +833,6 @@ async function createWasm() { var __abort_js = () => abort('native code called abort()'); - var _emscripten_get_now = () => performance.now(); - - var _emscripten_date_now = () => Date.now(); - - var nowIsMonotonic = 1; - - var checkWasiClock = (clock_id) => clock_id >= 0 && clock_id <= 3; - - var INT53_MAX = 9007199254740992; - - var INT53_MIN = -9007199254740992; - var bigintToI53Checked = (num) => (num < INT53_MIN || num > INT53_MAX) ? NaN : Number(num); - - /** not-@type {!BigInt64Array} */ - var HEAP64; - function _clock_time_get(clk_id, ignored_precision, ptime) { - ignored_precision = bigintToI53Checked(ignored_precision); - - - if (!checkWasiClock(clk_id)) { - return 28; - } - var now; - // all wasi clocks but realtime are monotonic - if (clk_id === 0) { - now = _emscripten_date_now(); - } else if (nowIsMonotonic) { - now = _emscripten_get_now(); - } else { - return 52; - } - // "now" is in ms, and wasi times are in ns. - var nsec = Math.round(now * 1000 * 1000); - HEAP64[((ptime)>>3)] = BigInt(nsec); - return 0; - ; - } - var _emscripten_set_main_loop_timing = (mode, value) => { MainLoop.timingMode = mode; @@ -927,6 +889,7 @@ async function createWasm() { return 0; }; + var _emscripten_get_now = () => performance.now(); var runtimeKeepaliveCounter = 0; @@ -1175,7 +1138,6 @@ async function createWasm() { MainLoop.func = null; }; - var getHeapMax = () => // Stay one Wasm page short of 4GB: while e.g. Chrome is able to allocate // full 4GB Wasm memories, the size will wrap back to 0 bytes in Wasm side @@ -1605,6 +1567,10 @@ async function createWasm() { abort('fd_close called without SYSCALLS_REQUIRE_FILESYSTEM'); }; + var INT53_MAX = 9007199254740992; + + var INT53_MIN = -9007199254740992; + var bigintToI53Checked = (num) => (num < INT53_MIN || num > INT53_MAX) ? NaN : Number(num); function _fd_seek(fd, offset, whence, newOffset) { offset = bigintToI53Checked(offset); @@ -1688,6 +1654,7 @@ async function createWasm() { + Module['requestAnimationFrame'] = MainLoop.requestAnimationFrame; Module['pauseMainLoop'] = MainLoop.pause; Module['resumeMainLoop'] = MainLoop.resume; @@ -1851,6 +1818,7 @@ Module['FS_createPreloadedFile'] = FS.createPreloadedFile; 'getCallstack', 'convertPCtoSourceLocation', 'getEnvStrings', + 'checkWasiClock', 'wasiRightsToMuslOFlags', 'wasiOFlagsToMuslOFlags', 'initRandomFill', @@ -1985,7 +1953,6 @@ missingLibrarySymbols.forEach(missingLibrarySymbol) 'restoreOldWindowedStyle', 'UNWIND_CACHE', 'ExitStatus', - 'checkWasiClock', 'flush_NO_FILESYSTEM', 'emSetImmediate', 'emClearImmediate_deps', @@ -2174,13 +2141,16 @@ function checkIncomingModuleAPI() { } function peak_web_dom_open(id,w,h) { var name = UTF8ToString(id); var c = document.getElementById(name); if (!c) { c = document.createElement('canvas'); c.id = name; document.body.appendChild(c); } c.width = w; c.height = h; c.tabIndex = 0; c.focus(); } function peak_web_dom_present(id,w,h,pixels) { var c = document.getElementById(UTF8ToString(id)); if (!c) return; var ctx = c.getContext('2d'); if (c.width !== w || c.height !== h) { c.width = w; c.height = h; } var img = ctx.createImageData(w, h); img.data.set(HEAPU8.subarray(pixels, pixels + w * h * 4)); ctx.putImageData(img, 0, 0); } +function peak_web_audio_dom_start(channels,rate,frames,ptr) { var AC = window.AudioContext || window.webkitAudioContext; var ctx, proc, i, c, heap, off, ch; if (!AC || Module._peak_web_audio) return 0; ctx = new AC({ sampleRate: rate }); if (ctx.sampleRate !== rate) { ctx.close(); return 0; } proc = ctx.createScriptProcessor(frames, 0, channels); proc.onaudioprocess = function(e) { Module._peak_internal_web_audio_fill(ptr, frames); heap = Module.HEAP16; off = ptr >> 1; for (c = 0; c < channels; c++) { ch = e.outputBuffer.getChannelData(c); for (i = 0; i < frames; i++) ch[i] = heap[off + i * channels + c] / 32768.0; } }; proc.connect(ctx.destination); ctx.resume(); Module._peak_web_audio = { ctx: ctx, proc: proc }; return 1; } +function peak_web_audio_dom_stop() { var a = Module._peak_web_audio; if (!a) return; a.proc.disconnect(); a.ctx.close(); Module._peak_web_audio = null; } // Imports from the Wasm binary. +var _peak_internal_web_audio_fill = Module['_peak_internal_web_audio_fill'] = makeInvalidEarlyAccess('_peak_internal_web_audio_fill'); var _main = Module['_main'] = makeInvalidEarlyAccess('_main'); var _fflush = makeInvalidEarlyAccess('_fflush'); +var _strerror = makeInvalidEarlyAccess('_strerror'); var _emscripten_stack_get_end = makeInvalidEarlyAccess('_emscripten_stack_get_end'); var _emscripten_stack_get_base = makeInvalidEarlyAccess('_emscripten_stack_get_base'); -var _strerror = makeInvalidEarlyAccess('_strerror'); var _malloc = makeInvalidEarlyAccess('_malloc'); var _emscripten_stack_init = makeInvalidEarlyAccess('_emscripten_stack_init'); var _emscripten_stack_get_free = makeInvalidEarlyAccess('_emscripten_stack_get_free'); @@ -2193,11 +2163,12 @@ var wasmMemory = makeInvalidEarlyAccess('wasmMemory'); var wasmTable = makeInvalidEarlyAccess('wasmTable'); function assignWasmExports(wasmExports) { + assert(typeof wasmExports['peak_internal_web_audio_fill'] != 'undefined', 'missing Wasm export: peak_internal_web_audio_fill'); assert(typeof wasmExports['__main_argc_argv'] != 'undefined', 'missing Wasm export: __main_argc_argv'); assert(typeof wasmExports['fflush'] != 'undefined', 'missing Wasm export: fflush'); + assert(typeof wasmExports['strerror'] != 'undefined', 'missing Wasm export: strerror'); assert(typeof wasmExports['emscripten_stack_get_end'] != 'undefined', 'missing Wasm export: emscripten_stack_get_end'); assert(typeof wasmExports['emscripten_stack_get_base'] != 'undefined', 'missing Wasm export: emscripten_stack_get_base'); - assert(typeof wasmExports['strerror'] != 'undefined', 'missing Wasm export: strerror'); assert(typeof wasmExports['malloc'] != 'undefined', 'missing Wasm export: malloc'); assert(typeof wasmExports['emscripten_stack_init'] != 'undefined', 'missing Wasm export: emscripten_stack_init'); assert(typeof wasmExports['emscripten_stack_get_free'] != 'undefined', 'missing Wasm export: emscripten_stack_get_free'); @@ -2206,11 +2177,12 @@ function assignWasmExports(wasmExports) { assert(typeof wasmExports['emscripten_stack_get_current'] != 'undefined', 'missing Wasm export: emscripten_stack_get_current'); assert(typeof wasmExports['memory'] != 'undefined', 'missing Wasm export: memory'); assert(typeof wasmExports['__indirect_function_table'] != 'undefined', 'missing Wasm export: __indirect_function_table'); + _peak_internal_web_audio_fill = Module['_peak_internal_web_audio_fill'] = createExportWrapper('peak_internal_web_audio_fill', wasmExports['peak_internal_web_audio_fill'], 2); _main = Module['_main'] = createExportWrapper('__main_argc_argv', wasmExports['__main_argc_argv'], 2); _fflush = createExportWrapper('fflush', wasmExports['fflush'], 1); + _strerror = createExportWrapper('strerror', wasmExports['strerror'], 1); _emscripten_stack_get_end = wasmExports['emscripten_stack_get_end']; _emscripten_stack_get_base = wasmExports['emscripten_stack_get_base']; - _strerror = createExportWrapper('strerror', wasmExports['strerror'], 1); _malloc = createExportWrapper('malloc', wasmExports['malloc'], 1); _emscripten_stack_init = wasmExports['emscripten_stack_init']; _emscripten_stack_get_free = wasmExports['emscripten_stack_get_free']; @@ -2227,12 +2199,8 @@ var wasmImports = { /** @export */ _abort_js: __abort_js, /** @export */ - clock_time_get: _clock_time_get, - /** @export */ emscripten_cancel_main_loop: _emscripten_cancel_main_loop, /** @export */ - emscripten_get_now: _emscripten_get_now, - /** @export */ emscripten_resize_heap: _emscripten_resize_heap, /** @export */ emscripten_set_keydown_callback_on_thread: _emscripten_set_keydown_callback_on_thread, @@ -2253,6 +2221,8 @@ var wasmImports = { /** @export */ fd_write: _fd_write, /** @export */ + peak_web_audio_dom_stop, + /** @export */ peak_web_dom_open }; diff --git a/demos/demo.wasm b/demos/demo.wasm Binary files differnew file mode 100755 index 0000000..6dbbbd6 --- /dev/null +++ b/demos/demo.wasm diff --git a/demos/demo_run b/demos/demo_run Binary files differnew file mode 100755 index 0000000..45a51e9 --- /dev/null +++ b/demos/demo_run diff --git a/Peak/demos/demo_run.c b/demos/demo_run.c index f5c2f7f..db3e56a 100644 --- a/Peak/demos/demo_run.c +++ b/demos/demo_run.c @@ -1,7 +1,7 @@ #include <stdio.h> #define PEAK_IMPLEMENTATION -#include "../peak.h" +#include "../Peak/peak.h" void print_window_resize(PeakEvent ev) { diff --git a/Rend/demos/rend_compute.c b/demos/rend_compute.c index 7d5e407..f82836a 100644 --- a/Rend/demos/rend_compute.c +++ b/demos/rend_compute.c @@ -1,23 +1,19 @@ #include <string.h> #ifdef DEBUG_MEMORY -#define malloc(size) p_debug_malloc_impl((size), __FILE__, __LINE__, __func__) -#define realloc(ptr, size) p_debug_realloc_impl((ptr), (size), __FILE__, __LINE__, __func__) -#define free(ptr) p_debug_free_impl((ptr), __FILE__, __LINE__, __func__) +#define malloc(size) peak_debug_malloc_impl((size), __FILE__, __LINE__, __func__) +#define realloc(ptr, size) peak_debug_realloc_impl((ptr), (size), __FILE__, __LINE__, __func__) +#define free(ptr) peak_debug_free_impl((ptr), __FILE__, __LINE__, __func__) #endif -#include "../rend.h" -#include "../rend.c" +#define PEAK_IMPLEMENTATION +#include "../Rend/rend.h" +#include "../Rend/rend.c" #include <stddef.h> -#define PODIUM_IMPLEMENTATION -#define P_MODULE_MATH -#define P_MODULE_VULKAN -#include "podium.h" - float delta = 0; -P_Window win; -P_Event ev; +PeakWindow win; +PeakEvent ev; bool vsync = true; @@ -52,9 +48,23 @@ float cool_beans(uint32_t *seed) { return (float) *seed / 4294967295.0; } +static uint8_t * +load_spv(const char *a, const char *b, unsigned long *size) +{ + uint8_t *p = peak_file_alloc(a, size); + if (p) return p; + return peak_file_alloc(b, size); +} + int main() { - if (!p_window_open(&win, 800, 600, "demo")) { + if (!peak_init()) { + PFATAL("Failed to init Peak!"); + return 1; + } + + win = peak_window_open("demo", 800, 600, 0); + if (!win.running) { PFATAL("Failed to open a window!"); return 1; } @@ -62,18 +72,24 @@ int main() { // bindless setup RendBindingInfo bind_info = {0}; RendRenderer renderer = rend_renderer_create(&win, REND_BACKEND_AUTO, NULL, vsync, &bind_info); + if (!renderer) { + PFATAL("Failed to create renderer!"); + peak_window_close(&win); + peak_quit(); + return 1; + } unsigned long particles_bytes = 0; - uint8_t *particles_shader = p_file_alloc("particle.spv", &particles_bytes); + uint8_t *particles_shader = load_spv("particle.spv", "demos/compute/particle.spv", &particles_bytes); unsigned long sand_bytes = 0; - uint8_t *sand_shader = p_file_alloc("sand.spv", &sand_bytes); + uint8_t *sand_shader = load_spv("sand.spv", "demos/compute/sand.spv", &sand_bytes); unsigned long vert_size = 0; - uint8_t *vert = p_file_alloc("vert.spv", &vert_size); + uint8_t *vert = load_spv("vert.spv", "demos/compute/vert.spv", &vert_size); unsigned long frag_size = 0; - uint8_t *frag = p_file_alloc("frag.spv", &frag_size); + uint8_t *frag = load_spv("frag.spv", "demos/compute/frag.spv", &frag_size); uint32_t seed = 0xC0FFEE; @@ -125,11 +141,11 @@ int main() { uint32_t mode = 2; while (mode > 0) { - uint64_t start = p_get_time(); + uint64_t start = peak_get_time(); - P_Event ev; - while (p_window_poll_event(&win, &ev)) { - if (ev.type == P_EVENT_WINDOW_CLOSE) { + PeakEvent ev; + while (peak_window_epoll(&win, &ev)) { + if (ev.type == PEAK_EVENT_WINDOW_CLOSE) { mode -= 1; break; } @@ -189,9 +205,9 @@ int main() { rend_renderer_frame_end(renderer, NULL); } - uint64_t delta_ns = p_get_time() - start; + uint64_t delta_ns = peak_get_time() - start; if (delta_ns < dt_ns) { - p_sleep_ns(dt_ns - delta_ns); + peak_sleep_ns(dt_ns - delta_ns); } } @@ -205,10 +221,11 @@ int main() { rend_renderer_destroy(renderer); rend_quit(); - p_window_close(&win); + peak_window_close(&win); + peak_quit(); #if DEBUG_MEMORY - p_debug_memory_report(); + peak_debug_memory_report(); #endif return 0; } diff --git a/Rend/demos/rend_compute.slang b/demos/rend_compute.slang index 5fb124b..5fb124b 100644 --- a/Rend/demos/rend_compute.slang +++ b/demos/rend_compute.slang diff --git a/demos/rend_teapot.c b/demos/rend_teapot.c new file mode 100644 index 0000000..b4430e9 --- /dev/null +++ b/demos/rend_teapot.c @@ -0,0 +1,242 @@ +#define PEAK_IMPLEMENTATION +#include "teapot.h" +#include "../Rend/rend.h" +#include "../Rend/rend.c" +#include "../Math/math.h" +#include <stddef.h> +#include <stdlib.h> + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +static uint8_t * +load_spv(const char *a, const char *b, unsigned long *size) +{ + uint8_t *p = peak_file_alloc(a, size); + if (p) return p; + return peak_file_alloc(b, size); +} + +int main() { + + if (!peak_init()) { + PFATAL("Failed to init Peak!"); + return 1; + } + + PeakWindow win = peak_window_open("teapot", 800, 600, 0); + if (!win.running) { + PFATAL("Failed to open a window!"); + return 1; + } + + RendBindingInfo bind_info = {0}; + RendRenderer renderer; + + /* basic.slang: set 0 binding 0 = ubos[3], binding 1 = test[3] */ + bind_info.ubo_bindings[0] = 0; + bind_info.ubo_array_sizes[0] = 3; + bind_info.ubo_binding_count = 1; + bind_info.texture_bindings[0] = 1; + bind_info.texture_array_sizes[0] = 3; + bind_info.texture_binding_count = 1; + renderer = rend_renderer_create(&win, REND_BACKEND_AUTO, NULL, true, &bind_info); + if (!renderer) { + PFATAL("Failed to create renderer!"); + peak_window_close(&win); + peak_quit(); + return 1; + } + + const char *texture_path = "wal.jpg"; + if (!peak_file_exists(texture_path)) { + texture_path = "demos/teapot/wal.jpg"; + if (!peak_file_exists(texture_path)) { + texture_path = "demos/wal.jpg"; + } + } + typedef struct CoolPushConstants { + float MVP[16]; + } CoolPushConstants; + + typedef struct CoolVertex { + float pos[3]; + float uv[2]; + } CoolVertex; + + CoolVertex cool_array[TEAPOT_VERTEX_COUNT]; + + for (uint32_t i = 0; i < TEAPOT_VERTEX_COUNT; ++i) { + cool_array[i].pos[0] = teapot_positions[i][0]; + cool_array[i].pos[1] = teapot_positions[i][1]; + cool_array[i].pos[2] = teapot_positions[i][2]; + + float u = teapot_positions[i][0] / teapot_positions[i][2]; + float v = teapot_positions[i][1] / teapot_positions[i][2]; + + cool_array[i].uv[0] = u; + cool_array[i].uv[1] = v; + } + + size_t vert_size = TEAPOT_VERTEX_COUNT * sizeof *cool_array; + size_t ind_size = TEAPOT_INDEX_COUNT * sizeof *teapot_indices; + + RendBuffer vert_buf = rend_buffer_create(renderer, vert_size, REND_BUFFER_VERTEX, true); + RendBuffer ind_buf = rend_buffer_create(renderer, ind_size, REND_BUFFER_INDEX, true); + rend_buffer_write(renderer, &vert_buf, cool_array, vert_size, 0); + rend_buffer_write(renderer, &ind_buf, teapot_indices, ind_size, 0); + + int tex_w, tex_h, tex_channels; + unsigned char *pixels = stbi_load(texture_path, &tex_w, &tex_h, &tex_channels, 4); + RendTexture texture = rend_texture_create(renderer, tex_w, tex_h, 1, 0, 1, REND_FORMAT_R8G8B8A8_SRGB); + if (pixels) { + rend_texture_copy_data(renderer, &texture, pixels, (size_t)tex_w * tex_h * 4); + stbi_image_free(pixels); + } + + unsigned long basic_vert_bytes = 0; + unsigned long basic_frag_bytes = 0; + uint8_t *basic_vert = load_spv("basic.vert.spv", "demos/teapot/basic.vert.spv", &basic_vert_bytes); + uint8_t *basic_frag = load_spv("basic.frag.spv", "demos/teapot/basic.frag.spv", &basic_frag_bytes); + + RendPipeline basic_pipeline = rend_pipeline_create_graphics_spirv( + renderer, + basic_vert, basic_vert_bytes, + basic_frag, basic_frag_bytes, + &(RendVertexBinding){ .binding = 0, .stride = sizeof(CoolVertex), .input_rate = REND_INPUT_RATE_VERTEX }, 1, + (RendVertexAttributes[]){ + { .binding = 0, .location = 0, .format = REND_FORMAT_3_SFLOAT32, .offset = offsetof(CoolVertex, pos) }, + { .binding = 0, .location = 1, .format = REND_FORMAT_2_SFLOAT32, .offset = offsetof(CoolVertex, uv) } + }, 2, + &(RendPushConstantInfo){ .offset = 0, .size = sizeof(CoolPushConstants) }, 1, + REND_POLYGON_MODE_FILL, + REND_CULL_MODE_NONE, + REND_TOPOLOGY_TRIANGLE_STRIP, + REND_FORMAT_UNDEFINED, + true + ); + + free(basic_vert); + free(basic_frag); + if (!basic_pipeline) { + PFATAL("Failed to create teapot pipeline!"); + return 1; + } + rend_pipeline_bind_texture(basic_pipeline, &texture, 1, 0); + + float view[16], proj[16]; + float eye[3] = {0.0f, 0.0f, -3.0f}; + math_mat4_translate(view, eye); + math_mat4_perspective(proj, MATH_PI_HALF, 800.0f / 600.0f, 0.1f, 100.0f); + proj[5] *= -1.0f; /* Vulkan clip space Y inversion fix */ + + bool running = true; + float angle = 0; + + bool pointer_down = false; + float last_pointer_x = 0; + float last_pointer_y = 0; + + while (running) { + + PeakEvent ev; + while (peak_window_epoll(&win, &ev)) { + if (ev.type == PEAK_EVENT_WINDOW_CLOSE) { + running = false; + break; + } + if (ev.type == PEAK_EVENT_KEY_DOWN) { + switch (ev.key.key) { + case PEAK_KEY_W: { + float d[3] = {0.0f, 0.0f, 0.1f}; + math_mat4_translate_by(view, d); + } break; + case PEAK_KEY_S: { + float d[3] = {0.0f, 0.0f, -0.1f}; + math_mat4_translate_by(view, d); + } break; + case PEAK_KEY_A: { + float d[3] = {0.1f, 0.0f, 0.0f}; + math_mat4_translate_by(view, d); + } break; + case PEAK_KEY_D: { + float d[3] = {-0.1f, 0.0f, 0.0f}; + math_mat4_translate_by(view, d); + } break; + default: break; + } + break; + } + if (ev.type == PEAK_EVENT_POINTER) { + if (ev.pointer.state == PEAK_POINTER_PRESSED && ev.pointer.type == PEAK_POINTER_LEFT) { + pointer_down = true; + last_pointer_x = ev.pointer.x; + last_pointer_y = ev.pointer.y; + } else if (ev.pointer.state == PEAK_POINTER_RELEASED && ev.pointer.type == PEAK_POINTER_LEFT) { + pointer_down = false; + } else if (ev.pointer.state == PEAK_POINTER_MOVED) { + if (pointer_down) { + float dx = ev.pointer.x - last_pointer_x; + float dy = ev.pointer.y - last_pointer_y; + + float sensitivity = 0.005f; + float yaw = dx * sensitivity; + float pitch = dy * sensitivity; + + math_mat4_rotate_y_by(view, yaw); + { + float rx[16]; + int i; + math_mat4_rotate_x(rx, pitch); + math_mat4_mul(rx, view); + for (i = 0; i < 16; i++) view[i] = rx[i]; + } + + last_pointer_x = ev.pointer.x; + last_pointer_y = ev.pointer.y; + } + } + } + } + + angle += 0.05; + + float model[16], vm[16], mvp[16]; + int i; + math_mat4_identity(model); + math_mat4_rotate_y_by(model, angle * 0.5f); + + for (i = 0; i < 16; i++) vm[i] = view[i]; + math_mat4_mul(vm, model); + for (i = 0; i < 16; i++) mvp[i] = proj[i]; + math_mat4_mul(mvp, vm); + + CoolPushConstants pc; + for (i = 0; i < 16; i++) pc.MVP[i] = mvp[i]; + + if (rend_renderer_frame_begin(renderer)) { + + rend_renderer_render_pass_begin(renderer, 0.5, 0.5, 0.5, 0.0); { + rend_pipeline_bind(basic_pipeline); + rend_pipeline_push_constants(basic_pipeline, &pc, sizeof pc); + rend_pipeline_bind_vertex_buffer(basic_pipeline, 0, vert_buf, 0); + rend_pipeline_bind_index_buffer(basic_pipeline, ind_buf, 0, REND_INDEX_UINT16); + rend_pipeline_draw_indexed(basic_pipeline, TEAPOT_INDEX_COUNT, 0, 0, 1); + } rend_renderer_render_pass_end(renderer); + + rend_renderer_frame_end(renderer, NULL); + } + + } + + rend_texture_destroy(renderer, &texture); + rend_buffer_destroy(&vert_buf); + rend_buffer_destroy(&ind_buf); + rend_renderer_destroy(renderer); + rend_quit(); + + peak_window_close(&win); + peak_quit(); + + return 0; +} diff --git a/Rend/demos/teapot.h b/demos/teapot.h index dfde9be..dfde9be 100644 --- a/Rend/demos/teapot.h +++ b/demos/teapot.h diff --git a/demos/teapot/basic.frag.spv b/demos/teapot/basic.frag.spv Binary files differnew file mode 100644 index 0000000..6c81c42 --- /dev/null +++ b/demos/teapot/basic.frag.spv diff --git a/demos/teapot/basic.vert.spv b/demos/teapot/basic.vert.spv Binary files differnew file mode 100644 index 0000000..92eaaa5 --- /dev/null +++ b/demos/teapot/basic.vert.spv diff --git a/demos/teapot/rend_teapot b/demos/teapot/rend_teapot Binary files differnew file mode 100755 index 0000000..c1d3a30 --- /dev/null +++ b/demos/teapot/rend_teapot diff --git a/Rend/demos/teapot/shaders/basic.slang b/demos/teapot/shaders/basic.slang index cfbf48a..cfbf48a 100644 --- a/Rend/demos/teapot/shaders/basic.slang +++ b/demos/teapot/shaders/basic.slang diff --git a/Rend/demos/teapot/shaders/texture.slang b/demos/teapot/shaders/texture.slang index 0210b66..0210b66 100644 --- a/Rend/demos/teapot/shaders/texture.slang +++ b/demos/teapot/shaders/texture.slang diff --git a/Rend/demos/teapot/stb_image.h b/demos/teapot/stb_image.h index 9eedabe..9eedabe 100644 --- a/Rend/demos/teapot/stb_image.h +++ b/demos/teapot/stb_image.h diff --git a/Rend/demos/teapot/texture.frag.spv b/demos/teapot/texture.frag.spv Binary files differindex 09a2e00..dab59f8 100644 --- a/Rend/demos/teapot/texture.frag.spv +++ b/demos/teapot/texture.frag.spv diff --git a/Rend/demos/teapot/texture.vert.spv b/demos/teapot/texture.vert.spv Binary files differindex 9e6c442..9e6c442 100644 --- a/Rend/demos/teapot/texture.vert.spv +++ b/demos/teapot/texture.vert.spv diff --git a/Rend/demos/teapot/wal.jpg b/demos/teapot/wal.jpg Binary files differindex d30a9a2..d30a9a2 100644 --- a/Rend/demos/teapot/wal.jpg +++ b/demos/teapot/wal.jpg |
