summaryrefslogtreecommitdiff
path: root/demos/compute
diff options
context:
space:
mode:
authorVasco <[email protected]>2026-08-24 17:47:30 +0100
committerVasco <[email protected]>2026-08-24 17:50:52 +0100
commitcc3429220b19df8c5ca6abf28a02cd38d08de32d (patch)
treedd6fe3911535cc75570e5fe34ff84c758028aa81 /demos/compute
parentad5248a3e1bafc1b7dec4c8af82d56ed324ef314 (diff)
Cool 0.0.0. Rend 1.0.4.
Diffstat (limited to 'demos/compute')
-rw-r--r--demos/compute/rend_compute.c226
-rw-r--r--demos/compute/rend_compute.slang146
-rwxr-xr-xdemos/compute/rend_compute_demobin446416 -> 434248 bytes
-rwxr-xr-xdemos/compute/rend_compute_fastbin474256 -> 0 bytes
4 files changed, 372 insertions, 0 deletions
diff --git a/demos/compute/rend_compute.c b/demos/compute/rend_compute.c
new file mode 100644
index 0000000..37ee207
--- /dev/null
+++ b/demos/compute/rend_compute.c
@@ -0,0 +1,226 @@
+#define PEAK_IMPLEMENTATION
+#include "../Rend/rend.h"
+#include "../Rend/rend.c"
+#include <stddef.h>
+#include <string.h>
+
+#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__)
+
+float delta = 0;
+PeakWindow win;
+PeakEvent ev;
+
+bool vsync = true;
+
+#define PARTICLE_COUNT 1024 * 10
+#define GRID_WIDTH 512
+#define GRID_HEIGHT 512
+
+typedef struct {
+ uint64_t particles;
+ uint64_t src_grid;
+ uint64_t dst_grid;
+ uint32_t particle_count;
+ uint32_t width;
+ uint32_t height;
+ float delta_time;
+ uint32_t render_mode;
+} PushConstants;
+
+typedef struct Particle {
+ float pos[2];
+ float vel[2];
+} Particle;
+
+uint32_t pcg_hash(uint32_t seed) {
+ uint32_t state = seed * 747796405u + 2891336453u;
+ uint32_t word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
+ return (word >> 22u) ^ word;
+}
+
+float cool_beans(uint32_t *seed) {
+ *seed = pcg_hash(*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 (!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;
+ }
+
+ // 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 = load_spv("particle.spv", "demos/compute/particle.spv", &particles_bytes);
+
+ unsigned long sand_bytes = 0;
+ uint8_t *sand_shader = load_spv("sand.spv", "demos/compute/sand.spv", &sand_bytes);
+
+ unsigned long vert_size = 0;
+ uint8_t *vert = load_spv("vert.spv", "demos/compute/vert.spv", &vert_size);
+
+ unsigned long frag_size = 0;
+ uint8_t *frag = load_spv("frag.spv", "demos/compute/frag.spv", &frag_size);
+
+ uint32_t seed = 0xC0FFEE;
+
+ Particle *initial_particles = malloc(PARTICLE_COUNT * sizeof *initial_particles);
+ for (int i = 0; i < PARTICLE_COUNT; i++) {
+ initial_particles[i].pos[0] = cool_beans(&seed) * 2.0f - 1.0f;
+ initial_particles[i].pos[1] = cool_beans(&seed) * 2.0f - 1.0f;
+ initial_particles[i].vel[0] = cool_beans(&seed) * 0.4f - 0.2f;
+ initial_particles[i].vel[1] = cool_beans(&seed) * 0.4f - 0.2f;
+ }
+
+ size_t buffer_size = PARTICLE_COUNT * sizeof(Particle);
+ RendBuffer particle_buf = rend_buffer_create(renderer, buffer_size, REND_BUFFER_STORAGE, true);
+ rend_buffer_write(renderer, &particle_buf, initial_particles, buffer_size, 0);
+
+ size_t grid_buffer_size = GRID_WIDTH * GRID_HEIGHT * sizeof(uint32_t);
+ uint32_t *initial_grid = calloc(GRID_WIDTH * GRID_HEIGHT, sizeof(uint32_t));
+
+ for (int y = 0; y < 100; y++) {
+ for (int x = 150; x < 362; x++) {
+ initial_grid[y * GRID_WIDTH + x] = 1; // 1 = sand
+ }
+ }
+
+ RendBuffer src_buf = rend_buffer_create(renderer, GRID_WIDTH * GRID_HEIGHT * sizeof(uint32_t), REND_BUFFER_STORAGE, false);
+ RendBuffer dst_buf = rend_buffer_create(renderer, GRID_WIDTH * GRID_HEIGHT * sizeof(uint32_t), REND_BUFFER_STORAGE, false);
+ rend_buffer_write(renderer, &src_buf, initial_grid, grid_buffer_size, 0);
+ rend_buffer_write(renderer, &dst_buf, initial_grid, grid_buffer_size, 0);
+
+ uint64_t particles_address = rend_buffer_address(&particle_buf);
+
+ RendBuffer *src_grid = &src_buf;
+ RendBuffer *dst_grid = &dst_buf;
+
+ RendPushConstantInfo pc = { .offset = 0, .size = sizeof(PushConstants) };
+ RendPipeline particle_compute = rend_pipeline_create_compute_spirv(renderer, particles_shader, particles_bytes, &pc, 1);
+ RendPipeline sand_compute = rend_pipeline_create_compute_spirv(renderer, sand_shader, sand_bytes, &pc, 1);
+ free(particles_shader);
+ free(sand_shader);
+
+ RendPipeline graphics_pipeline = rend_pipeline_create_graphics_bindless_spirv(renderer, vert, vert_size, frag, frag_size, &pc, 1, REND_POLYGON_MODE_FILL, REND_CULL_MODE_NONE, REND_TOPOLOGY_TRIANGLE_LIST, 0, true);
+
+ free(vert);
+ free(frag);
+
+ float dt = 1.0f / 60.0f;
+ uint64_t dt_ns = (uint64_t) (dt * NANOS_PER_SEC);
+
+ uint32_t mode = 2;
+ while (mode > 0) {
+
+ uint64_t start = peak_get_time();
+
+ PeakEvent ev;
+ while (peak_window_epoll(&win, &ev)) {
+ if (ev.type == PEAK_EVENT_WINDOW_CLOSE) {
+ mode -= 1;
+ break;
+ }
+ }
+
+ if (rend_renderer_frame_begin(renderer)) {
+
+ PushConstants pc = {0};
+
+ if (mode == 2) {
+ /*
+ * Sand Simulation
+ */
+ pc.delta_time = dt;
+ pc.width = GRID_WIDTH;
+ pc.height = GRID_HEIGHT;
+ pc.render_mode = 1;
+ pc.src_grid = src_grid->gpu_address;
+ pc.dst_grid = dst_grid->gpu_address;
+
+ rend_pipeline_bind(sand_compute);
+ rend_pipeline_push_constants(sand_compute, &pc, sizeof(pc));
+
+ uint32_t x = (GRID_WIDTH + 15) / 16;
+ uint32_t y = (GRID_HEIGHT + 15) / 16;
+ rend_pipeline_dispatch(sand_compute, x, y, 1);
+
+ RendBuffer *temp = src_grid;
+ src_grid = dst_grid;
+ dst_grid = temp;
+
+ memset(src_grid->mapped_memory, 0, grid_buffer_size);
+
+ } else {
+ /*
+ * Particles Simulation
+ */
+ pc.particles = particles_address;
+ pc.delta_time = dt;
+ pc.particle_count = PARTICLE_COUNT;
+ pc.render_mode = 0;
+
+ rend_pipeline_bind(particle_compute);
+ rend_pipeline_push_constants(particle_compute, &pc, sizeof(pc));
+ rend_pipeline_dispatch(particle_compute, (PARTICLE_COUNT + 255) / 256, 1, 1);
+ }
+
+ rend_renderer_render_pass_begin(renderer, 0.5f, 0.5f, 0.5f, 0.0f); {
+ rend_pipeline_bind(graphics_pipeline);
+ rend_pipeline_push_constants(graphics_pipeline, &pc, sizeof(pc));
+
+ uint32_t vertex_count = (mode == 2) ? (6 * GRID_WIDTH * GRID_HEIGHT) : (6 * PARTICLE_COUNT);
+ rend_pipeline_draw(graphics_pipeline, vertex_count, 1);
+
+ } rend_renderer_render_pass_end(renderer);
+
+ rend_renderer_frame_end(renderer, NULL);
+ }
+
+ uint64_t delta_ns = peak_get_time() - start;
+ if (delta_ns < dt_ns) {
+ peak_sleep_ns(dt_ns - delta_ns);
+ }
+ }
+
+ free(initial_particles);
+ free(initial_grid);
+
+ rend_buffer_destroy(&particle_buf);
+ rend_buffer_destroy(&src_buf);
+ rend_buffer_destroy(&dst_buf);
+
+ rend_renderer_destroy(renderer);
+ // rend_quit();
+
+ peak_window_close(&win);
+ peak_quit();
+
+ peak_debug_memory_report();
+ return 0;
+}
diff --git a/demos/compute/rend_compute.slang b/demos/compute/rend_compute.slang
new file mode 100644
index 0000000..5fb124b
--- /dev/null
+++ b/demos/compute/rend_compute.slang
@@ -0,0 +1,146 @@
+struct Particle {
+ float2 position;
+ float2 velocity;
+};
+
+struct PushConstants {
+ Particle* particles;
+ uint* srcGrid;
+ uint* dstGrid;
+ uint particleCount;
+ uint width;
+ uint height;
+ float deltaTime;
+ uint renderMode; // 0 = particles, 1 = sand
+};
+
+struct VSOutput {
+ float4 position : SV_Position;
+ float4 color : COLOR0;
+};
+
+[[vk::push_constant]]
+PushConstants pc;
+
+uint getIndex(uint x, uint y) {
+ return y * pc.width + x;
+}
+
+bool isEmpty(uint x, uint y) {
+ if (x >= pc.width || y >= pc.height) return false;
+ return pc.srcGrid[getIndex(x, y)] == 0;
+}
+
+[shader("compute")]
+[numthreads(256, 1, 1)]
+void particleMain(uint3 threadId : SV_DispatchThreadID) {
+ uint index = threadId.x;
+ if (index >= pc.particleCount) return;
+
+ Particle p = pc.particles[index];
+
+ p.position += p.velocity * pc.deltaTime;
+ p.velocity *= exp(-0.005 * pc.deltaTime);
+
+ pc.particles[index] = p;
+}
+
+[shader("compute")]
+[numthreads(16, 16, 1)]
+void sandMain(uint3 threadId : SV_DispatchThreadID) {
+ uint x = threadId.x;
+ uint y = threadId.y;
+
+ if (x >= pc.width || y >= pc.height) return;
+
+ uint currentIdx = getIndex(x, y);
+ uint cellState = pc.srcGrid[currentIdx];
+
+ if (cellState == 1) { // Sand
+ if (y + 1 < pc.height && isEmpty(x, y + 1)) {
+ pc.dstGrid[getIndex(x, y + 1)] = 1;
+ pc.dstGrid[currentIdx] = 0;
+ return;
+ }
+
+ bool fallLeftFirst = ((x + y) % 2) == 0;
+ int dir1 = fallLeftFirst ? -1 : 1;
+ int dir2 = fallLeftFirst ? 1 : -1;
+
+ if (y + 1 < pc.height && isEmpty(x + dir1, y + 1)) {
+ pc.dstGrid[getIndex(x + dir1, y + 1)] = 1;
+ pc.dstGrid[currentIdx] = 0;
+ return;
+ }
+ else if (y + 1 < pc.height && isEmpty(x + dir2, y + 1)) {
+ pc.dstGrid[getIndex(x + dir2, y + 1)] = 1;
+ pc.dstGrid[currentIdx] = 0;
+ return;
+ }
+
+ pc.dstGrid[currentIdx] = 1;
+ }
+}
+
+static const float2 QUAD_OFFSETS[6] = {
+ float2(-0.5, -0.5), float2( 0.5, -0.5), float2(-0.5, 0.5),
+ float2(-0.5, 0.5), float2( 0.5, -0.5), float2( 0.5, 0.5)
+};
+
+float3 cosinePalette(float t, float3 a, float3 b, float3 c, float3 d) {
+ return a + b * cos(6.28318 * (c * t + d));
+}
+
+[shader("vertex")]
+VSOutput vertMain(uint vertexID : SV_VertexID) {
+ VSOutput output;
+
+ uint elementIdx = vertexID / 6;
+ uint cornerIdx = vertexID % 6;
+
+ float2 worldPos = float2(0.0, 0.0);
+ float3 color = float3(0.0, 0.0, 0.0);
+
+ if (pc.renderMode == 0) {
+ Particle p = pc.particles[elementIdx];
+ worldPos = p.position;
+
+ float speed = length(p.velocity);
+ color = cosinePalette(
+ speed * 0.8,
+ float3(0.5, 0.5, 0.5),
+ float3(0.5, 0.5, 0.5),
+ float3(1.0, 1.0, 1.0),
+ float3(0.0, 0.33, 0.67)
+ );
+ }
+
+ else {
+ uint x = elementIdx % pc.width;
+ uint y = elementIdx / pc.width;
+
+ uint cellState = pc.srcGrid[elementIdx];
+
+ if (cellState == 0) {
+ output.position = float4(0, 0, 0, 0);
+ output.color = float4(0, 0, 0, 0);
+ return output;
+ }
+
+ worldPos.x = ((float)x / (float)pc.width) * 2.0 - 1.0;
+ worldPos.y = ((float)y / (float)pc.height) * 2.0 - 1.0;
+
+ color = float3(0.94, 0.82, 0.53);
+ }
+
+ float2 quadOffset = QUAD_OFFSETS[cornerIdx] * 0.01;
+ output.position = float4(worldPos + quadOffset, 0.0, 1.0);
+ output.color = float4(color, 1.0);
+
+ return output;
+}
+
+[shader("fragment")]
+float4 fragMain(VSOutput input) : SV_Target {
+ return input.color;
+}
diff --git a/demos/compute/rend_compute_demo b/demos/compute/rend_compute_demo
index 1aecc00..46734c6 100755
--- a/demos/compute/rend_compute_demo
+++ b/demos/compute/rend_compute_demo
Binary files differ
diff --git a/demos/compute/rend_compute_fast b/demos/compute/rend_compute_fast
deleted file mode 100755
index d9583f6..0000000
--- a/demos/compute/rend_compute_fast
+++ /dev/null
Binary files differ