summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVasco <[email protected]>2026-08-24 03:53:16 +0100
committerVasco <[email protected]>2026-08-24 03:53:16 +0100
commitad5248a3e1bafc1b7dec4c8af82d56ed324ef314 (patch)
treeb575c3b85d6ad314fa9768855bed9aed8642917e
parent5dc2716abd43166cd65133625f408ad36a6623b3 (diff)
Slimed out podium. Updated math. Rend kinda works but still bugged.
-rw-r--r--Draw/draw.h307
-rw-r--r--Fuse/demos/fuse.slang31
-rw-r--r--Fuse/demos/fuse_demo.c280
-rw-r--r--Math/math.h343
-rw-r--r--Peak/build.c39
-rwxr-xr-xPeak/demos/demobin43544 -> 0 bytes
-rwxr-xr-xPeak/demos/demo.wasmbin30983 -> 0 bytes
-rwxr-xr-xPeak/demos/demo_runbin43912 -> 0 bytes
-rw-r--r--Peak/peak.h1037
-rw-r--r--Peak/src/p_emscripten.c118
-rw-r--r--Peak/src/p_linux.c187
-rw-r--r--Peak/src/p_log.c153
-rw-r--r--Peak/src/p_win32.c391
-rw-r--r--Peak/src/peak.c80
-rw-r--r--Peak/src/peak_header.h103
-rw-r--r--Peak/src/peak_header.h.out1037
-rw-r--r--README.md10
-rw-r--r--Rend/demos/build.c85
-rwxr-xr-xRend/demos/compute/rend_compute_demobin424800 -> 0 bytes
-rwxr-xr-xRend/demos/compute/rend_compute_fastbin450512 -> 0 bytes
-rw-r--r--Rend/demos/poof.h874
-rw-r--r--Rend/demos/rend_teapot.c172
-rw-r--r--Rend/podium.h2282
-rw-r--r--Rend/rend.c11
-rw-r--r--Rend/rend.h20
-rw-r--r--Rend/rend_internal.h20
-rw-r--r--Rend/rend_vk.c198
-rw-r--r--Rend/rend_vk_device.c18
-rw-r--r--Rend/rend_vk_internal.h4
-rw-r--r--archive/Podium/README.md9
-rw-r--r--archive/Podium/p_allocators.c301
-rw-r--r--archive/Podium/p_allocators.h44
-rw-r--r--archive/Podium/p_ds.h52
-rw-r--r--archive/Podium/p_log.h186
-rw-r--r--archive/Podium/p_math.c255
-rw-r--r--archive/Podium/p_math.h33
-rw-r--r--archive/Podium/p_platform_linux.c404
-rw-r--r--archive/Podium/p_platform_win32.c402
-rw-r--r--archive/Podium/p_string.c201
-rw-r--r--archive/Podium/p_string.h38
-rw-r--r--archive/Podium/p_types.h88
-rw-r--r--archive/Podium/podium.c1
-rw-r--r--archive/Podium/podium.h2269
-rw-r--r--archive/Podium/podium_header.h258
-rw-r--r--build.c128
-rw-r--r--demos/compute/frag.spv (renamed from Rend/demos/compute/frag.spv)bin376 -> 376 bytes
-rw-r--r--demos/compute/particle.spv (renamed from Rend/demos/compute/particle.spv)bin1876 -> 1876 bytes
-rwxr-xr-xdemos/compute/rend_compute_demobin0 -> 446416 bytes
-rwxr-xr-xdemos/compute/rend_compute_fastbin0 -> 474256 bytes
-rw-r--r--demos/compute/sand.spv (renamed from Rend/demos/compute/sand.spv)bin5548 -> 5548 bytes
-rw-r--r--demos/compute/vert.spv (renamed from Rend/demos/compute/vert.spv)bin3704 -> 3704 bytes
-rwxr-xr-xdemos/demobin0 -> 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-xdemos/demo.wasmbin0 -> 31763 bytes
-rwxr-xr-xdemos/demo_runbin0 -> 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.c242
-rw-r--r--demos/teapot.h (renamed from Rend/demos/teapot.h)0
-rw-r--r--demos/teapot/basic.frag.spvbin0 -> 764 bytes
-rw-r--r--demos/teapot/basic.vert.spvbin0 -> 1008 bytes
-rwxr-xr-xdemos/teapot/rend_teapotbin0 -> 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)bin684 -> 680 bytes
-rw-r--r--demos/teapot/texture.vert.spv (renamed from Rend/demos/teapot/texture.vert.spv)bin844 -> 844 bytes
-rw-r--r--demos/teapot/wal.jpg (renamed from Rend/demos/teapot/wal.jpg)bin21110 -> 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
deleted file mode 100755
index 8f72b0a..0000000
--- a/Peak/demos/demo
+++ /dev/null
Binary files differ
diff --git a/Peak/demos/demo.wasm b/Peak/demos/demo.wasm
deleted file mode 100755
index e7e428b..0000000
--- a/Peak/demos/demo.wasm
+++ /dev/null
Binary files differ
diff --git a/Peak/demos/demo_run b/Peak/demos/demo_run
deleted file mode 100755
index 7a526c1..0000000
--- a/Peak/demos/demo_run
+++ /dev/null
Binary files differ
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
diff --git a/README.md b/README.md
index 846362e..5a69cdf 100644
--- a/README.md
+++ b/README.md
@@ -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
deleted file mode 100755
index 071b495..0000000
--- a/Rend/demos/compute/rend_compute_demo
+++ /dev/null
Binary files differ
diff --git a/Rend/demos/compute/rend_compute_fast b/Rend/demos/compute/rend_compute_fast
deleted file mode 100755
index b9dac67..0000000
--- a/Rend/demos/compute/rend_compute_fast
+++ /dev/null
Binary files differ
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
diff --git a/build.c b/build.c
index d111efe..7c34e42 100644
--- a/build.c
+++ b/build.c
@@ -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
index 5366c51..5366c51 100644
--- a/Rend/demos/compute/frag.spv
+++ b/demos/compute/frag.spv
Binary files differ
diff --git a/Rend/demos/compute/particle.spv b/demos/compute/particle.spv
index 6547772..6547772 100644
--- a/Rend/demos/compute/particle.spv
+++ b/demos/compute/particle.spv
Binary files differ
diff --git a/demos/compute/rend_compute_demo b/demos/compute/rend_compute_demo
new file mode 100755
index 0000000..1aecc00
--- /dev/null
+++ b/demos/compute/rend_compute_demo
Binary files differ
diff --git a/demos/compute/rend_compute_fast b/demos/compute/rend_compute_fast
new file mode 100755
index 0000000..d9583f6
--- /dev/null
+++ b/demos/compute/rend_compute_fast
Binary files differ
diff --git a/Rend/demos/compute/sand.spv b/demos/compute/sand.spv
index 9dcac4d..9dcac4d 100644
--- a/Rend/demos/compute/sand.spv
+++ b/demos/compute/sand.spv
Binary files differ
diff --git a/Rend/demos/compute/vert.spv b/demos/compute/vert.spv
index e5d2970..e5d2970 100644
--- a/Rend/demos/compute/vert.spv
+++ b/demos/compute/vert.spv
Binary files differ
diff --git a/demos/demo b/demos/demo
new file mode 100755
index 0000000..9f9b6ee
--- /dev/null
+++ b/demos/demo
Binary files differ
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
new file mode 100755
index 0000000..6dbbbd6
--- /dev/null
+++ b/demos/demo.wasm
Binary files differ
diff --git a/demos/demo_run b/demos/demo_run
new file mode 100755
index 0000000..45a51e9
--- /dev/null
+++ b/demos/demo_run
Binary files differ
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
new file mode 100644
index 0000000..6c81c42
--- /dev/null
+++ b/demos/teapot/basic.frag.spv
Binary files differ
diff --git a/demos/teapot/basic.vert.spv b/demos/teapot/basic.vert.spv
new file mode 100644
index 0000000..92eaaa5
--- /dev/null
+++ b/demos/teapot/basic.vert.spv
Binary files differ
diff --git a/demos/teapot/rend_teapot b/demos/teapot/rend_teapot
new file mode 100755
index 0000000..c1d3a30
--- /dev/null
+++ b/demos/teapot/rend_teapot
Binary files differ
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
index 09a2e00..dab59f8 100644
--- a/Rend/demos/teapot/texture.frag.spv
+++ b/demos/teapot/texture.frag.spv
Binary files differ
diff --git a/Rend/demos/teapot/texture.vert.spv b/demos/teapot/texture.vert.spv
index 9e6c442..9e6c442 100644
--- a/Rend/demos/teapot/texture.vert.spv
+++ b/demos/teapot/texture.vert.spv
Binary files differ
diff --git a/Rend/demos/teapot/wal.jpg b/demos/teapot/wal.jpg
index d30a9a2..d30a9a2 100644
--- a/Rend/demos/teapot/wal.jpg
+++ b/demos/teapot/wal.jpg
Binary files differ