From 446ad080e1d00246a2245ebc3ea29fec24be7705 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Fri, 2 May 2025 11:18:13 -0500 Subject: [PATCH] render sprite geometry --- scripts/modules/lcdsprite.js | 5 +++ scripts/modules/sdl_render.js | 3 +- source/jsffi.c | 81 ++++++++++++++++++++++++++++++++--- tests/bunnymark.js | 50 ++++++++++++++++++--- 4 files changed, 124 insertions(+), 15 deletions(-) diff --git a/scripts/modules/lcdsprite.js b/scripts/modules/lcdsprite.js index f5741058..418f9b19 100644 --- a/scripts/modules/lcdsprite.js +++ b/scripts/modules/lcdsprite.js @@ -57,4 +57,9 @@ sprite.geometry = function() return graphics.make_sprite_mesh(sprites) } +sprite.queue = function() +{ + return graphics.make_sprite_queue(sprites) +} + return sprite diff --git a/scripts/modules/sdl_render.js b/scripts/modules/sdl_render.js index ef3fe855..9d59a8d6 100644 --- a/scripts/modules/sdl_render.js +++ b/scripts/modules/sdl_render.js @@ -29,12 +29,11 @@ render.initialize = function(config) } prosperon.window = prosperon.engine_start({width:500,height:500}) - context = prosperon.window.make_renderer() + context = prosperon.window.make_renderer("vulkan") } render.sprite = function(sprite) { - context.sprite(sprite) } diff --git a/source/jsffi.c b/source/jsffi.c index 90445fbf..924d7ed1 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -2880,7 +2880,6 @@ JSC_CCALL(renderer_rects, return JS_ThrowReferenceError(js, "SDL_RenderFillRects: %s", SDL_GetError()); ) - // Should take a single struct with pos, color, uv, and indices arrays JSC_CCALL(renderer_geometry, renderer_ctx *ctx = js2renderer_ctx(js,self); @@ -2890,7 +2889,7 @@ JSC_CCALL(renderer_geometry, JSValue uv = JS_GetPropertyStr(js,argv[1], "uv"); JSValue indices = JS_GetPropertyStr(js,argv[1], "indices"); int vertices = js_getnum_str(js, argv[1], "vertices"); - int count = js_getnum_str(js, argv[1], "count"); + int count = js_getnum_str(js, argv[1], "num_indices"); size_t pos_stride, indices_stride, uv_stride, color_stride; void *posdata = get_gpu_buffer(js,pos, &pos_stride, NULL); @@ -2908,7 +2907,7 @@ JSC_CCALL(renderer_geometry, trans_pos[i].x = p.x; trans_pos[i].y = p.y; } - + if (!SDL_RenderGeometryRaw(r, tex, trans_pos, pos_stride,colordata,color_stride,uvdata, uv_stride, vertices, idxdata, count, indices_stride)) ret = JS_ThrowReferenceError(js, "Error rendering geometry: %s",SDL_GetError()); @@ -2920,6 +2919,73 @@ JSC_CCALL(renderer_geometry, JS_FreeValue(js,indices); ) +JSC_CCALL(renderer_geometry2, + renderer_ctx *ctx = js2renderer_ctx(js, self); + SDL_Renderer *r = ctx->sdl; + + SDL_Texture *tex = js2SDL_Texture(js, argv[0]); + + JSValue geo = argv[1]; + int vertices = js_getnum_str(js, geo, "vertices"); + int count = js_getnum_str(js, geo, "num_indices"); + + JSValue pos_v = JS_GetPropertyStr(js, geo, "pos"); + JSValue color_v = JS_GetPropertyStr(js, geo, "color"); + JSValue uv_v = JS_GetPropertyStr(js, geo, "uv"); + JSValue idx_v = JS_GetPropertyStr(js, geo, "indices"); + + size_t pos_stride, color_stride, uv_stride, idx_stride; + void *pos_data = get_gpu_buffer(js, pos_v, &pos_stride, NULL); + void *color_data = get_gpu_buffer(js, color_v, &color_stride, NULL); + void *uv_data = get_gpu_buffer(js, uv_v, &uv_stride, NULL); + void *idx_data = get_gpu_buffer(js, idx_v, &idx_stride, NULL); + + SDL_Vertex *verts = malloc(vertices * sizeof *verts); + for(int i = 0; i < vertices; ++i) { + const float *p = (const float *)((uint8_t *)pos_data + i * pos_stride); + const float *u = (const float *)((uint8_t *)uv_data + i * uv_stride); + const float *c = (const float *)((uint8_t *)color_data + i * color_stride); + + SDL_FPoint screen = renderer_world_to_screen(ctx, + (HMM_Vec2){ .x = p[0], .y = p[1] }); + + verts[i].position = screen; + verts[i].tex_coord = (SDL_FPoint){ u[0], u[1] }; + verts[i].color = (SDL_FColor){ c[0], c[1], c[2], c[3] }; + } + + const int *indices32 = NULL; + int *tmp_idx = NULL; + + if(idx_data && count) { + if(idx_stride == 4) indices32 = (const int *)idx_data; + else { + tmp_idx = malloc(count * sizeof *tmp_idx); + if(idx_stride == 2) { + const uint16_t *src = idx_data; + for(int i = 0; i < count; ++i) tmp_idx[i] = src[i]; + } else { /* 8-bit */ + const uint8_t *src = idx_data; + for(int i = 0; i < count; ++i) tmp_idx[i] = src[i]; + } + indices32 = tmp_idx; + } + } + + printf("num verts, num indices: %d, %d\n", vertices, count); + + if(!SDL_RenderGeometry(r, tex, verts, vertices, indices32, count)) + printf("Error rendering geometry: %s\n", SDL_GetError()); + + free(tmp_idx); + free(verts); + + JS_FreeValue(js, pos_v); + JS_FreeValue(js, color_v); + JS_FreeValue(js, uv_v); + JS_FreeValue(js, idx_v); +) + JSC_CCALL(renderer_logical_size, SDL_Renderer *r = js2renderer_ctx(js,self)->sdl; HMM_Vec2 v = js2vec2(js,argv[0]); @@ -3119,6 +3185,7 @@ static const JSCFunctionListEntry js_renderer_ctx_funcs[] = { MIST_FUNC_DEF(renderer, texture, 5), MIST_FUNC_DEF(renderer, rects, 1), MIST_FUNC_DEF(renderer, geometry, 2), + MIST_FUNC_DEF(renderer, geometry2, 2), MIST_FUNC_DEF(renderer, sprite, 1), MIST_FUNC_DEF(renderer, load_texture, 1), MIST_FUNC_DEF(renderer, get_image, 1), @@ -4074,10 +4141,10 @@ JSC_CCALL(gpu_make_sprite_queue, int needfree = 1; int sort = js2number(js,argv[3]); - // test for fastest - size_t size; - sprites = JS_GetArrayBuffer(js,&size,argv[0]); - if (sprites) { + if (JS_IsArrayBuffer(js, argv[0])) { + // test for fastest + size_t size; + sprite *sprites = JS_GetArrayBuffer(js, &size, argv[0]); quads = size/sizeof(*sprites); needfree = 0; for (int i = 0; i < quads; i++) diff --git a/tests/bunnymark.js b/tests/bunnymark.js index b9dd8e05..a6ddc3a6 100644 --- a/tests/bunnymark.js +++ b/tests/bunnymark.js @@ -33,18 +33,43 @@ var center = [0.5,0.5] var vel = 50 -for (var i = 0; i < 20000; i++) { - var sp = sprite.create(bunny, [Math.random()*dim.x, Math.random()*dim.y], center) +function hsl_to_rgb(h, s, l) { + var c = (1 - Math.abs(2 * l - 1)) * s + var x = c * (1 - Math.abs((h / 60) % 2 - 1)) + var m = l - c / 2 + var r = 0, g = 0, b = 0 + + if (h < 60) { r = c; g = x } + else if (h < 120) { r = x; g = c } + else if (h < 180) { g = c; b = x } + else if (h < 240) { g = x; b = c } + else if (h < 300) { r = x; b = c } + else { r = c; b = x } + + return [r + m, g + m, b + m, 1] // 0‒1 floats, alpha = 1 +} + +var bunny_count = 2000 + +for (var i = 0; i < bunny_count; i++) { + var pct = i/bunny_count + var sp = sprite.create(bunny, [Math.random()*dim.x*pct, Math.random()*dim.y*pct], center) sp.dir = [Math.random()*vel, Math.random()*vel] + var hue = 270 * i / bunny_count + sp.color = hsl_to_rgb(hue, 0.5, 0.5) } function movendraw(sp) { -// sp.move([sp.dir[0]*dt, sp.dir[1]*dt]) - sp.draw() + sp.move([sp.dir[0]*dt, sp.dir[1]*dt]) +// sp.draw() } var dt = 0 +var fps_samples = [] +var fps_window = 10 +var fps_update_period = 0.5 +var last_fps_update = os.now() function loop() { var now = os.now() @@ -52,11 +77,24 @@ function loop() render.camera(camera) // sprite.forEach(movendraw) - var mesh = sprite.geometry() - render.geometry(bunny.texture, mesh) + var queue = sprite.queue() + + for (var q of queue) + render.geometry(q.image.texture, q.mesh) render.present() dt = os.now() - now + + fps_samples.push(dt) + if (fps_samples.length > fps_window) fps_samples.shift() + + if (now - last_fps_update >= fps_update_period) { + var sum = 0 + for (var i = 0; i < fps_samples.length; i++) sum += fps_samples[i] + prosperon.window.title = `Bunnymark [fps: ${(fps_samples.length/sum).toFixed(1)}]`; + last_fps_update = now + } + var delay = (1/60) - dt $_.delay(loop, delay) }