From a204fce4b505de47b8c9fe86c4f716cc18a1f68f Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Thu, 22 May 2025 11:48:27 -0500 Subject: [PATCH] initial refactor --- examples/chess/Makefile | 3 - meson.build | 2 +- source/jsffi.c | 5325 ++++----------------------------------- source/jsffi.h | 72 + source/prosperon.c | 8 +- source/qjs_geometry.c | 447 ++++ source/qjs_geometry.h | 8 + source/qjs_imgui.cpp | 23 +- source/qjs_io.c | 382 +++ source/qjs_io.h | 8 + source/qjs_macros.h | 34 +- source/qjs_math.c | 509 ++++ source/qjs_math.h | 8 + source/qjs_os.c | 489 ++++ source/qjs_os.h | 8 + source/qjs_renderer.c | 32 +- source/qjs_sdl.c | 476 ++++ source/qjs_sdl.h | 10 + source/qjs_sdl_gpu.c | 2481 ++++++++++++++++++ source/qjs_sdl_gpu.h | 38 + source/qjs_sprite.c | 64 + source/qjs_sprite.h | 12 + source/qjs_transform.c | 212 ++ source/qjs_transform.h | 8 + 24 files changed, 5802 insertions(+), 4857 deletions(-) delete mode 100644 examples/chess/Makefile create mode 100644 source/qjs_geometry.c create mode 100644 source/qjs_geometry.h create mode 100644 source/qjs_io.c create mode 100644 source/qjs_io.h create mode 100644 source/qjs_math.c create mode 100644 source/qjs_math.h create mode 100644 source/qjs_os.c create mode 100644 source/qjs_os.h create mode 100644 source/qjs_sdl.c create mode 100644 source/qjs_sdl.h create mode 100644 source/qjs_sdl_gpu.c create mode 100644 source/qjs_sdl_gpu.h create mode 100644 source/qjs_sprite.c create mode 100644 source/qjs_sprite.h create mode 100644 source/qjs_transform.c create mode 100644 source/qjs_transform.h diff --git a/examples/chess/Makefile b/examples/chess/Makefile deleted file mode 100644 index 73a32507..00000000 --- a/examples/chess/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -def: - cd _prosperon && make && cp build_dbg/prosperon .. - ./prosperon \ No newline at end of file diff --git a/meson.build b/meson.build index bc2e26a5..b85abbf7 100644 --- a/meson.build +++ b/meson.build @@ -147,7 +147,7 @@ sources = [] src += [ 'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c', 'render.c','simplex.c','spline.c', 'transform.c','prosperon.c', 'wildmatch.c', - 'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', + 'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_os.c', 'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c' ] # quirc src diff --git a/source/jsffi.c b/source/jsffi.c index 1d4bece0..da8a7e52 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -1,6 +1,10 @@ #include "jsffi.h" #include "font.h" #include "datastream.h" +#include "qjs_sdl.h" +#include "qjs_sdl_gpu.h" +#include "qjs_io.h" +#include "transform.h" #include "stb_ds.h" #include "stb_image.h" #include "stb_rect_pack.h" @@ -33,6 +37,16 @@ #include "qjs_enet.h" #include "qjs_soloud.h" #include "qjs_qr.h" +#include "qjs_sdl.h" +#include "qjs_math.h" +#include "qjs_geometry.h" +#include "qjs_transform.h" +#include "qjs_sprite.h" +#include "qjs_io.h" +#include "qjs_sdl_gpu.h" +#include "qjs_os.h" + +SDL_Window *global_window; #include @@ -65,12 +79,14 @@ typedef struct rtree rtree; #include #include -#define JS_SetProperty(js, tar, str, val) JS_SetPropertyStr(js, tar, #str, val) -#define JS_GetProperty(js, tar, atom) JS_GetPropertyStr(js, tar, #atom) - int randombytes(void *buf, size_t n); -static int trace = 0; +int trace = 0; + +// External transform function declarations +extern JSClassID js_transform_id; +JSValue transform2js(JSContext *js, transform *t); +transform *js2transform(JSContext *js, JSValue v); #ifdef __APPLE__ #include @@ -78,12 +94,14 @@ static int trace = 0; //#include #endif +// Random number generation constants #define UPPER_MASK 0x80000000 #define LOWER_MASK 0x7fffffff #define TEMPERING_MASK_B 0x9d2c5680 #define TEMPERING_MASK_C 0xefc60000 -inline static void m_seedRand(MTRand* rand, uint32_t seed) { +// Random number generation functions +void m_seedRand(MTRand* rand, uint32_t seed) { /* set initial seeds to mt[STATE_VECTOR_LENGTH] using the generator * from Line 25 of Table 1 in: Donald Knuth, "The Art of Computer * Programming," Vol. 2 (2nd Ed.) pp.102. @@ -95,7 +113,6 @@ inline static void m_seedRand(MTRand* rand, uint32_t seed) { } uint32_t genRandLong(MTRand* rand) { - uint32_t y; static uint32_t mag[2] = {0x0, 0x9908b0df}; /* mag[x] = x * 0x9908b0df for x = 0,1 */ if(rand->index >= STATE_VECTOR_LENGTH || rand->index < 0) { @@ -128,6 +145,12 @@ double genRand(MTRand* rand) { return((double)genRandLong(rand) / (uint32_t)0xffffffff); } +double rand_range(JSContext *js, double min, double max) +{ + MTRand *mrand = &((prosperon_rt*)JS_GetContextOpaque(js))->mrand; + return genRand(mrand) * (max-min)+min; +} + typedef struct texture_vertex { float x, y, z; float u, v; @@ -182,7 +205,7 @@ JSValue val = JS_GetPropertyUint32(JS,VAL,I); \ TO = js2##TYPE(JS, val); \ JS_FreeValue(JS, val); } \ -static inline int js2bool(JSContext *js, JSValue v) { return JS_ToBool(js,v);} +int js2bool(JSContext *js, JSValue v) { return JS_ToBool(js,v); } static inline const char *js2cstring(JSContext *js, JSValue v) { return JS_ToCString(js,v); } JSValue number2js(JSContext *js, double g) { return JS_NewFloat64(js,g); } @@ -278,710 +301,69 @@ void *get_gpu_buffer(JSContext *js, JSValue argv, size_t *stride, size_t *size) return data; } -typedef struct { - JSValue val; - void *ptr; - size_t size; - int need_new; -} BufferCheckResult; +JSValue make_quad_indices_buffer(JSContext *js, int quads) +{ + prosperon_rt *rt = JS_GetContextOpaque(js); + int count = quads*6; + if (!JS_IsUndefined(rt->idx_buffer) && rt->idx_count >= count) + return JS_DupValue(js,rt->idx_buffer); -static BufferCheckResult get_or_extend_buffer( - JSContext *js, - JSValue old_mesh, - const char *prop, - size_t needed_size, - int type, - int elements_per_item, - int copy, - int index -) { - BufferCheckResult res = { JS_UNDEFINED, NULL, 0, 0 }; - if (!JS_IsUndefined(old_mesh)) { - JSValue old_buf = JS_GetProperty(js, old_mesh, prop); - if (!JS_IsUndefined(old_buf)) { - size_t old_size; - void *data = get_gpu_buffer(js, old_buf, NULL, &old_size); - if (data && old_size >= needed_size) { - // Old buffer is large enough - res.val = old_buf; // keep it - res.ptr = data; - res.size = old_size; - return res; - } - JS_FreeValue(js, old_buf); - } + int verts = quads*4; + uint16_t *indices = malloc(sizeof(*indices)*count); + for (int i = 0, v = 0; v < verts; i +=6, v += 4) { + indices[i] = v; + indices[i+1] = v+2; + indices[i+2] = v+1; + indices[i+3] = v+2; + indices[i+4] = v+3; + indices[i+5] = v+1; } - // If we reach here, we need a new buffer - res.need_new = 1; - return res; + + if (!JS_IsUndefined(rt->idx_buffer)) + JS_FreeValue(js,rt->idx_buffer); + + rt->idx_buffer = make_gpu_buffer(js,indices, sizeof(*indices)*count, JS_TYPED_ARRAY_UINT16, 1,0,1); + rt->idx_count = count; + return JS_DupValue(js,rt->idx_buffer); +} + +JSValue quads_to_mesh(JSContext *js, text_vert *buffer) +{ + size_t verts = arrlen(buffer); + + HMM_Vec2 *pos = malloc(arrlen(buffer)*sizeof(HMM_Vec2)); + HMM_Vec2 *uv = malloc(arrlen(buffer)*sizeof(HMM_Vec2)); + HMM_Vec4 *color = malloc(arrlen(buffer)*sizeof(HMM_Vec4)); + + for (int i = 0; i < arrlen(buffer); i++) { + pos[i] = buffer[i].pos; + uv[i] = buffer[i].uv; + color[i] = buffer[i].color; + } + + JSValue jspos = make_gpu_buffer(js, pos, sizeof(HMM_Vec2)*arrlen(buffer), JS_TYPED_ARRAY_FLOAT32, 2,0,0); + JSValue jsuv = make_gpu_buffer(js, uv, sizeof(HMM_Vec2)*arrlen(buffer), JS_TYPED_ARRAY_FLOAT32, 2,0,0); + JSValue jscolor = make_gpu_buffer(js, color, sizeof(HMM_Vec4)*arrlen(buffer), JS_TYPED_ARRAY_FLOAT32, 4,0,0); + + size_t quads = verts/4; + size_t count = verts/2*3; + JSValue jsidx = make_quad_indices_buffer(js, quads); + + JSValue ret = JS_NewObject(js); + JS_SetPropertyStr(js, ret, "pos", jspos); + JS_SetPropertyStr(js, ret, "uv", jsuv); + JS_SetPropertyStr(js, ret, "color", jscolor); + JS_SetPropertyStr(js, ret, "indices", jsidx); + JS_SetPropertyStr(js, ret, "vertices", number2js(js, verts)); + JS_SetPropertyStr(js,ret,"num_indices", number2js(js,count)); + + return ret; } #ifndef _WIN32 #include #endif -struct lrtb { - float l; - float r; - float t; - float b; -}; - -static SDL_GPUDevice *global_gpu; -static SDL_Window *global_window; - -SDL_GPUGraphicsPipelineTargetInfo js2SDL_GPUGraphicsPipelineTargetInfo(JSContext *js, JSValue v) -{ - SDL_GPUGraphicsPipelineTargetInfo info = {0}; - return info; -} - -SDL_GPUSampleCount js2SDL_GPUSampleCount(JSContext *js, JSValue v) -{ - int n = js2number(js,v); - switch(n) { - case 1: return SDL_GPU_SAMPLECOUNT_1; - case 2: return SDL_GPU_SAMPLECOUNT_2; - case 4: return SDL_GPU_SAMPLECOUNT_4; - case 8: return SDL_GPU_SAMPLECOUNT_8; - } - return -1; -} - -#define JS2ENUM(NAME, RETS, VALS) \ -int js2##NAME(JSContext *js, JSValue v) { \ - if (JS_IsUndefined(v)) return 0; \ - const char *str = JS_ToCString(js, v); \ - int *rets = (RETS); \ - const char **vals = (VALS); \ - /* Compute how many entries are in the arrays */ \ - int n = (int)(sizeof((RETS)) / sizeof((RETS)[0])); \ - for(int i = 0; i < n; i++) \ - if(!strcmp(vals[i], str)) { \ - JS_FreeCString(js, str); \ - return rets[i]; \ - } \ - JS_FreeCString(js, str); \ - return 0; \ -} - -static int rets_SDL_GPUSwapchainComposition[] = { - SDL_GPU_SWAPCHAINCOMPOSITION_SDR, - SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR, - SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR, -// SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084 -}; - -static const char *vals_SDL_GPUSwapchainComposition[] = { - "sdr", - "linear", - "hdr", -// "hdr10" -}; - -JS2ENUM(SDL_GPUSwapchainComposition, rets_SDL_GPUSwapchainComposition, vals_SDL_GPUSwapchainComposition) - -static int rets_SDL_FlipMode[] = { - SDL_FLIP_NONE, - SDL_FLIP_HORIZONTAL, - SDL_FLIP_VERTICAL -}; - -static const char *vals_SDL_FlipMode[] = { - "none", - "horizontal", - "vertical" -}; - -JS2ENUM(SDL_FlipMode, rets_SDL_FlipMode, vals_SDL_FlipMode) - -/* ------------------------------------------------------- - 1) SDL_GPUBlendFactor - ------------------------------------------------------- */ - -static int rets_SDL_GPUBlendFactor[] = { - SDL_GPU_BLENDFACTOR_INVALID, - SDL_GPU_BLENDFACTOR_ZERO, - SDL_GPU_BLENDFACTOR_ONE, - SDL_GPU_BLENDFACTOR_SRC_COLOR, - SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_COLOR, - SDL_GPU_BLENDFACTOR_DST_COLOR, - SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_COLOR, - SDL_GPU_BLENDFACTOR_SRC_ALPHA, - SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, - SDL_GPU_BLENDFACTOR_DST_ALPHA, - SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_ALPHA, - SDL_GPU_BLENDFACTOR_CONSTANT_COLOR, - SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR, - SDL_GPU_BLENDFACTOR_SRC_ALPHA_SATURATE -}; -static const char *vals_SDL_GPUBlendFactor[] = { - "invalid", - "zero", - "one", - "src_color", - "one_minus_src_color", - "dst_color", - "one_minus_dst_color", - "src_alpha", - "one_minus_src_alpha", - "dst_alpha", - "one_minus_dst_alpha", - "constant_color", - "one_minus_constant_color", - "src_alpha_saturate" -}; - -JS2ENUM(SDL_GPUBlendFactor, rets_SDL_GPUBlendFactor, vals_SDL_GPUBlendFactor) - -/* ------------------------------------------------------- - 2) SDL_GPUBlendOp - ------------------------------------------------------- */ -static int rets_SDL_GPUBlendOp[] = { - SDL_GPU_BLENDOP_INVALID, - SDL_GPU_BLENDOP_ADD, - SDL_GPU_BLENDOP_SUBTRACT, - SDL_GPU_BLENDOP_REVERSE_SUBTRACT, - SDL_GPU_BLENDOP_MIN, - SDL_GPU_BLENDOP_MAX -}; -static const char *vals_SDL_GPUBlendOp[] = { - "invalid", - "add", - "subtract", - "reverse_subtract", - "min", - "max" -}; - -JS2ENUM(SDL_GPUBlendOp, rets_SDL_GPUBlendOp, vals_SDL_GPUBlendOp) - -/* ------------------------------------------------------- - 3) SDL_GPUCompareOp - ------------------------------------------------------- */ -static int rets_SDL_GPUCompareOp[] = { - SDL_GPU_COMPAREOP_INVALID, - SDL_GPU_COMPAREOP_NEVER, - SDL_GPU_COMPAREOP_LESS, - SDL_GPU_COMPAREOP_EQUAL, - SDL_GPU_COMPAREOP_LESS_OR_EQUAL, - SDL_GPU_COMPAREOP_GREATER, - SDL_GPU_COMPAREOP_NOT_EQUAL, - SDL_GPU_COMPAREOP_GREATER_OR_EQUAL, - SDL_GPU_COMPAREOP_ALWAYS -}; -static const char *vals_SDL_GPUCompareOp[] = { - "invalid", - "never", - "less", - "equal", - "less_or_equal", - "greater", - "not_equal", - "greater_or_equal", - "always" -}; - -JS2ENUM(SDL_GPUCompareOp, rets_SDL_GPUCompareOp, vals_SDL_GPUCompareOp) - -/* ------------------------------------------------------- - 4) SDL_GPUCullMode - ------------------------------------------------------- */ -static int rets_SDL_GPUCullMode[] = { - SDL_GPU_CULLMODE_NONE, - SDL_GPU_CULLMODE_FRONT, - SDL_GPU_CULLMODE_BACK -}; -static const char *vals_SDL_GPUCullMode[] = { - "none", - "front", - "back" -}; - -JS2ENUM(SDL_GPUCullMode, rets_SDL_GPUCullMode, vals_SDL_GPUCullMode) - -/* ------------------------------------------------------- - 5) SDL_GPUFillMode - ------------------------------------------------------- */ -static int rets_SDL_GPUFillMode[] = { - SDL_GPU_FILLMODE_FILL, - SDL_GPU_FILLMODE_LINE -}; -static const char *vals_SDL_GPUFillMode[] = { - "fill", - "line" -}; - -JS2ENUM(SDL_GPUFillMode, rets_SDL_GPUFillMode, vals_SDL_GPUFillMode) - -/* ------------------------------------------------------- - 6) SDL_GPUFilter - ------------------------------------------------------- */ -static int rets_SDL_GPUFilter[] = { - SDL_GPU_FILTER_NEAREST, - SDL_GPU_FILTER_LINEAR -}; -static const char *vals_SDL_GPUFilter[] = { - "nearest", - "linear" -}; - -JS2ENUM(SDL_GPUFilter, rets_SDL_GPUFilter, vals_SDL_GPUFilter) - -/* ------------------------------------------------------- - 7) SDL_GPUFrontFace - ------------------------------------------------------- */ -static int rets_SDL_GPUFrontFace[] = { - SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE, - SDL_GPU_FRONTFACE_CLOCKWISE -}; -static const char *vals_SDL_GPUFrontFace[] = { - "counter_clockwise", - "clockwise" -}; - -JS2ENUM(SDL_GPUFrontFace, rets_SDL_GPUFrontFace, vals_SDL_GPUFrontFace) - -/* ------------------------------------------------------- - 8) SDL_GPULoadOp - ------------------------------------------------------- */ -static int rets_SDL_GPULoadOp[] = { - SDL_GPU_LOADOP_LOAD, - SDL_GPU_LOADOP_CLEAR, - SDL_GPU_LOADOP_DONT_CARE -}; -static const char *vals_SDL_GPULoadOp[] = { - "load", - "clear", - "dont_care" -}; - -JS2ENUM(SDL_GPULoadOp, rets_SDL_GPULoadOp, vals_SDL_GPULoadOp) - -/* ------------------------------------------------------- - 9) SDL_GPUPresentMode - ------------------------------------------------------- */ -static int rets_SDL_GPUPresentMode[] = { - SDL_GPU_PRESENTMODE_VSYNC, - SDL_GPU_PRESENTMODE_IMMEDIATE, - SDL_GPU_PRESENTMODE_MAILBOX -}; -static const char *vals_SDL_GPUPresentMode[] = { - "vsync", - "immediate", - "mailbox" -}; - -JS2ENUM(SDL_GPUPresentMode, rets_SDL_GPUPresentMode, vals_SDL_GPUPresentMode) - -/* ------------------------------------------------------- - 10) SDL_GPUPrimitiveType - ------------------------------------------------------- */ -static int rets_SDL_GPUPrimitiveType[] = { - SDL_GPU_PRIMITIVETYPE_TRIANGLELIST, - SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP, - SDL_GPU_PRIMITIVETYPE_LINELIST, - SDL_GPU_PRIMITIVETYPE_LINESTRIP, - SDL_GPU_PRIMITIVETYPE_POINTLIST -}; -static const char *vals_SDL_GPUPrimitiveType[] = { - "triangle", - "trianglestrip", - "line", - "linestrip", - "point" -}; - -JS2ENUM(SDL_GPUPrimitiveType, rets_SDL_GPUPrimitiveType, vals_SDL_GPUPrimitiveType) - -/* ------------------------------------------------------- - 11) SDL_GPUSamplerAddressMode - ------------------------------------------------------- */ -static int rets_SDL_GPUSamplerAddressMode[] = { - SDL_GPU_SAMPLERADDRESSMODE_REPEAT, - SDL_GPU_SAMPLERADDRESSMODE_MIRRORED_REPEAT, - SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE -}; -static const char *vals_SDL_GPUSamplerAddressMode[] = { - "repeat", - "mirrored_repeat", - "clamp" -}; - -JS2ENUM(SDL_GPUSamplerAddressMode, rets_SDL_GPUSamplerAddressMode, vals_SDL_GPUSamplerAddressMode) - -/* ------------------------------------------------------- - 12) SDL_GPUSamplerMipmapMode - ------------------------------------------------------- */ -static int rets_SDL_GPUSamplerMipmapMode[] = { - SDL_GPU_SAMPLERMIPMAPMODE_NEAREST, - SDL_GPU_SAMPLERMIPMAPMODE_LINEAR -}; -static const char *vals_SDL_GPUSamplerMipmapMode[] = { - "nearest", - "linear" -}; - -JS2ENUM(SDL_GPUSamplerMipmapMode, rets_SDL_GPUSamplerMipmapMode, vals_SDL_GPUSamplerMipmapMode) - -/* ------------------------------------------------------- - 13) SDL_GPUStencilOp - ------------------------------------------------------- */ -static int rets_SDL_GPUStencilOp[] = { - SDL_GPU_STENCILOP_INVALID, - SDL_GPU_STENCILOP_KEEP, - SDL_GPU_STENCILOP_ZERO, - SDL_GPU_STENCILOP_REPLACE, - SDL_GPU_STENCILOP_INCREMENT_AND_CLAMP, - SDL_GPU_STENCILOP_DECREMENT_AND_CLAMP, - SDL_GPU_STENCILOP_INVERT, - SDL_GPU_STENCILOP_INCREMENT_AND_WRAP, - SDL_GPU_STENCILOP_DECREMENT_AND_WRAP -}; -static const char *vals_SDL_GPUStencilOp[] = { - "invalid", - "keep", - "zero", - "replace", - "increment_and_clamp", - "decrement_and_clamp", - "invert", - "increment_and_wrap", - "decrement_and_wrap" -}; - -JS2ENUM(SDL_GPUStencilOp, rets_SDL_GPUStencilOp, vals_SDL_GPUStencilOp) - -static int rets_SDL_GPUTextureType[] = { - SDL_GPU_TEXTURETYPE_2D, /**< The texture is a 2-dimensional image. */ - SDL_GPU_TEXTURETYPE_2D_ARRAY, /**< The texture is a 2-dimensional array image. */ - SDL_GPU_TEXTURETYPE_3D, /**< The texture is a 3-dimensional image. */ - SDL_GPU_TEXTURETYPE_CUBE, /**< The texture is a cube image. */ - SDL_GPU_TEXTURETYPE_CUBE_ARRAY /**< The texture is a cube array image. */ -}; - -static const char *vals_SDL_GPUTextureType[] = { - "2d", - "2d array", - "3d", - "cube", - "cube array" -}; - -JS2ENUM(SDL_GPUTextureType, rets_SDL_GPUTextureType, vals_SDL_GPUTextureType) - -/* ------------------------------------------------------- - 14) SDL_GPUStoreOp - ------------------------------------------------------- */ -static int rets_SDL_GPUStoreOp[] = { - SDL_GPU_STOREOP_STORE, - SDL_GPU_STOREOP_DONT_CARE, - SDL_GPU_STOREOP_RESOLVE, - SDL_GPU_STOREOP_RESOLVE_AND_STORE -}; - -static const char *vals_SDL_GPUStoreOp[] = { - "store", - "dont_care", - "resolve", - "resolve_and_store" -}; - -JS2ENUM(SDL_GPUStoreOp, rets_SDL_GPUStoreOp, vals_SDL_GPUStoreOp) - -/* ------------------------------------------------------- - X) SDL_GPUTextureType - ------------------------------------------------------- */ -static int rets_SDL_GPUTextureFormat[] = { - SDL_GPU_TEXTUREFORMAT_INVALID, - - /* Unsigned Normalized Float Color Formats */ - SDL_GPU_TEXTUREFORMAT_A8_UNORM, - SDL_GPU_TEXTUREFORMAT_R8_UNORM, - SDL_GPU_TEXTUREFORMAT_R8G8_UNORM, - SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, - SDL_GPU_TEXTUREFORMAT_R16_UNORM, - SDL_GPU_TEXTUREFORMAT_R16G16_UNORM, - SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM, - SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM, - SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM, - SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM, - SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM, - SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, - /* Compressed Unsigned Normalized Float Color Formats */ - SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM, - SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM, - SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM, - SDL_GPU_TEXTUREFORMAT_BC4_R_UNORM, - SDL_GPU_TEXTUREFORMAT_BC5_RG_UNORM, - SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM, - /* Compressed Signed Float Color Formats */ - SDL_GPU_TEXTUREFORMAT_BC6H_RGB_FLOAT, - /* Compressed Unsigned Float Color Formats */ - SDL_GPU_TEXTUREFORMAT_BC6H_RGB_UFLOAT, - /* Signed Normalized Float Color Formats */ - SDL_GPU_TEXTUREFORMAT_R8_SNORM, - SDL_GPU_TEXTUREFORMAT_R8G8_SNORM, - SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM, - SDL_GPU_TEXTUREFORMAT_R16_SNORM, - SDL_GPU_TEXTUREFORMAT_R16G16_SNORM, - SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM, - /* Signed Float Color Formats */ - SDL_GPU_TEXTUREFORMAT_R16_FLOAT, - SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT, - SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, - SDL_GPU_TEXTUREFORMAT_R32_FLOAT, - SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT, - SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT, - /* Unsigned Float Color Formats */ - SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT, - /* Unsigned Integer Color Formats */ - SDL_GPU_TEXTUREFORMAT_R8_UINT, - SDL_GPU_TEXTUREFORMAT_R8G8_UINT, - SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT, - SDL_GPU_TEXTUREFORMAT_R16_UINT, - SDL_GPU_TEXTUREFORMAT_R16G16_UINT, - SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT, - SDL_GPU_TEXTUREFORMAT_R32_UINT, - SDL_GPU_TEXTUREFORMAT_R32G32_UINT, - SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT, - /* Signed Integer Color Formats */ - SDL_GPU_TEXTUREFORMAT_R8_INT, - SDL_GPU_TEXTUREFORMAT_R8G8_INT, - SDL_GPU_TEXTUREFORMAT_R8G8B8A8_INT, - SDL_GPU_TEXTUREFORMAT_R16_INT, - SDL_GPU_TEXTUREFORMAT_R16G16_INT, - SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT, - SDL_GPU_TEXTUREFORMAT_R32_INT, - SDL_GPU_TEXTUREFORMAT_R32G32_INT, - SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT, - /* SRGB Unsigned Normalized Color Formats */ - SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB, - /* Compressed SRGB Unsigned Normalized Color Formats */ - SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM_SRGB, - /* Depth Formats */ - SDL_GPU_TEXTUREFORMAT_D16_UNORM, - SDL_GPU_TEXTUREFORMAT_D24_UNORM, - SDL_GPU_TEXTUREFORMAT_D32_FLOAT, - SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT, - SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT, - /* Compressed ASTC Normalized Float Color Formats*/ - SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM, - SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM, - SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM, - SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM, - SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM, - SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM, - SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM, - SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM, - SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM, - SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM, - SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM, - SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM, - SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM, - SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM, - /* Compressed SRGB ASTC Normalized Float Color Formats*/ - SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM_SRGB, - SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM_SRGB, - /* Compressed ASTC Signed Float Color Formats*/ - SDL_GPU_TEXTUREFORMAT_ASTC_4x4_FLOAT, - SDL_GPU_TEXTUREFORMAT_ASTC_5x4_FLOAT, - SDL_GPU_TEXTUREFORMAT_ASTC_5x5_FLOAT, - SDL_GPU_TEXTUREFORMAT_ASTC_6x5_FLOAT, - SDL_GPU_TEXTUREFORMAT_ASTC_6x6_FLOAT, - SDL_GPU_TEXTUREFORMAT_ASTC_8x5_FLOAT, - SDL_GPU_TEXTUREFORMAT_ASTC_8x6_FLOAT, - SDL_GPU_TEXTUREFORMAT_ASTC_8x8_FLOAT, - SDL_GPU_TEXTUREFORMAT_ASTC_10x5_FLOAT, - SDL_GPU_TEXTUREFORMAT_ASTC_10x6_FLOAT, - SDL_GPU_TEXTUREFORMAT_ASTC_10x8_FLOAT, - SDL_GPU_TEXTUREFORMAT_ASTC_10x10_FLOAT, - SDL_GPU_TEXTUREFORMAT_ASTC_12x10_FLOAT, - SDL_GPU_TEXTUREFORMAT_ASTC_12x12_FLOAT -}; - -static const char *vals_SDL_GPUTextureFormat[] = { - "invalid", - "a8", - "r8", - "rg8", - "rgba8", - "r16", - "rg16", - "rgba16", - "r10g10b10a2", - "b5g6r5", - "b5g5r5a1", - "b4g4r4a4", - "b8g8r8a8", - - "bc1", - "bc2", - "bc3", - "bc4", - "bc5", - "bc7", - "bc6h float", - "bc6h ufloat", - - "r8 snorm", - "rg8 snorm", - "rgba8 snorm", - "r16 snorm", - "rg16 snorm", - "rgba16 snorm", - - "r16 float", - "rg16 float", - "rgba16 float", - "r32 float", - "rg32 float", - "rgba32 float", - - "r11g11b10", - - "r8 uint", - "rg8 uint", - "rgba8 uint", - "r16 uint", - "rg16 uint", - "rgba16 uint", - "r32 uint", - "rg32 uint", - "rgba32 uint", - - "r8 int", - "rg8 int", - "rgba8 int", - "r16 int", - "rg16 int", - "rgba16 int", - "r32 int", - "rg32 int", - "rgba32 int", - - "rgba8 srgb", - "b8g8r8a8 srgb", - "bc1 srgb", - "bc2 srgb", - "bc3 srgb", - "bc7 srgb", - - "d16", - "d24", - "d32 float", - "d24 s8", - "d32 float s8", - - "astc 4x4", - "astc 5x4", - "astc 5x5", - "astc 6x5", - "astc 6x6", - "astc 8x5", - "astc 8x6", - "astc 8x8", - "astc 10x5", - "astc 10x6", - "astc 10x8", - "astc 10x10", - "astc 12x10", - "astc 12x12", - - "astc 4x4 srgb", - "astc 5x4 srgb", - "astc 5x5 srgb", - "astc 6x5 srgb", - "astc 6x6 srgb", - "astc 8x5 srgb", - "astc 8x6 srgb", - "astc 8x8 srgb", - "astc 10x5 srgb", - "astc 10x6 srgb", - "astc 10x8 srgb", - "astc 10x10 srgb", - "astc 12x10 srgb", - "astc 12x12 srgb", - - "astc 4x4 float", - "astc 5x4 float", - "astc 5x5 float", - "astc 6x5 float", - "astc 6x6 float", - "astc 8x5 float", - "astc 8x6 float", - "astc 8x8 float", - "astc 10x5 float", - "astc 10x6 float", - "astc 10x8 float", - "astc 10x10 float", - "astc 12x10 float", - "astc 12x12 float" -}; - -JS2ENUM(SDL_GPUTextureFormat, rets_SDL_GPUTextureFormat, vals_SDL_GPUTextureFormat) - -SDL_GPUColorTargetBlendState js2SDL_GPUColorTargetBlendState(JSContext *js, JSValue v) -{ - SDL_GPUColorTargetBlendState state = {0}; - JS_GETPROP(js,state.src_color_blendfactor,v,src_rgb,SDL_GPUBlendFactor); - JS_GETPROP(js,state.dst_color_blendfactor,v,dst_rgb,SDL_GPUBlendFactor); - JS_GETPROP(js,state.src_alpha_blendfactor,v,src_alpha,SDL_GPUBlendFactor); - JS_GETPROP(js,state.dst_alpha_blendfactor,v,src_alpha,SDL_GPUBlendFactor); - JS_GETPROP(js,state.color_blend_op,v,op_rgb,SDL_GPUBlendOp) - JS_GETPROP(js,state.alpha_blend_op,v,op_alpha,SDL_GPUBlendOp) - JS_GETPROP(js,state.enable_blend,v,enabled,bool) - return state; -} - -SDL_GPUColorTargetDescription js2SDL_GPUColorTargetDescription(JSContext *js, JSValue v) -{ - SDL_GPUColorTargetDescription dsc = {0}; - JS_GETPROP(js,dsc.format,v,format,SDL_GPUTextureFormat) - JS_GETPROP(js,dsc.blend_state,v,blend,SDL_GPUColorTargetBlendState) - return dsc; -} - -SDL_GPUStencilOpState js2SDL_GPUStencilOpState(JSContext *js, JSValue v) -{ - SDL_GPUStencilOpState state; - memset(&state, 0, sizeof(state)); - - JSValue compare_val = JS_GetPropertyStr(js, v, "compare"); - if(!JS_IsUndefined(compare_val)) state.compare_op = js2SDL_GPUCompareOp(js, compare_val); - JS_FreeValue(js, compare_val); - - JSValue fail_val = JS_GetPropertyStr(js, v, "fail"); - if(!JS_IsUndefined(fail_val)) state.fail_op = js2SDL_GPUStencilOp(js, fail_val); - JS_FreeValue(js, fail_val); - - JSValue depth_fail_val = JS_GetPropertyStr(js, v, "depth_fail"); - if(!JS_IsUndefined(depth_fail_val)) state.depth_fail_op = js2SDL_GPUStencilOp(js, depth_fail_val); - JS_FreeValue(js, depth_fail_val); - - JSValue pass_val = JS_GetPropertyStr(js, v, "pass"); - if(!JS_IsUndefined(pass_val)) state.pass_op = js2SDL_GPUStencilOp(js, pass_val); - JS_FreeValue(js, pass_val); - - return state; -} - typedef struct lrtb lrtb; lrtb js2lrtb(JSContext *js, JSValue v) @@ -1033,67 +415,16 @@ void SDL_Surface_free(JSRuntime *rt, SDL_Surface *s) { SDL_DestroySurface(s); } -void SDL_Camera_free(JSRuntime *rt, SDL_Camera *cam) -{ - SDL_CloseCamera(cam); -} - -void SDL_Cursor_free(JSRuntime *rt, SDL_Cursor *c) -{ - SDL_DestroyCursor(c); -} - -void SDL_GPUDevice_free(JSRuntime *rt, SDL_GPUDevice *d) -{ - SDL_DestroyGPUDevice(d); -} - void SDL_GPUCommandBuffer_free(JSRuntime *rt, SDL_GPUCommandBuffer *c) { + } QJSCLASS(renderer_ctx,) -void SDL_GPUComputePass_free(JSRuntime *rt, SDL_GPUComputePass *c) { } -void SDL_GPUCopyPass_free(JSRuntime *rt, SDL_GPUCopyPass *c) { } -void SDL_GPURenderPass_free(JSRuntime *rt, SDL_GPURenderPass *c) { } - -#define GPURELEASECLASS(NAME) \ -void SDL_GPU##NAME##_free(JSRuntime *rt, SDL_GPU##NAME *c) { \ - SDL_ReleaseGPU##NAME(global_gpu, c); } \ -QJSCLASS(SDL_GPU##NAME,) \ - -static JSClassID js_sprite_id; -static void js_sprite_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { - sprite *sp = JS_GetOpaque(val, js_sprite_id); - if (!sp) return; - JS_MarkValue(rt, sp->image, mark_func); -} -QJSCLASSMARK(sprite,) - -static JSClassID js_transform_id; -static void js_transform_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { - transform *t = JS_GetOpaque(val, js_transform_id); - if (!t) return; - // Mark the JSValue references stored in your struct -// JS_MarkValue(rt, t->self, mark_func); - JS_MarkValue(rt, t->change_hook, mark_func); - JS_MarkValue(rt, t->jsparent, mark_func); - // Mark the array elements - for (int i = 0; i < arrlen(t->jschildren); i++) - JS_MarkValue(rt, t->jschildren[i], mark_func); -} - -QJSCLASSMARK(transform,) QJSCLASS(font,) QJSCLASS(datastream,) QJSCLASS(SDL_Window,) -QJSCLASS(SDL_Camera,) - -void SDL_AudioStream_free(JSRuntime *rt, SDL_AudioStream *st) { - SDL_DestroyAudioStream(st); -} -QJSCLASS(SDL_AudioStream,) void SDL_Texture_free(JSRuntime *rt, SDL_Texture *t){ SDL_DestroyTexture(t); @@ -1109,30 +440,6 @@ QJSCLASS(SDL_Surface, JS_SetProperty(js,j,height_atom,number2js(js,n->h)); ) -QJSCLASS(SDL_GPUDevice,) - -GPURELEASECLASS(Buffer) -GPURELEASECLASS(ComputePipeline) -GPURELEASECLASS(GraphicsPipeline) -GPURELEASECLASS(Sampler) -GPURELEASECLASS(Shader) -GPURELEASECLASS(Texture) -GPURELEASECLASS(TransferBuffer) -GPURELEASECLASS(Fence) - -QJSCLASS(SDL_GPUCommandBuffer,) -QJSCLASS(SDL_GPUComputePass,) -QJSCLASS(SDL_GPUCopyPass,) -QJSCLASS(SDL_GPURenderPass,) -QJSCLASS(SDL_Cursor,) - -static void PHYSFS_File_free(JSRuntime *rt, PHYSFS_File *f) -{ - PHYSFS_close(f); -} - -QJSCLASS(PHYSFS_File,) - void rtree_free(JSRuntime *rt, rtree *tree) { rtree_destroy(tree); @@ -1140,63 +447,6 @@ void rtree_free(JSRuntime *rt, rtree *tree) QJSCLASS(rtree,) -int js_arrlen(JSContext *js,JSValue v) { - if (JS_IsUndefined(v)) return 0; - int len; - len = js_getnum_str(js,v,"length"); - return len; -} - -// Unpacks a typed array javascript object. If it has a gpu property, returns it, too. Otherwise, if requested, makes one. -void *gpu_buffer_unpack(JSContext *js, SDL_GPUDevice *device, JSValue buffer, size_t *size, void **send_data, SDL_GPUBuffer **send_gpu) -{ - size_t o, len, bytes, msize; - JSValue buf = JS_GetTypedArrayBuffer(js, buffer, &o, &len, &bytes); - void *data = JS_GetArrayBuffer(js, &msize, buf); - JS_FreeValue(js,buf); - if (size) *size = msize; - if (send_gpu) { - JSValue gpu = JS_GetPropertyStr(js,buffer,"gpu"); - *send_gpu = js2SDL_GPUBuffer(js,gpu); - if (!*send_gpu) { - JSValue idx = JS_GetPropertyStr(js,buffer, "index"); - Uint32 usage = JS_ToBool(js,idx) ? SDL_GPU_BUFFERUSAGE_INDEX : SDL_GPU_BUFFERUSAGE_VERTEX; - JS_FreeValue(js,idx); - *send_gpu = SDL_CreateGPUBuffer(device, &(SDL_GPUBufferCreateInfo) { .usage=usage,.size=msize}); - if (!*send_gpu) printf("COULDN'T MAKE GPU BUFFER: %s\n", SDL_GetError()); - JS_SetPropertyStr(js, buffer, "gpu", SDL_GPUBuffer2js(js,*send_gpu)); - } - JS_FreeValue(js,gpu); - } - - if (send_data) - *send_data = data; - return data; -} - -void *get_typed_buffer(JSContext *js, JSValue argv, size_t *len) -{ - size_t o,bytes,size; - JSValue buf = JS_GetTypedArrayBuffer(js,argv,&o,len,&bytes); - void *data = JS_GetArrayBuffer(js,&size,buf); - JS_FreeValue(js,buf); - return data; -} - -float *js2floatarray(JSContext *js, JSValue v) { - JSClassID class = JS_GetClassID(v); - if (class == JS_CLASS_FLOAT32_ARRAY) { - size_t len; - return get_typed_buffer(js,v, &len); - } -// switch(p->class_id){} - int len = js_arrlen(js,v); - float *array = malloc(sizeof(float)*len); - for (int i = 0; i < len; i++) - array[i] = js_getnum_uint32(js,v,i); - return array; -} - JSValue angle2js(JSContext *js,double g) { return number2js(js,g*HMM_RadToTurn); } @@ -1225,20 +475,6 @@ colorf js2color(JSContext *js,JSValue v) { return color; } -SDL_FColor js2SDL_FColor(JSContext *js, JSValue v) -{ - colorf color = js2color(js,v); - return (SDL_FColor){color.r,color.g,color.b,color.a}; -} - -SDL_GPUTextureSamplerBinding js2SDL_GPUTextureSamplerBinding(JSContext *js, JSValue v) -{ - SDL_GPUTextureSamplerBinding b; - JS_GETPROP(js, b.texture, v, texture, SDL_GPUTexture) - JS_GETPROP(js, b.sampler, v, sampler, SDL_GPUSampler) - return b; -} - JSValue color2js(JSContext *js, colorf color) { JSValue arr = JS_NewArray(js); @@ -1268,7 +504,7 @@ HMM_Vec3 js2vec3(JSContext *js,JSValue v) float *js2floats(JSContext *js, JSValue v, size_t *len) { - *len = js_arrlen(js,v); + *len = JS_ArrayLength(js,v); float *arr = malloc(sizeof(float)* *len); for (int i = 0; i < *len; i++) arr[i] = js_getnum_uint32(js,v,i); @@ -1277,7 +513,7 @@ float *js2floats(JSContext *js, JSValue v, size_t *len) double *js2doubles(JSContext *js, JSValue v, size_t *len) { - *len = js_arrlen(js,v); + *len = JS_ArrayLength(js,v); double *arr = malloc(sizeof(double)* *len); for (int i = 0; i < *len; i++) arr[i] = js_getnum_uint32(js,v,i); @@ -1328,7 +564,7 @@ HMM_Vec4 js2vec4(JSContext *js, JSValue v) double arr_vec_length(JSContext *js,JSValue v) { - int len = js_arrlen(js,v); + int len = JS_ArrayLength(js,v); switch(len) { case 2: return HMM_LenV2(js2vec2(js,v)); case 3: return HMM_LenV3(js2vec3(js,v)); @@ -1357,7 +593,7 @@ JSValue vec42js(JSContext *js, HMM_Vec4 v) HMM_Vec2 *js2cpvec2arr(JSContext *js,JSValue v) { HMM_Vec2 *arr = NULL; - int n = js_arrlen(js,v); + int n = JS_ArrayLength(js,v); arrsetlen(arr,n); for (int i = 0; i < n; i++) @@ -1519,32 +755,6 @@ int point2segindex(HMM_Vec2 p, HMM_Vec2 *segs, double slop) { return best; } -JSValue make_quad_indices_buffer(JSContext *js, int quads) -{ - prosperon_rt *rt = JS_GetContextOpaque(js); - int count = quads*6; - if (!JS_IsUndefined(rt->idx_buffer) && rt->idx_count >= count) - return JS_DupValue(js,rt->idx_buffer); - - int verts = quads*4; - uint16_t *indices = malloc(sizeof(*indices)*count); - for (int i = 0, v = 0; v < verts; i +=6, v += 4) { - indices[i] = v; - indices[i+1] = v+2; - indices[i+2] = v+1; - indices[i+3] = v+2; - indices[i+4] = v+3; - indices[i+5] = v+1; - } - - if (!JS_IsUndefined(rt->idx_buffer)) - JS_FreeValue(js,rt->idx_buffer); - - rt->idx_buffer = make_gpu_buffer(js,indices, sizeof(*indices)*count, JS_TYPED_ARRAY_UINT16, 1,0,1); - rt->idx_count = count; - return JS_DupValue(js,rt->idx_buffer); -} - struct quad_buffers { HMM_Vec2 *pos; HMM_Vec2 *uv; @@ -1583,39 +793,6 @@ JSValue quadbuffers_to_mesh(JSContext *js, struct quad_buffers buffers) return ret; } -JSValue quads_to_mesh(JSContext *js, text_vert *buffer) -{ - size_t verts = arrlen(buffer); - - HMM_Vec2 *pos = malloc(arrlen(buffer)*sizeof(HMM_Vec2)); - HMM_Vec2 *uv = malloc(arrlen(buffer)*sizeof(HMM_Vec2)); - HMM_Vec4 *color = malloc(arrlen(buffer)*sizeof(HMM_Vec4)); - - for (int i = 0; i < arrlen(buffer); i++) { - pos[i] = buffer[i].pos; - uv[i] = buffer[i].uv; - color[i] = buffer[i].color; - } - - JSValue jspos = make_gpu_buffer(js, pos, sizeof(HMM_Vec2)*arrlen(buffer), JS_TYPED_ARRAY_FLOAT32, 2,0,0); - JSValue jsuv = make_gpu_buffer(js, uv, sizeof(HMM_Vec2)*arrlen(buffer), JS_TYPED_ARRAY_FLOAT32, 2,0,0); - JSValue jscolor = make_gpu_buffer(js, color, sizeof(HMM_Vec4)*arrlen(buffer), JS_TYPED_ARRAY_FLOAT32, 4,0,0); - - size_t quads = verts/4; - size_t count = verts/2*3; - JSValue jsidx = make_quad_indices_buffer(js, quads); - - JSValue ret = JS_NewObject(js); - JS_SetProperty(js, ret, pos, jspos); - JS_SetProperty(js, ret, uv, jsuv); - JS_SetProperty(js, ret, color, jscolor); - JS_SetProperty(js, ret, indices, jsidx); - JS_SetProperty(js, ret, vertices, number2js(js, verts)); - JS_SetProperty(js,ret,num_indices, number2js(js,count)); - - return ret; -} - JSC_CCALL(os_make_text_buffer, const char *s = JS_ToCString(js, argv[0]); rect rectpos = js2rect(js,argv[1]); @@ -1735,597 +912,9 @@ static JSValue floats2array(JSContext *js, float *vals, size_t len) { return arr; } -JSValue js_math_dot(JSContext *js, JSValue self, int argc, JSValue *argv) { - size_t alen, blen; - float *a = js2floats(js,argv[0], &alen); - float *b = js2floats(js,argv[1], &blen); - float dot = 0; - size_t len = alen < blen? alen : blen; - for (size_t i = 0; i < len; i++) - dot += a[i] * b[i]; - - free(a); - free(b); - return number2js(js,dot); -} - -JSValue js_math_project(JSContext *js, JSValue self, int argc, JSValue *argv) { - size_t alen, blen; - float *a = js2floats(js, argv[0], &alen); - float *b = js2floats(js, argv[1], &blen); - - if (!a || !b) { - free(a); - free(b); - return JS_UNDEFINED; - } - - // We'll work up to the smaller length - size_t len = (alen < blen) ? alen : blen; - if (len == 0) { - free(a); - free(b); - return JS_UNDEFINED; - } - - // Compute dot products: a·b and b·b - float ab = 0, bb = 0; - for (size_t i = 0; i < len; i++) { - ab += a[i] * b[i]; - bb += b[i] * b[i]; - } - - // Build the result array - float *proj = (float*)malloc(sizeof(float) * len); - if (!proj) { - free(a); - free(b); - return JS_EXCEPTION; // or some error - } - - float scale = (bb != 0.0f) ? (ab / bb) : 0.0f; - for (size_t i = 0; i < len; i++) - proj[i] = scale * b[i]; - - JSValue ret = floats2array(js, proj, len); - - free(a); - free(b); - free(proj); - - return ret; -} - -// --------------------------- -// math_midpoint(a, b) -// midpoint = (a + b) / 2 -// returns new vector (array) -// dimension = min(alen, blen) -// --------------------------- -JSValue js_math_midpoint(JSContext *js, JSValue self, int argc, JSValue *argv) { - size_t alen, blen; - float *a = js2floats(js, argv[0], &alen); - float *b = js2floats(js, argv[1], &blen); - - if (!a || !b) { - free(a); - free(b); - return JS_UNDEFINED; - } - - size_t len = (alen < blen) ? alen : blen; - if (len == 0) { - free(a); - free(b); - return JS_UNDEFINED; - } - - float *m = (float*)malloc(sizeof(float) * len); - if (!m) { - free(a); - free(b); - return JS_EXCEPTION; - } - - for (size_t i = 0; i < len; i++) - m[i] = (a[i] + b[i]) * 0.5f; - - JSValue ret = floats2array(js, m, len); - - free(a); - free(b); - free(m); - return ret; -} - -// --------------------------- -// math_distance(a, b) -// Euclidean distance = sqrt( Σ (b[i]-a[i])^2 ) -// dimension = min(alen, blen) -// returns scalar (number) -// --------------------------- -JSValue js_math_distance(JSContext *js, JSValue self, int argc, JSValue *argv) { - size_t alen, blen; - float *a = js2floats(js, argv[0], &alen); - float *b = js2floats(js, argv[1], &blen); - - if (!a || !b) { - free(a); - free(b); - return JS_UNDEFINED; - } - - size_t len = (alen < blen) ? alen : blen; - if (len == 0) { - free(a); - free(b); - return JS_UNDEFINED; - } - - float distSq = 0.0f; - for (size_t i = 0; i < len; i++) { - float diff = b[i] - a[i]; - distSq += diff * diff; - } - float dist = sqrtf(distSq); - - free(a); - free(b); - return number2js(js, dist); -} - -// --------------------------- -// math_angle(a) -// In 2D: angle from x-axis via atan2(y, x). -// For higher dimensions, there's no single "angle" in the usual sense. -// We'll only return angle if len >= 2, using first two coords. -// Or strictly require len==2, your call. -// --------------------------- -JSValue js_math_angle(JSContext *js, JSValue self, int argc, JSValue *argv) { - size_t alen; - float *a = js2floats(js, argv[0], &alen); - - if (!a || alen < 2) { - free(a); - return JS_UNDEFINED; - } - - // If you want to be strict, require alen == 2 - // if (alen != 2) { - // free(a); - // return JS_UNDEFINED; - // } - - float angle = atan2f(a[1], a[0]); - free(a); - return number2js(js, angle); -} - -/* Given a series of points p, computes a new series with them expanded on either side by d */ -/* -HMM_Vec2 *inflatepoints(HMM_Vec2 *p, float d, int n) -{ - if (d == 0) { - HMM_Vec2 *ret = NULL; - arraddn(ret,n); - for (int i = 0; i < n; i++) - ret[i] = p[i]; - - return ret; - } - - parsl_position par_v[n]; - uint16_t spine_lens[] = {n}; - for (int i = 0; i < n; i++) { - par_v[i].x = p[i].x; - par_v[i].y = p[i].y; - }; - - parsl_context *par_ctx = parsl_create_context((parsl_config){ - .thickness = d, - .flags= PARSL_FLAG_ANNOTATIONS, - .u_mode = PAR_U_MODE_DISTANCE - }); - - parsl_mesh *mesh = parsl_mesh_from_lines(par_ctx, (parsl_spine_list){ - .num_vertices = n, - .num_spines = 1, - .vertices = par_v, - .spine_lengths = spine_lens, - .closed = 0, - }); - - HMM_Vec2 *ret = NULL; - arraddn(ret,mesh->num_vertices); - for (int i = 0; i < mesh->num_vertices; i++) { - ret[i].x = mesh->positions[i].x; - ret[i].y = mesh->positions[i].y; - }; - - return ret; -} - -JSC_CCALL(math_inflate, - HMM_Vec2 *p = js2cpvec2arr(js,argv[0]); - double d = js2number(js,argv[1]); - HMM_Vec2 *infl = inflatepoints(p,d, js_arrlen(js,argv[0])); - ret = vecarr2js(js,infl, arrlen(infl)); - arrfree(infl); - arrfree(p); -) -*/ -JSC_CCALL(math_rotate, - HMM_Vec2 vec = js2vec2(js,argv[0]); - double angle = js2angle(js, argv[1]); - HMM_Vec2 pivot = JS_IsUndefined(argv[2]) ? v2zero : js2vec2(js,argv[2]); - // vec = vec - pivot -// cblas_saxpy(2, -1.0f, pivot.e, 1, vec.e, 1); - vec = HMM_SubV2(vec,pivot); - - // Length of the vector (r) -// float r = sqrtf(cblas_sdot(2, vec.e, 1, vec.e, 1)); - float r = HMM_LenV2(vec); - - // Update angle - angle += atan2f(vec.y, vec.x); - - // Update vec to rotated position - vec.x = r * cosf(angle); - vec.y = r * sinf(angle); - - // vec = vec + pivot -// cblas_saxpy(2, 1.0f, pivot.e, 1, vec.e, 1); - vec = HMM_AddV2(vec,pivot); - - // Convert back to JS and return - return vec22js(js, vec); -) - -JSC_CCALL(math_norm, - int len = js_arrlen(js,argv[0]); - - switch(len) { - case 2: return vec22js(js,HMM_NormV2(js2vec2(js,argv[0]))); - case 3: return vec32js(js, HMM_NormV3(js2vec3(js,argv[0]))); - case 4: return vec42js(js,HMM_NormV4(js2vec4(js,argv[0]))); - } - - double length = arr_vec_length(js,argv[0]); - JSValue newarr = JS_NewArray(js); - - for (int i = 0; i < len; i++) - JS_SetPropertyUint32(js, newarr, i, number2js(js,js_getnum_uint32(js, argv[0],i)/length)); - - ret = newarr; -) - -JSC_CCALL(math_angle_between, - int len = js_arrlen(js,argv[0]); - switch(len) { - case 2: return angle2js(js,HMM_AngleV2(js2vec2(js,argv[0]), js2vec2(js,argv[1]))); - case 3: return angle2js(js,HMM_AngleV3(js2vec3(js,argv[0]), js2vec3(js,argv[1]))); - case 4: return angle2js(js,HMM_AngleV4(js2vec4(js,argv[0]), js2vec4(js,argv[1]))); - } - return JS_ThrowReferenceError(js, "Input array must have a length between 2 and 4."); -) - -JSC_CCALL(math_lerp, - double s = js2number(js,argv[0]); - double f = js2number(js,argv[1]); - double t = js2number(js,argv[2]); - - ret = number2js(js,(f-s)*t+s); -) - -int gcd(int a, int b) { - if (b == 0) - return a; - return gcd(b, a % b); -} - -JSC_CCALL(math_gcd, ret = number2js(js,gcd(js2number(js,argv[0]), js2number(js,argv[1]))); ) - -JSC_CCALL(math_lcm, - double a = js2number(js,argv[0]); - double b = js2number(js,argv[1]); - ret = number2js(js,(a*b)/gcd(a,b)); -) - -JSC_CCALL(math_clamp, - double x = js2number(js,argv[0]); - double l = js2number(js,argv[1]); - double h = js2number(js,argv[2]); - return number2js(js,x > h ? h : x < l ? l : x); -) - -JSC_CCALL(math_angledist, - double a1 = js2number(js,argv[0]); - double a2 = js2number(js,argv[1]); - a1 = fmod(a1,1); - a2 = fmod(a2,1); - double dist = a2-a1; - if (dist == 0) return number2js(js,dist); - if (dist > 0) { - if (dist > 0.5) return number2js(js,dist-1); - return number2js(js,dist); - } - - if (dist < -0.5) return number2js(js,dist+1); - - return number2js(js,dist); -) - -JSC_CCALL(math_length, return number2js(js,arr_vec_length(js,argv[0])); ) - -double rand_range(JSContext *js, double min, double max) -{ - MTRand mrand = ((prosperon_rt*)JS_GetContextOpaque(js))->mrand; - return genRand(&mrand) * (max-min)+min; -} - -JSC_CCALL(math_jitter, - double n = js2number(js,argv[0]); - double pct = js2number(js,argv[1]); - - return number2js(js,n + (rand_range(js,-pct,pct)*n)); -) - -JSC_CCALL(math_mean, - double len = js_arrlen(js,argv[0]); - double sum = 0; - for (int i = 0; i < len; i++) - sum += js_getnum_uint32(js, argv[0], i); - - return number2js(js,sum/len); -) - -JSC_CCALL(math_sum, - double sum = 0.0; - int len = js_arrlen(js,argv[0]); - for (int i = 0; i < len; i++) - sum += js_getnum_uint32(js, argv[0], i); - - return number2js(js,sum); -) - -JSC_CCALL(math_sigma, - int len = js_arrlen(js,argv[0]); - double sum = 0; - for (int i = 0; i < len; i++) - sum += js_getnum_uint32(js, argv[0], i); - - double mean = sum/(double)len; - - double sq_diff = 0; - - for (int i = 0; i < len; i++) { - double x = js_getnum_uint32(js, argv[0],i); - sq_diff += pow(x-mean, 2); - } - - double variance = sq_diff/((double)len); - - return number2js(js,sqrt(variance)); -) - -JSC_CCALL(math_median, - int len = js_arrlen(js,argv[0]); - double arr[len]; - double temp; - - for (int i = 0; i < len; i++) - arr[i] = js_getnum_uint32(js, argv[0], i); - - for (int i = 0; i < len-1; i++) { - for (int j = i+1; j < len; j++) { - if (arr[i] > arr[j]) { - temp = arr[i]; - arr[i] = arr[j]; - arr[j] = temp; - } - } - } - - if (len % 2 == 0) return number2js(js,(arr[len/2-1] + arr[len/2])/2.0); - return number2js(js,arr[len/2]); -) - -JSC_CCALL(math_from_to, - HMM_Vec2 from = js2vec2(js,argv[0]); - HMM_Vec2 to = js2vec2(js,argv[1]); - float space = js2number(js,argv[2]); - float from_offset = js2number(js,argv[3]); - float to_offset = js2number(js,argv[4]); - - HMM_Vec2 dir = HMM_NormV2(HMM_SubV2(to, from)); - from = HMM_AddV2(from, HMM_MulV2F(dir, from_offset)); - to = HMM_SubV2(to,HMM_MulV2F(dir, to_offset)); - float length = HMM_DistV2(from, to); - int steps = floor(length/space); - float stepsize = length/(steps+1.0); - - ret = JS_NewArray(js); - JS_SetPropertyUint32(js,ret,0,vec22js(js,from)); - for (int i = 1; i < steps+1; i++) { - HMM_Vec2 val = HMM_AddV2(from, HMM_MulV2F(dir, i*stepsize)); - JS_SetPropertyUint32(js, ret, i, vec22js(js,val)); - } - JS_SetPropertyUint32(js, ret, steps+1, vec22js(js,to)); -) - -JSC_CCALL(math_rand, - MTRand mrand = ((prosperon_rt*)JS_GetContextOpaque(js))->mrand; - return JS_NewFloat64(js, genRand(&mrand)) -) - -JSC_CCALL(math_randi, - MTRand mrand = ((prosperon_rt*)JS_GetContextOpaque(js))->mrand; - return JS_NewUint32(js, genRandLong(&mrand)) -) - -JSC_CCALL(math_srand, - MTRand mrand = ((prosperon_rt*)JS_GetContextOpaque(js))->mrand; - if (argc < 1) - m_seedRand(&mrand, time(NULL)); - else - m_seedRand(&mrand, js2number(js,argv[0])); -) - -JSValue js_math_direction(JSContext *js, JSValue self, int argc, JSValue *argv) { - size_t alen, blen; - float *a = js2floats(js, argv[0], &alen); - float *b = js2floats(js, argv[1], &blen); - - if (!a || !b) { - free(a); - free(b); - return JS_UNDEFINED; - } - - size_t len = (alen < blen) ? alen : blen; - if (len == 0) { - free(a); - free(b); - return JS_UNDEFINED; - } - - float *dir = (float*)malloc(sizeof(float) * len); - if (!dir) { - free(a); - free(b); - return JS_EXCEPTION; // or some error - } - - // Compute (b - a) - for (size_t i = 0; i < len; i++) - dir[i] = b[i] - a[i]; - - // Compute magnitude of dir - float mag = 0.0f; - for (size_t i = 0; i < len; i++) - mag += dir[i] * dir[i]; - mag = sqrtf(mag); - - // Normalize if possible - if (mag > 0.0f) { - for (size_t i = 0; i < len; i++) - dir[i] /= mag; - } else { - // Optional: if a == b, direction is zero - // you might want to do something else here - // (like return all zeros). - } - - JSValue ret = floats2array(js, dir, len); - - free(a); - free(b); - free(dir); - return ret; -} - -JSValue js_math_reflect(JSContext *js, JSValue self, int argc, JSValue *argv) { - size_t vlen, nlen; - float *vec = js2floats(js, argv[0], &vlen); - float *norm = js2floats(js, argv[1], &nlen); - - if (!vec || !norm) { - free(vec); - free(norm); - return JS_UNDEFINED; - } - - size_t len = (vlen < nlen) ? vlen : nlen; - if (len == 0) { - free(vec); - free(norm); - return JS_UNDEFINED; - } - - // 1) Normalize the planeNormal - // p = planeNormal / |planeNormal| - float mag = 0.0f; - for (size_t i = 0; i < len; i++) - mag += norm[i] * norm[i]; - mag = sqrtf(mag); - - // If the plane normal is zero-length, no reflection is well-defined - if (mag == 0.0f) { - // Return original vec or undefined, your choice. - // Here, let's just return a copy of the original vec. - JSValue retNoReflect = floats2array(js, vec, vlen); - free(vec); - free(norm); - return retNoReflect; - } - - float *pnorm = (float*)malloc(sizeof(float) * len); - if (!pnorm) { - free(vec); - free(norm); - return JS_EXCEPTION; - } - for (size_t i = 0; i < len; i++) - pnorm[i] = norm[i] / mag; - - // 2) Compute dot = vec•pnorm - float dot = 0.0f; - for (size_t i = 0; i < len; i++) - dot += vec[i] * pnorm[i]; - - // 3) reflect = vec - 2*dot*pnorm - float *ref = (float*)malloc(sizeof(float) * len); - if (!ref) { - free(vec); - free(norm); - free(pnorm); - return JS_EXCEPTION; - } - for (size_t i = 0; i < len; i++) - ref[i] = vec[i] - 2.0f * dot * pnorm[i]; - - // Now return 'ref' as an array - JSValue ret = floats2array(js, ref, len); - - free(vec); - free(norm); - free(pnorm); - free(ref); - return ret; -} - -static const JSCFunctionListEntry js_math_funcs[] = { - MIST_FUNC_DEF(math, dot,2), - MIST_FUNC_DEF(math, project,2), - MIST_FUNC_DEF(math, rotate, 3), - MIST_FUNC_DEF(math, midpoint, 2), - MIST_FUNC_DEF(math, reflect, 0), - MIST_FUNC_DEF(math, distance, 2), - MIST_FUNC_DEF(math, direction, 2), - MIST_FUNC_DEF(math, angle, 1), - MIST_FUNC_DEF(math, norm, 1), - MIST_FUNC_DEF(math, angle_between, 2), - MIST_FUNC_DEF(math, lerp, 3), - MIST_FUNC_DEF(math, gcd, 2), - MIST_FUNC_DEF(math, lcm, 2), - MIST_FUNC_DEF(math, clamp, 3), - MIST_FUNC_DEF(math, angledist, 2), - MIST_FUNC_DEF(math, jitter, 2), - MIST_FUNC_DEF(math, mean, 1), - MIST_FUNC_DEF(math, sum, 1), - MIST_FUNC_DEF(math, sigma, 1), - MIST_FUNC_DEF(math, median, 1), - MIST_FUNC_DEF(math, length, 1), - MIST_FUNC_DEF(math, from_to, 5), - MIST_FUNC_DEF(math, rand, 0), - MIST_FUNC_DEF(math, randi, 0), - MIST_FUNC_DEF(math, srand,0), - -}; - #define JS_HMM_FN(OP, HMM, SIGN) \ JSC_CCALL(array_##OP, \ - int len = js_arrlen(js,self); \ + int len = JS_ArrayLength(js,self); \ if (!JS_IsArray(js, argv[0])) { \ double n = js2number(js,argv[0]); \ JSValue arr = JS_NewArray(js); \ @@ -2358,7 +947,7 @@ JS_HMM_FN(scale, Mul, *) JSC_CCALL(array_lerp, double t = js2number(js,argv[1]); - int len = js_arrlen(js,self); + int len = JS_ArrayLength(js,self); JSValue arr = JS_NewArray(js); for (int i = 0; i < len; i++) { @@ -2521,72 +1110,6 @@ JSC_CCALL(os_engine_start, return SDL_Window2js(js,new); ) -static JSValue js_keymod(JSContext *js) -{ - SDL_Keymod modstate = SDL_GetModState(); - JSValue ret = JS_NewObject(js); - if (SDL_KMOD_CTRL & modstate) - JS_SetPropertyStr(js,ret,"ctrl", JS_NewBool(js,1)); - if (SDL_KMOD_SHIFT & modstate) - JS_SetPropertyStr(js,ret,"shift", JS_NewBool(js,1)); - if (SDL_KMOD_ALT & modstate) - JS_SetPropertyStr(js,ret,"alt", JS_NewBool(js,1)); - if (SDL_KMOD_GUI & modstate) - JS_SetPropertyStr(js,ret,"super", JS_NewBool(js,1)); - if (SDL_KMOD_NUM & modstate) - JS_SetPropertyStr(js,ret,"numlock", JS_NewBool(js,1)); - if (SDL_KMOD_CAPS & modstate) - JS_SetPropertyStr(js,ret,"caps", JS_NewBool(js,1)); - if (SDL_KMOD_SCROLL & modstate) - JS_SetPropertyStr(js,ret,"scrolllock", JS_NewBool(js,1)); - if (SDL_KMOD_MODE & modstate) - JS_SetPropertyStr(js,ret,"mode", JS_NewBool(js,1)); - - return ret; -} - -JSC_CCALL(camera_list, - int num; - SDL_CameraID *ids = SDL_GetCameras(&num); - if (num == 0) return JS_UNDEFINED; - JSValue jsids = JS_NewArray(js); - for (int i = 0; i < num; i++) - JS_SetPropertyUint32(js,jsids, i, number2js(js,ids[i])); - - return jsids; -) - -JSC_CCALL(camera_open, - int id = js2number(js,argv[0]); - SDL_Camera *cam = SDL_OpenCamera(id, NULL); - if (!cam) ret = JS_ThrowReferenceError(js, "Could not open camera %d: %s\n", id, SDL_GetError()); - else - ret = SDL_Camera2js(js,cam); -) - -JSC_CCALL(camera_name, - const char *name = SDL_GetCameraName(js2number(js,argv[0])); - if (!name) return JS_ThrowReferenceError(js, "Could not get camera name from id %d.", (int)js2number(js,argv[0])); - - return JS_NewString(js, name); -) - -JSC_CCALL(camera_position, - SDL_CameraPosition pos = SDL_GetCameraPosition(js2number(js,argv[0])); - switch(pos) { - case SDL_CAMERA_POSITION_UNKNOWN: return JS_NewString(js,"unknown"); - case SDL_CAMERA_POSITION_FRONT_FACING: return JS_NewString(js,"front"); - case SDL_CAMERA_POSITION_BACK_FACING: return JS_NewString(js,"back"); - } -) - -static const JSCFunctionListEntry js_camera_funcs[] = { - MIST_FUNC_DEF(camera, list, 0), - MIST_FUNC_DEF(camera, open, 1), - MIST_FUNC_DEF(camera, name, 1), - MIST_FUNC_DEF(camera, position, 1), -}; - JSC_SCALL(SDL_Window_make_renderer, SDL_Window *win = js2SDL_Window(js,self); renderer_ctx *ctx = malloc(sizeof(*ctx)); @@ -2600,22 +1123,7 @@ JSC_SCALL(SDL_Window_make_renderer, return renderer_ctx2js(js,ctx); ) -SDL_GPUShaderFormat js2SDL_GPUShaderFormat(JSContext *js, JSValue v) -{ - const char *format_str = JS_ToCString(js, v); - SDL_GPUShaderFormat format = SDL_GPU_SHADERFORMAT_SPIRV; // default to SPIR-V - if (format_str) { - if (!strcmp(format_str, "private")) format = SDL_GPU_SHADERFORMAT_PRIVATE; - else if (!strcmp(format_str, "spv")) format = SDL_GPU_SHADERFORMAT_SPIRV; - else if (!strcmp(format_str, "dxbc")) format = SDL_GPU_SHADERFORMAT_DXBC; - else if (!strcmp(format_str, "dxil")) format = SDL_GPU_SHADERFORMAT_DXIL; - else if (!strcmp(format_str, "msl")) format = SDL_GPU_SHADERFORMAT_MSL; - else if (!strcmp(format_str, "metallib")) format = SDL_GPU_SHADERFORMAT_METALLIB; - JS_FreeCString(js, format_str); - } - return format; -} - +/* JSC_CCALL(SDL_Window_make_gpu, if (global_gpu) return JS_ThrowReferenceError(js, "A gpu has already been created somewhere."); @@ -2640,6 +1148,7 @@ JSC_CCALL(SDL_Window_make_gpu, global_gpu = gpu; return SDL_GPUDevice2js(js,gpu); ) +*/ JSC_CCALL(SDL_Window_fullscreen, SDL_SetWindowFullscreen(js2SDL_Window(js,self), SDL_WINDOW_FULLSCREEN) @@ -2721,7 +1230,7 @@ JSValue js_window_mouse_grab(JSContext *js, JSValue self, int argc, JSValue *arg static const JSCFunctionListEntry js_SDL_Window_funcs[] = { MIST_FUNC_DEF(SDL_Window, fullscreen, 0), MIST_FUNC_DEF(SDL_Window, make_renderer, 1), - MIST_FUNC_DEF(SDL_Window, make_gpu, 2), +// MIST_FUNC_DEF(SDL_Window, make_gpu, 2), MIST_FUNC_DEF(SDL_Window, keyboard_shown, 0), MIST_FUNC_DEF(window, theme, 0), MIST_FUNC_DEF(window, safe_area, 0), @@ -2786,20 +1295,6 @@ JSC_CCALL(SDL_Renderer_draw_color, SDL_SetRenderDrawColorFloat(renderer, color.r,color.g,color.b,color.a); ) -SDL_GPUColorTargetInfo js2SDL_GPUColorTargetInfo(JSContext *js, JSValue v) -{ - SDL_GPUColorTargetInfo info = {0}; - JS_GETPROP(js, info.texture, v, texture, SDL_GPUTexture) - JS_GETPROP(js,info.mip_level,v,mip_level,number) - JS_GETPROP(js,info.layer_or_depth_plane, v, layer, number) - JS_GETPROP(js,info.load_op,v,load,SDL_GPULoadOp) - JS_GETPROP(js,info.store_op,v,store,SDL_GPUStoreOp) - JS_GETPROP(js,info.resolve_mip_level,v,resolve_mip_level,number) - JS_GETPROP(js,info.resolve_layer,v,resolve_layer,number) - JS_GETPROP(js,info.clear_color,v,clear,SDL_FColor) - return info; -} - JSC_CCALL(renderer_load_texture, SDL_Renderer *r = js2renderer_ctx(js,self)->sdl; SDL_Surface *surf = js2SDL_Surface(js,argv[0]); @@ -2829,7 +1324,7 @@ JSC_CCALL(renderer_line, renderer_ctx *ctx = js2renderer_ctx(js,self); SDL_Renderer *r = ctx->sdl; - int len = js_arrlen(js,argv[0]); + int len = JS_ArrayLength(js,argv[0]); if (len < 2) return JS_ThrowReferenceError(js, "Must provide at least 2 points to render."); SDL_FPoint points[len]; @@ -2850,7 +1345,7 @@ JSC_CCALL(renderer_point, SDL_Renderer *r = ctx->sdl; if (JS_IsArray(js, argv[0])) { - int len = js_arrlen(js, argv[0]); + int len = JS_ArrayLength(js, argv[0]); SDL_FPoint pts[len]; for (int i = 0; i < len; ++i) { @@ -2874,7 +1369,7 @@ JSC_CCALL(renderer_rects, /* array-of-rectangles case */ if (JS_IsArray(js, argv[0])) { - int len = js_arrlen(js, argv[0]); + int len = JS_ArrayLength(js, argv[0]); if (len <= 0) return JS_UNDEFINED; SDL_FRect rects[len]; @@ -3078,7 +1573,7 @@ JSC_CCALL(renderer_target, // color: the color this sprite should be hued by JSC_CCALL(renderer_make_sprite_mesh, JSValue sprites = argv[0]; - size_t quads = js_arrlen(js,argv[0]); + size_t quads = JS_ArrayLength(js,argv[0]); size_t verts = quads*4; size_t count = quads*6; @@ -3214,2245 +1709,6 @@ static const JSCFunctionListEntry js_renderer_ctx_funcs[] = { MIST_FUNC_DEF(renderer, make_sprite_mesh, 2), }; -static const struct { const char *s; SDL_AudioFormat f; } fmt_lut[] = { - { "u8", SDL_AUDIO_U8 }, /* Unsigned 8-bit */ - { "s8", SDL_AUDIO_S8 }, /* Signed 8-bit */ - { "s16", SDL_AUDIO_S16 }, /* Signed 16-bit, host endian */ - { "s32", SDL_AUDIO_S32 }, /* Signed 32-bit, host endian */ - { "f32", SDL_AUDIO_F32 } /* Float 32-bit, host endian */ -}; - -static int format_str_to_enum(const char *f, SDL_AudioFormat *out) -{ - struct { const char *s; SDL_AudioFormat f; } map[] = { - {"u8", SDL_AUDIO_U8 }, {"s16", SDL_AUDIO_S16}, - {"s32", SDL_AUDIO_S32}, {"f32", SDL_AUDIO_F32} - }; - for (size_t i=0;iformat))); - - JS_SetPropertyStr(js, o, "channels", - JS_NewInt32(js, spec->channels)); - - JS_SetPropertyStr(js, o, "samplerate", - JS_NewInt32(js, spec->freq)); - - return o; -} - -static SDL_AudioSpec js2audiospec(JSContext *js, JSValue obj) -{ - SDL_AudioSpec spec; - - JSValue v; - - v = JS_GetPropertyStr(js, obj, "format"); - if (!JS_IsUndefined(v)) { - const char *s = JS_ToCString(js, v); - format_str_to_enum(s, &spec.format); - JS_FreeCString(js, s); - } - JS_FreeValue(js, v); - - v = JS_GetPropertyStr(js, obj, "channels"); - if (!JS_IsUndefined(v)) JS_ToInt32(js, &spec.channels, v); - JS_FreeValue(js, v); - - v = JS_GetPropertyStr(js, obj, "samplerate"); - if (!JS_IsUndefined(v)) JS_ToInt32(js, &spec.freq, v); - JS_FreeValue(js, v); - - return spec; -} - -// SDL AUDIO -JSC_CCALL(sdl_audio_drivers, - int num = SDL_GetNumAudioDrivers(); - JSValue arr = JS_NewArray(js); - for (int i = 0; i < num; i++) - JS_SetPropertyUint32(js, arr, i, JS_NewString(js, SDL_GetAudioDriver(i))); - return arr; -) - -JSC_CCALL(sdl_audio_devices, - int n; - SDL_AudioDeviceID *ids = SDL_GetAudioPlaybackDevices(&n); - - JSValue arr = JS_NewArray(js); - for (int i = 0; i < n; i++) - JS_SetPropertyUint32(js,arr,i,JS_NewString(js, SDL_GetAudioDeviceName(ids[i]))); - - return arr; -) - -JSC_CCALL(sdl_audio_open_stream, - const char *type = JS_IsString(argv[0]) ? JS_ToCString(js, argv[0]) : NULL; - SDL_AudioDeviceID devid = !strcmp(type, "capture") ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING : SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK; - - if (type) - JS_FreeCString(js, type); - - SDL_AudioStream *st; - - if (JS_IsUndefined(argv[1])) - st = SDL_OpenAudioDeviceStream(devid, NULL, NULL, NULL); - else { - SDL_AudioSpec want = js2audiospec(js, argv[1]); - st = SDL_OpenAudioDeviceStream(devid, &want, NULL, NULL); - } - - if (!st) - return JS_ThrowInternalError(js, "open failed: %s", SDL_GetError()); - - return SDL_AudioStream2js(js, st); -) - -static const JSCFunctionListEntry js_sdl_audio_funcs[] = { - MIST_FUNC_DEF(sdl_audio, drivers, 0), - MIST_FUNC_DEF(sdl_audio, devices, 0), - MIST_FUNC_DEF(sdl_audio, open_stream, 2), -}; - -JSC_CCALL(sdl_audiostream_get_format, - SDL_AudioStream *as = js2SDL_AudioStream(js, self); - SDL_AudioSpec src; - SDL_AudioSpec dst; - SDL_GetAudioStreamFormat(as, &src, &dst); - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "src", audiospec2js(js, &src)); - JS_SetPropertyStr(js, obj, "dst", audiospec2js(js, &dst)); - return obj; -) - -JSC_CCALL(sdl_audiostream_set_format, - SDL_AudioStream *as=js2SDL_AudioStream(js,self); - const SDL_AudioSpec *src_ptr=NULL,*dst_ptr=NULL; - SDL_AudioSpec src={0},dst={0}; - - if(argc>0&&!JS_IsUndefined(argv[0])){ - src=js2audiospec(js,argv[0]); - src_ptr=&src; - } - if(argc>1&&!JS_IsUndefined(argv[1])){ - dst=js2audiospec(js,argv[1]); - dst_ptr=&dst; - } - - if(!SDL_SetAudioStreamFormat(as,src_ptr,dst_ptr)) - return JS_ThrowInternalError(js,"%s",SDL_GetError()); - - return JS_UNDEFINED; -) - -JSC_CCALL(sdl_audiostream_resume, - SDL_AudioStream *as = js2SDL_AudioStream(js,self); - if (!SDL_ResumeAudioStreamDevice(as)) - return JS_ThrowInternalError(js,"%s",SDL_GetError()); - - return JS_UNDEFINED; -) - -JSC_CCALL(sdl_audiostream_clear, - SDL_AudioStream *as=js2SDL_AudioStream(js,self); - if (!SDL_ClearAudioStream(as)) - return JS_ThrowInternalError(js,"%s",SDL_GetError()); - return JS_UNDEFINED; -) - -JSC_CCALL(sdl_audiostream_flush, - SDL_AudioStream *as=js2SDL_AudioStream(js,self); - if(!SDL_FlushAudioStream(as)) - return JS_ThrowInternalError(js,"%s",SDL_GetError()); - return JS_UNDEFINED; -) - -JSC_CCALL(sdl_audiostream_available, - SDL_AudioStream *as=js2SDL_AudioStream(js,self); - Sint64 n = SDL_GetAudioStreamAvailable(as); - if(n<0) return JS_ThrowInternalError(js,"%s",SDL_GetError()); - return JS_NewInt64(js,n); -) - -JSC_CCALL(sdl_audiostream_queued, - SDL_AudioStream *as=js2SDL_AudioStream(js,self); - Sint64 n = SDL_GetAudioStreamQueued(as); - if(n<0) return JS_ThrowInternalError(js,"%s",SDL_GetError()); - return JS_NewInt64(js,n); -) - -/* ---------- data IO ---------------------------------------------------- */ -JSC_CCALL(sdl_audiostream_put, - SDL_AudioStream *as=js2SDL_AudioStream(js,self); - size_t len; - void *buf = JS_GetArrayBuffer(js, &len, argv[0]); - if (!buf) - return JS_ThrowInternalError(js, "Requires array buffer."); - - if (!SDL_PutAudioStreamData(as,buf,len)) - return JS_ThrowInternalError(js, "%s", SDL_GetError()); - return JS_UNDEFINED; -) - -JSC_CCALL(sdl_audiostream_get, - SDL_AudioStream *as=js2SDL_AudioStream(js,self); - int want; - JS_ToInt32(js,&want,argv[0]); - void *data = malloc(want); - int got = SDL_GetAudioStreamData(as, data, want); - - if (got<0) { - free(data); - return JS_ThrowInternalError(js,"%s",SDL_GetError()); - } - - JSValue ab = JS_NewArrayBufferCopy(js, data, got); - free(data); - - return ab; -) - -JSC_CCALL(sdl_audiostream_get_gain, - SDL_AudioStream *as=js2SDL_AudioStream(js,self); - return JS_NewFloat64(js,SDL_GetAudioStreamGain(as)); -) - -JSC_CCALL(sdl_audiostream_set_gain, - SDL_AudioStream *as=js2SDL_AudioStream(js,self); - double g; JS_ToFloat64(js,&g,argv[0]); - SDL_SetAudioStreamGain(as,(float)g); - return JS_UNDEFINED; -) - -JSC_CCALL(sdl_audiostream_get_freq_ratio, - SDL_AudioStream *as=js2SDL_AudioStream(js,self); - return JS_NewFloat64(js,SDL_GetAudioStreamFrequencyRatio(as)); -) - -JSC_CCALL(sdl_audiostream_set_freq_ratio, - SDL_AudioStream *as=js2SDL_AudioStream(js,self); - double r; JS_ToFloat64(js,&r,argv[0]); - SDL_SetAudioStreamFrequencyRatio(as,(float)r); - return JS_UNDEFINED; -) - -/* ---------- JS export list -------------------------------------------- */ -static const JSCFunctionListEntry js_SDL_AudioStream_funcs[] = { - MIST_FUNC_DEF(sdl_audiostream, get_format, 0), - MIST_FUNC_DEF(sdl_audiostream, set_format, 2), - MIST_FUNC_DEF(sdl_audiostream, resume, 0), - MIST_FUNC_DEF(sdl_audiostream, clear, 0), - MIST_FUNC_DEF(sdl_audiostream, flush, 0), - MIST_FUNC_DEF(sdl_audiostream, available, 0), - MIST_FUNC_DEF(sdl_audiostream, queued, 0), - MIST_FUNC_DEF(sdl_audiostream, put, 1), - MIST_FUNC_DEF(sdl_audiostream, get, 1), - MIST_FUNC_DEF(sdl_audiostream, set_gain, 1), - MIST_FUNC_DEF(sdl_audiostream, get_gain, 0), - MIST_FUNC_DEF(sdl_audiostream, set_freq_ratio, 1), - MIST_FUNC_DEF(sdl_audiostream, get_freq_ratio, 0), -}; - -// GPU API -JSC_CCALL(gpu_claim_window, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - SDL_Window *win = js2SDL_Window(js, argv[0]); - SDL_ClaimWindowForGPUDevice(gpu,win); -) - -JSC_CCALL(gpu_set_swapchain, - if (!SDL_SetGPUSwapchainParameters(global_gpu,global_window, js2SDL_GPUSwapchainComposition(js,argv[0]), js2SDL_GPUPresentMode(js,argv[1]))) - return JS_ThrowReferenceError(js, "Could not set: %s\n", SDL_GetError()); -) - -JSC_CCALL(cmd_acquire_swapchain, - SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); - Uint32 w,h; - SDL_GPUTexture *texture; - SDL_AcquireGPUSwapchainTexture(cmds,global_window, &texture, &w, &h); - if (!texture) return JS_UNDEFINED; - JSValue swap = JS_UNDEFINED; - - JSValue *js_swapchains = ((prosperon_rt*)JS_GetContextOpaque(js))->js_swapchains; - - for (int i = 0; i < arrlen(js_swapchains); i++) { - if (js2SDL_GPUTexture(js,js_swapchains[i]) == texture) { - swap = js_swapchains[i]; - break; - } - } - - if (JS_IsUndefined(swap)) { - swap = SDL_GPUTexture2js(js,texture); - arrput(js_swapchains,swap); - } - - JS_SetPropertyStr(js,swap,"width", number2js(js,w)); - JS_SetPropertyStr(js,swap,"height", number2js(js,h)); - return JS_DupValue(js,swap); -) - -JSC_CCALL(cmd_swapchain_pass, - SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); - SDL_GPUColorTargetInfo info = {0}; - Uint32 w, h; - SDL_AcquireGPUSwapchainTexture(cmds, global_window, &info.texture, &w,&h); - info.load_op = SDL_GPU_LOADOP_CLEAR; - info.store_op = SDL_GPU_STOREOP_STORE; - colorf c = js2color(js,argv[0]); - info.clear_color = (SDL_FColor){c.r,c.g,c.b,c.a}; - SDL_GPURenderPass *pass = SDL_BeginGPURenderPass( - cmds, - &info, - 1, - NULL - ); - if (!pass) return JS_ThrowReferenceError(js, "Unable to create swapchain pass: %s", SDL_GetError()); - JSValue jspass = SDL_GPURenderPass2js(js,pass); - JS_SetPropertyStr(js,jspass,"size", vec22js(js,(HMM_Vec2){w,h})); - return jspass; -) - -JSC_CCALL(gpu_load_texture, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self); - SDL_Surface *surf = js2SDL_Surface(js, argv[0]); - if (!surf) return JS_ThrowReferenceError(js, "Surface was not a surface."); - - int compression_level = js2number(js,argv[1]); - if (compression_level < 0) compression_level = 0; - if (compression_level > 2) compression_level = 2; - - int dofree = 0; - - SDL_PixelFormat sfmt = surf->format; - const SDL_PixelFormatDetails *pdetails = SDL_GetPixelFormatDetails(sfmt); - -if (!pdetails) { - // If we can't get pixel format details, fall back to converting to RGBA8888 - surf = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA8888); - dofree = 1; - sfmt = SDL_PIXELFORMAT_RGBA8888; - pdetails = SDL_GetPixelFormatDetails(sfmt); - if (!pdetails) { - // Should never happen with RGBA8888, but just in case - return JS_ThrowReferenceError(js, "Unable to get pixel format details."); - } -} - -// Check if format has alpha -bool has_alpha = (pdetails->Amask != 0); - -// Choose a GPU format that closely matches the surface's format -SDL_GPUTextureFormat chosen_format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; - -switch (sfmt) { - case SDL_PIXELFORMAT_RGBA32: - case SDL_PIXELFORMAT_RGBX8888: - case SDL_PIXELFORMAT_XRGB8888: - case SDL_PIXELFORMAT_XBGR8888: - chosen_format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; - break; - - case SDL_PIXELFORMAT_RGBA4444: - case SDL_PIXELFORMAT_ABGR4444: - case SDL_PIXELFORMAT_ARGB4444: - case SDL_PIXELFORMAT_BGRA4444: - // 4-4-4-4 format - chosen_format = SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM; - break; - - case SDL_PIXELFORMAT_RGB565: - case SDL_PIXELFORMAT_BGR565: - // 5-6-5 format - chosen_format = SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM; - break; - - case SDL_PIXELFORMAT_RGBA5551: - case SDL_PIXELFORMAT_ARGB1555: - case SDL_PIXELFORMAT_BGRA5551: - case SDL_PIXELFORMAT_ABGR1555: - // 5-5-5-1 format - chosen_format = SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM; - break; - - default: - surf = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA32); - dofree = 1; - sfmt = SDL_PIXELFORMAT_RGBA32; - pdetails = SDL_GetPixelFormatDetails(sfmt); - chosen_format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; - has_alpha = true; - break; -} - -// If compression_level > 0, we override format with BC1/BC3 -bool compress = (compression_level > 0); -int stb_mode = STB_DXT_NORMAL; -SDL_GPUTextureFormat compressed_format = chosen_format; - -if (compress) { - if (has_alpha) - compressed_format = SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM; // DXT5 - else - compressed_format = SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM; // DXT1 - - if (compression_level > 1) stb_mode = STB_DXT_HIGHQUAL; -} - -SDL_GPUTexture *tex = SDL_CreateGPUTexture(gpu, &(SDL_GPUTextureCreateInfo) { - .type = SDL_GPU_TEXTURETYPE_2D, - .format = compress ? compressed_format : chosen_format, - .width = surf->w, - .height = surf->h, - .layer_count_or_depth = 1, - .num_levels = 1, - .usage = SDL_GPU_TEXTUREUSAGE_SAMPLER -}); - -const void *pixel_data = surf->pixels; -size_t pixel_data_size = surf->pitch * surf->h; -unsigned char *upload_data = NULL; -size_t upload_size = pixel_data_size; - -if (compress) { - // Compress with stb_dxt - int block_width = (surf->w + 3) / 4; - int block_height = (surf->h + 3) / 4; - int block_size = (compressed_format == SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM) ? 8 : 16; - int blocks_count = block_width * block_height; - - unsigned char *compressed_data = (unsigned char*)malloc(blocks_count * block_size); - if (!compressed_data) { - if (dofree) SDL_DestroySurface(surf); - return JS_ThrowOutOfMemory(js); - } - - const unsigned char *src_pixels = (const unsigned char *)pixel_data; - for (int by = 0; by < block_height; ++by) { - for (int bx = 0; bx < block_width; ++bx) { - unsigned char block_rgba[4*4*4]; - memset(block_rgba, 0, sizeof(block_rgba)); - - for (int y = 0; y < 4; ++y) { - for (int x = 0; x < 4; ++x) { - int sx = bx*4 + x; - int sy = by*4 + y; - if (sx < surf->w && sy < surf->h) { - const unsigned char *pixel = src_pixels + sy * surf->pitch + sx * pdetails->bytes_per_pixel; - Uint32 pixelValue = 0; - // Copy pixel data into a Uint32 for SDL_GetRGBA-like extraction - // We'll use masks/shifts from pdetails - memcpy(&pixelValue, pixel, pdetails->bytes_per_pixel); - - // Extract RGBA - unsigned char r = (pixelValue & pdetails->Rmask) >> pdetails->Rshift; - unsigned char g = (pixelValue & pdetails->Gmask) >> pdetails->Gshift; - unsigned char b = (pixelValue & pdetails->Bmask) >> pdetails->Bshift; - unsigned char a = pdetails->Amask ? ((pixelValue & pdetails->Amask) >> pdetails->Ashift) : 255; - - // If bits are not fully 8-bit, scale them: - // pdetails->Rbits (etc.) give how many bits each channel uses - int Rmax = (1 << pdetails->Rbits) - 1; - int Gmax = (1 << pdetails->Gbits) - 1; - int Bmax = (1 << pdetails->Bbits) - 1; - int Amax = pdetails->Abits ? ((1 << pdetails->Abits) - 1) : 255; - - if (pdetails->Rbits < 8) r = (r * 255) / Rmax; - if (pdetails->Gbits < 8) g = (g * 255) / Gmax; - if (pdetails->Bbits < 8) b = (b * 255) / Bmax; - if (pdetails->Amask && pdetails->Abits < 8) a = (a * 255) / Amax; - - // Set block pixel - block_rgba[(y*4+x)*4+0] = r; - block_rgba[(y*4+x)*4+1] = g; - block_rgba[(y*4+x)*4+2] = b; - block_rgba[(y*4+x)*4+3] = a; - } - } - } - - unsigned char *dest_block = compressed_data + (by * block_width + bx)*block_size; - int alpha = (compressed_format == SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM) ? 1 : 0; - stb_compress_dxt_block(dest_block, block_rgba, alpha, stb_mode); - } - } - - upload_data = compressed_data; - upload_size = blocks_count * block_size; - } else { - // No compression, upload directly - upload_data = (unsigned char*)pixel_data; - upload_size = pixel_data_size; - } - - SDL_GPUTransferBuffer *tex_buffer = SDL_CreateGPUTransferBuffer( - gpu, - &(SDL_GPUTransferBufferCreateInfo) { - .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, - .size = upload_size - }); - void *tex_ptr = SDL_MapGPUTransferBuffer(gpu, tex_buffer, false); - memcpy(tex_ptr, upload_data, upload_size); - SDL_UnmapGPUTransferBuffer(gpu, tex_buffer); - - SDL_GPUCommandBuffer *uploadcmd = SDL_AcquireGPUCommandBuffer(gpu); - SDL_GPUCopyPass *copypass = SDL_BeginGPUCopyPass(uploadcmd); - SDL_UploadToGPUTexture( - copypass, - &(SDL_GPUTextureTransferInfo) { - .transfer_buffer = tex_buffer, - .offset = 0 - }, - &(SDL_GPUTextureRegion) { - .texture = tex, - .w = surf->w, - .h = surf->h, - .d = 1 - }, - false -); - - SDL_EndGPUCopyPass(copypass); - SDL_SubmitGPUCommandBuffer(uploadcmd); - SDL_ReleaseGPUTransferBuffer(gpu, tex_buffer); - - if (compress) { - free(upload_data); - } - - ret = SDL_GPUTexture2js(js, tex); - JS_SetProperty(js, ret, width, number2js(js, surf->w)); - JS_SetProperty(js, ret, height, number2js(js, surf->h)); - JS_SetPropertyStr(js,ret,"dim", vec22js(js,(HMM_Vec2){surf->w,surf->h})); - - if (dofree) SDL_DestroySurface(surf); - -) - -static JSValue js_gpu_make_pipeline(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - if (argc < 1) - return JS_ThrowTypeError(js, "gpu_pipeline requires a pipeline object"); - - JSValue pipe = argv[0]; - if (!JS_IsObject(pipe)) - return JS_ThrowTypeError(js, "gpu_pipeline argument must be an object"); - - SDL_GPUGraphicsPipelineCreateInfo info = {0}; - - // --------------------------------------------------- - // 2. Retrieve vertex buffer descriptions array - // --------------------------------------------------- - JSValue vbd_val = JS_GetPropertyStr(js, pipe, "vertex_buffer_descriptions"); - Uint32 vbd_len = js_arrlen(js,vbd_val); - SDL_GPUVertexBufferDescription vbd[vbd_len]; - for (Uint32 i = 0; i < vbd_len; i++) { - JSValue elem = JS_GetPropertyUint32(js, vbd_val, i); - if (JS_IsObject(elem)) { - JSValue slot_val = JS_GetPropertyStr(js, elem, "slot"); - JSValue pitch_val = JS_GetPropertyStr(js, elem, "pitch"); - JSValue rate_val = JS_GetPropertyStr(js, elem, "input_rate"); - JSValue step_val = JS_GetPropertyStr(js, elem, "instance_step_rate"); - - // Slot - Uint32 slot = 0; - JS_ToUint32(js, &slot, slot_val); - JS_FreeValue(js, slot_val); - - // Pitch - Uint32 pitch = 0; - JS_ToUint32(js, &pitch, pitch_val); - JS_FreeValue(js, pitch_val); - - // Input Rate - const char *rate_str = JS_ToCString(js, rate_val); - SDL_GPUVertexInputRate input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX; - if (rate_str) { - if (!strcmp(rate_str, "vertex")) input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX; - else if (!strcmp(rate_str, "instance")) input_rate = SDL_GPU_VERTEXINPUTRATE_INSTANCE; - JS_FreeCString(js, rate_str); - } - JS_FreeValue(js, rate_val); - - // Instance Step Rate - Uint32 step_rate = 0; - JS_ToUint32(js, &step_rate, step_val); - JS_FreeValue(js, step_val); - - vbd[i].slot = slot; - vbd[i].pitch = pitch; - vbd[i].input_rate = input_rate; - vbd[i].instance_step_rate = step_rate; - } - JS_FreeValue(js, elem); - } - JS_FreeValue(js, vbd_val); - - // --------------------------------------------------- - // 3. Retrieve vertex attributes array - // --------------------------------------------------- - JSValue vat_val = JS_GetPropertyStr(js, pipe, "vertex_attributes"); - Uint32 vat_len = js_arrlen(js,vat_val); - - SDL_GPUVertexAttribute vat[vat_len]; - for (Uint32 i = 0; i < vat_len; i++) { - JSValue elem = JS_GetPropertyUint32(js, vat_val, i); - if (JS_IsObject(elem)) { - JSValue loc_val = JS_GetPropertyStr(js, elem, "location"); - JSValue slot_val = JS_GetPropertyStr(js, elem, "buffer_slot"); - JSValue fmt_val = JS_GetPropertyStr(js, elem, "format"); - JSValue off_val = JS_GetPropertyStr(js, elem, "offset"); - - // location - Uint32 location = 0; - JS_ToUint32(js, &location, loc_val); - JS_FreeValue(js, loc_val); - - // buffer_slot - Uint32 buffer_slot = 0; - JS_ToUint32(js, &buffer_slot, slot_val); - JS_FreeValue(js, slot_val); - - // format - const char *fmt_str = JS_ToCString(js, fmt_val); - SDL_GPUVertexElementFormat format = -1; // TODO: Check for error (if this is still -1) - if (fmt_str) { - // Map from string to SDL_GPUVertexElementFormat - if (!strcmp(fmt_str, "float2")) format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2; - else if (!strcmp(fmt_str, "float3")) format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; - else if (!strcmp(fmt_str, "float4")) format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4; - else if (!strcmp(fmt_str, "color")) - format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4; - JS_FreeCString(js, fmt_str); - } - JS_FreeValue(js, fmt_val); - - // offset - Uint32 offset = 0; - JS_ToUint32(js, &offset, off_val); - JS_FreeValue(js, off_val); - - vat[i].location = location; - vat[i].buffer_slot = buffer_slot; - vat[i].format = format; - vat[i].offset = offset; - } - JS_FreeValue(js, elem); - } - JS_FreeValue(js, vat_val); - - info.vertex_input_state = (SDL_GPUVertexInputState){ - .vertex_buffer_descriptions = vbd, - .num_vertex_buffers = vbd_len, - .vertex_attributes = vat, - .num_vertex_attributes = vat_len - }; - - // Vertex Shader - JSValue vertex_val = JS_GetPropertyStr(js, pipe, "vertex"); - JSValue fragment_val = JS_GetPropertyStr(js,pipe,"fragment"); - JS_GETPROP(js,info.vertex_shader, vertex_val, gpu, SDL_GPUShader) - JS_GETPROP(js, info.fragment_shader, fragment_val, gpu, SDL_GPUShader) - - JS_FreeValue(js, vertex_val); - JS_FreeValue(js, fragment_val); - - // Primitive Type - JS_GETPROP(js, info.primitive_type, pipe, primitive, SDL_GPUPrimitiveType) - - // Rasterizer State - JS_GETPROP(js, info.rasterizer_state.fill_mode, pipe, fill, SDL_GPUFillMode) - JS_GETPROP(js, info.rasterizer_state.cull_mode, pipe, cull, SDL_GPUCullMode) - JS_GETPROP(js, info.rasterizer_state.front_face, pipe, face, SDL_GPUFrontFace) - - // Depth Stencil State - JSValue depth_val = JS_GetPropertyStr(js, pipe, "depth"); - if (JS_IsObject(depth_val)) { - JS_GETPROP(js,info.depth_stencil_state.compare_op, depth_val, compare, SDL_GPUCompareOp) - JS_GETPROP(js,info.depth_stencil_state.enable_depth_test, depth_val, test, bool) - JS_GETPROP(js,info.depth_stencil_state.enable_depth_write, depth_val, write, bool) - JS_GETPROP(js,info.rasterizer_state.depth_bias_constant_factor, depth_val, bias, number) - JS_GETPROP(js,info.rasterizer_state.depth_bias_slope_factor, depth_val,bias_slope_scale, number) - JS_GETPROP(js,info.rasterizer_state.depth_bias_clamp, depth_val,bias_clamp_val, number) - } - JS_FreeValue(js, depth_val); - - // Stencil State - JSValue stencil_val = JS_GetPropertyStr(js, pipe, "stencil"); - if (JS_IsObject(stencil_val)) { - JS_GETPROP(js,info.depth_stencil_state.enable_stencil_test, stencil_val, enabled, bool) - if (info.depth_stencil_state.enable_stencil_test) { - JS_GETPROP(js, info.depth_stencil_state.front_stencil_state, stencil_val, front, SDL_GPUStencilOpState) - JS_GETPROP(js, info.depth_stencil_state.back_stencil_state, stencil_val, back, SDL_GPUStencilOpState) - - JSValue compare_mask_val = JS_GetPropertyStr(js, stencil_val, "compare_mask"); - uint32_t tmp; - JS_ToUint32(js, &tmp, compare_mask_val); - info.depth_stencil_state.compare_mask = tmp; - JS_FreeValue(js, compare_mask_val); - - // Write Mask - JSValue write_mask_val = JS_GetPropertyStr(js, stencil_val, "write_mask"); - JS_ToUint32(js, &tmp, write_mask_val); - info.depth_stencil_state.write_mask = tmp; - } - } - JS_FreeValue(js, stencil_val); - - JSValue js_tar = JS_GetPropertyStr(js,pipe,"target"); - - SDL_GPUGraphicsPipelineTargetInfo target_info = {0}; - JSValue color_tars = JS_GetPropertyStr(js,js_tar,"color_targets"); - target_info.num_color_targets = js_arrlen(js,color_tars); - SDL_GPUColorTargetDescription dsc[target_info.num_color_targets]; - target_info.color_target_descriptions = dsc; - - for (int i = 0; i < target_info.num_color_targets; i++) { - JSValue c = JS_GetPropertyUint32(js,color_tars,i); - dsc[i] = js2SDL_GPUColorTargetDescription(js,c); - JS_FreeValue(js,c); - } - JS_FreeValue(js,color_tars); - - JS_GETPROP(js,target_info.depth_stencil_format,js_tar,depth,SDL_GPUTextureFormat); - if (target_info.depth_stencil_format) target_info.has_depth_stencil_target = 1; - - info.target_info = target_info; - - JS_FreeValue(js, js_tar); - - // Create the pipeline - SDL_GPUGraphicsPipeline *pipeline = SDL_CreateGPUGraphicsPipeline(gpu, &info); - if (!pipeline) return JS_ThrowInternalError(js, "Failed to create GPU pipeline"); - - return SDL_GPUGraphicsPipeline2js(js, pipeline); -} - -// Now the function to create the sampler -static JSValue js_gpu_make_sampler(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - SDL_GPUSamplerCreateInfo info = {0}; - JSValue sampler = argv[0]; - - JS_GETPROP(js,info.min_filter,sampler,min_filter,SDL_GPUFilter) - JS_GETPROP(js,info.mag_filter,sampler,mag_filter,SDL_GPUFilter) - JS_GETPROP(js,info.mipmap_mode, sampler, mipmap, SDL_GPUSamplerMipmapMode) - JS_GETPROP(js,info.address_mode_u, sampler, u, SDL_GPUSamplerAddressMode) - JS_GETPROP(js,info.address_mode_v, sampler, v, SDL_GPUSamplerAddressMode) - JS_GETPROP(js,info.address_mode_w, sampler, w, SDL_GPUSamplerAddressMode) - JS_GETPROP(js,info.mip_lod_bias, sampler, mip_bias, number) - JS_GETPROP(js,info.max_anisotropy, sampler, max_anisotropy, number) - JS_GETPROP(js,info.compare_op,sampler,compare_op,SDL_GPUCompareOp) - JS_GETPROP(js,info.min_lod,sampler,min_lod,number) - JS_GETPROP(js,info.max_lod,sampler,max_lod,number) - JS_GETPROP(js, info.enable_anisotropy, sampler, anistropy, bool) - JS_GETPROP(js, info.enable_compare, sampler, compare, bool) - - // Create the sampler - SDL_GPUSampler *sdl_sampler = SDL_CreateGPUSampler(gpu, &info); - if (!sdl_sampler) return JS_ThrowInternalError(js, "Failed to create GPU sampler"); - - return SDL_GPUSampler2js(js, sdl_sampler); // You need to implement SDL_GPUSampler2js similar to pipeline -} - - -JSC_CCALL(gpu_texture, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - SDL_GPUTextureCreateInfo info = {0}; - JSValue fmt = argv[0]; - JS_GETPROP(js, info.width, fmt, width, number) - JS_GETPROP(js, info.height, fmt, height, number) - JS_GETPROP(js, info.layer_count_or_depth, fmt, layers, number) - JS_GETPROP(js, info.num_levels, fmt, mip_levels, number) - JS_GETPROP(js, info.sample_count, fmt, samples, number) - JS_GETPROP(js, info.type, fmt, type, SDL_GPUTextureType) - JS_GETPROP(js, info.format, fmt, format, SDL_GPUTextureFormat) - if (JS_GETBOOL(js,fmt,"sampler")) - info.usage |= SDL_GPU_TEXTUREUSAGE_SAMPLER; - if (JS_GETBOOL(js,fmt,"color_target")) - info.usage |= SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; - if (JS_GETBOOL(js,fmt,"depth_target")) - info.usage |= SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; - if (JS_GETBOOL(js,fmt,"read")) - info.usage |= (SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ); - if (JS_GETBOOL(js,fmt,"write")) - info.usage |= SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE; - if (JS_GETBOOL(js,fmt,"readwrite")) - info.usage |= SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE; - - SDL_GPUTexture *tex = SDL_CreateGPUTexture(gpu,&info); - if (!tex) return JS_ThrowReferenceError(js, "Unable to create texture: %s", SDL_GetError()); - JSValue jstex = SDL_GPUTexture2js(js,tex); - JS_SetPropertyStr(js,jstex,"width", number2js(js,info.width)); - JS_SetPropertyStr(js,jstex,"height", number2js(js,info.height)); - JS_SetPropertyStr(js,jstex,"dim", vec22js(js,(HMM_Vec2){info.width, info.height})); - return jstex; -) - -static HMM_Vec3 base_quad[4] = { - {0.0,0.0,1.0}, - {1.0,0.0,1.0}, - {0.0,1.0,1.0}, - {1.0,1.0,1.0} -}; - -static inline void add_quad(text_vert **verts, rect *restrict src, rect *restrict dst) -{ - text_vert v = (text_vert){ - .pos = (HMM_Vec2){dst->x, dst->y}, - .uv = (HMM_Vec2){src->x,src->y}, - .color = (HMM_Vec4){1,1,1,1} - }; - arrput(*verts, v); - - v = (text_vert){ - .pos = (HMM_Vec2){dst->x+dst->w, dst->y}, - .uv = (HMM_Vec2){src->x+src->w,src->y}, - .color = (HMM_Vec4){1,1,1,1} - }; - arrput(*verts, v); - - v = (text_vert){ - .pos = (HMM_Vec2){dst->x, dst->y+dst->h}, - .uv = (HMM_Vec2){src->x,src->y+src->h}, - .color = (HMM_Vec4){1,1,1,1} - }; - arrput(*verts, v); - - v = (text_vert){ - .pos = (HMM_Vec2){dst->x+dst->w, dst->y+dst->h}, - .uv = (HMM_Vec2){src->x+src->w,src->y+src->h}, - .color = (HMM_Vec4){1,1,1,1} - }; - arrput(*verts, v); -} - -typedef struct { - text_vert vert[4]; -} quad; - -// Comparator inline for potential compiler inlining -static inline int sprite_compare(const sprite *a, const sprite *b) { - if (a->layer != b->layer) return a->layer - b->layer; - - if (a->pos.Y != b->pos.Y) - return (b->pos.Y - a->pos.Y); - - if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) - return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; - - return 0; -} - -// Merge two sorted sub-ranges [left..mid] and [mid+1..right] -static void merge_sprites(sprite *arr, sprite *temp, size_t left, size_t mid, size_t right) { - size_t i = left, j = mid + 1, k = left; - - while (i <= mid && j <= right) - if (sprite_compare(&arr[i], &arr[j]) <= 0) temp[k++] = arr[i++]; - else temp[k++] = arr[j++]; - - while (i <= mid) temp[k++] = arr[i++]; - while (j <= right) temp[k++] = arr[j++]; - - for (size_t p = left; p <= right; p++) - arr[p] = temp[p]; -} - -// Recursive mergesort -static void merge_sort_recursive(sprite *arr, sprite *temp, size_t left, size_t right) { - if (left >= right) return; - size_t mid = (left + right) / 2; - merge_sort_recursive(arr, temp, left, mid); - merge_sort_recursive(arr, temp, mid + 1, right); - merge_sprites(arr, temp, left, mid, right); -} - -// Public function to sort the array of sprites with mergesort -void better_sort_sprites(sprite *arr, size_t length) { - if (length < 2) return; - sprite *temp = malloc(length * sizeof(sprite)); - if (!temp) return; // fallback or handle error - merge_sort_recursive(arr, temp, 0, length - 1); - free(temp); -} - -int sort_sprite_backtofront(const sprite *a, const sprite *b) -{ - if (a->layer != b->layer) return a->layer - b->layer; - if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) - return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; - return 0; -} - -int sort_sprite_fronttoback(const sprite *a, const sprite *b) -{ - if (a->layer != b->layer) return b->layer - a->layer; - if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) - return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; - return 0; -} - -int sort_sprite_texture(const sprite *a, const sprite *b) -{ - if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) - return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; - return 0; -} - -int sort_sprite(const sprite *a, const sprite *b) -{ - if (a->layer != b->layer) return a->layer - b->layer; - - if (a->pos.Y != b->pos.Y) - return (b->pos.Y - a->pos.Y); - - if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) - return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; - return 0; -} - -JSC_CCALL(gpu_sort_sprite, - JSValue a = argv[0]; - JSValue b = argv[1]; - - int alayer, blayer; - JS_GETATOM(js,alayer,a,layer,number) - JS_GETATOM(js,blayer,b,layer,number) - if (alayer != blayer) return number2js(js,alayer - blayer); - - rect ar, br; - JS_GETATOM(js,ar,a,rect,rect) - JS_GETATOM(js,br,b,rect,rect) - if (ar.y != br.y) return number2js(js,br.y-ar.y); - - JSValue aimg,bimg; - aimg = JS_GetProperty(js,a,image); - bimg = JS_GetProperty(js,b,image); - JS_FreeValue(js,aimg); - JS_FreeValue(js,bimg); - if (!JS_SameValue(js,aimg,bimg)) return number2js(js,JS_VALUE_GET_PTR(aimg) < JS_VALUE_GET_PTR(bimg) ? -1 : 1); - return number2js(js,0); -) - -JSC_CCALL(gpu_make_sprite_queue, - sprite *sprites = NULL; - size_t quads = 0; - int needfree = 1; - - 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++) - JS_DupValue(js,sprites[i].image); - } else { - quads = js_arrlen(js, argv[0]); - if (quads == 0) - return JS_ThrowReferenceError(js, "Expected an array of sprites with length > 0."); - - arrsetcap(sprites, quads); - - for (int i = 0; i < quads; i++) { - JSValue sub = JS_GetPropertyUint32(js, argv[0], i); - sprite *jsp = js2sprite(js, sub); - if (jsp) { - arrput(sprites, *jsp); - JS_DupValue(js,jsp->image); - } - else { - sprite sp = {0}; - JS_GETATOM(js, sp.pos, sub, pos, vec2) - JS_GETATOM(js, sp.center, sub, center, vec2) - JS_GETATOM(js, sp.skew, sub, skew, vec2) - JS_GETATOM(js, sp.scale, sub, scale, vec2) - JS_GETATOM(js, sp.color,sub,color,color) - JS_GETATOM(js, sp.layer,sub,layer,number) - sp.image = JS_GetProperty(js,sub,image); - sprite_apply(&sp); - arrput(sprites,sp); - } - JS_FreeValue(js, sub); - } - } - - qsort(sprites, quads, sizeof(sprite), sort_sprite); - - struct quad_buffers buffers = quad_buffers_new(quads*4); - - const HMM_Vec2 local[4] = { {0,0}, {1,0}, {0,1}, {1,1} }; - - rect uv; - rect uv_px; - JSValue cur_img = JS_UNDEFINED; - - for (size_t i = 0; i < quads; i++) { - sprite *s = &sprites[i]; - if (JS_IsUndefined(cur_img) || !JS_StrictEq(js, s->image, cur_img)) { - cur_img = s->image; - JS_GETATOM(js, uv, cur_img, rect, rect) - JS_GETATOM(js, uv_px, cur_img, rect_px, rect) - } - - HMM_Vec2 px_size = { - uv_px.w * s->scale.X, - uv_px.h * s->scale.Y - }; - - HMM_Vec2 anchor = { - px_size.X * s->center.X, - px_size.Y * s->center.Y - }; - - size_t base = i * 4; - - for (int v = 0; v < 4; v++) { - HMM_Vec2 lp = { - local[v].X * px_size.X - anchor.X, - local[v].Y * px_size.Y - anchor.Y - }; - - HMM_Vec2 world = HMM_AddV2(s->pos, HMM_MulM2V2(s->affine, lp)); - - buffers.pos[base + v] = world; - buffers.color[base + v] = s->color; - } - - /* UVs are still top-left-origin pixel coords, so keep previous packing */ - buffers.uv[base + 0] = (HMM_Vec2){ uv.x, uv.y + uv.h }; - buffers.uv[base + 1] = (HMM_Vec2){ uv.x + uv.w, uv.y + uv.h }; - buffers.uv[base + 2] = (HMM_Vec2){ uv.x, uv.y }; - buffers.uv[base + 3] = (HMM_Vec2){ uv.x + uv.w, uv.y }; - } - - JSValue mesh = quadbuffers_to_mesh(js, buffers); - - ret = JS_NewArray(js); - int first_index = 0; - int count = 0; - int n = 0; - JSValue img = JS_UNDEFINED; - - for (int i = 0; i < quads; i++) { - if (!JS_SameValue(js, sprites[i].image, img)) { - if (count > 0) { - JSValue q = JS_NewObject(js); - JS_SetPropertyStr(js, q, "type", JS_NewString(js, "geometry")); - JS_SetPropertyStr(js, q, "mesh", JS_DupValue(js, mesh)); - JS_SetPropertyStr(js, q, "pipeline", JS_DupValue(js, argv[2])); - JS_SetPropertyStr(js, q, "image", img); - JS_SetPropertyStr(js, q, "first_index", number2js(js, first_index)); - JS_SetPropertyStr(js, q, "num_indices", number2js(js, count * 6)); - JS_SetPropertyUint32(js, ret, n++, q); - } - first_index = i*6; - count = 1; - img = JS_DupValue(js, sprites[i].image); - } else count++; - JS_FreeValue(js,sprites[i].image); - } - - if (count > 0) { - JSValue q = JS_NewObject(js); - JS_SetPropertyStr(js, q, "type", JS_NewString(js, "geometry")); - JS_SetPropertyStr(js, q, "mesh", JS_DupValue(js, mesh)); - JS_SetPropertyStr(js, q, "pipeline", JS_DupValue(js, argv[2])); - JS_SetPropertyStr(js, q, "image", img); - JS_SetPropertyStr(js, q, "first_index", number2js(js, first_index)); - JS_SetPropertyStr(js, q, "num_indices", number2js(js, count * 6)); - JS_SetPropertyUint32(js, ret, n++, q); - } - - if (needfree) - arrfree(sprites); - - JS_FreeValue(js, mesh); -) - -JSC_CCALL(gpu_make_sprite_mesh, - size_t quads = js_arrlen(js, argv[0]); - size_t verts = quads*4; - size_t count = quads*6; - - // Prepare arrays on CPU - HMM_Vec2 *posdata = malloc(sizeof(*posdata)*verts); - HMM_Vec2 *uvdata = malloc(sizeof(*uvdata)*verts); - HMM_Vec4 *colordata = malloc(sizeof(*colordata)*verts); - - for (int i = 0; i < quads; i++) { - JSValue sub = JS_GetPropertyUint32(js,argv[0],i); - - transform *tr; - rect src; - HMM_Vec4 color; - JS_GETATOM(js,src,sub,src,rect) - JS_GETATOM(js,color,sub,color,color) - JS_GETATOM(js,tr,sub,transform,transform) - JS_FreeValue(js,sub); - - size_t base = i*4; - if (tr) { - HMM_Mat3 trmat = transform2mat3(tr); - for (int j = 0; j < 4; j++) - posdata[base+j] = HMM_MulM3V3(trmat, base_quad[j]).xy; - } else { - rect dst; - JS_GETATOM(js,dst,sub,rect,rect); - posdata[base+0] = (HMM_Vec2){dst.x,dst.y}; - posdata[base + 1] = (HMM_Vec2){ dst.x+dst.w, dst.y }; - posdata[base + 2] = (HMM_Vec2){ dst.x, dst.y+dst.h }; - posdata[base + 3] = (HMM_Vec2){ dst.x+dst.w, dst.y+dst.h }; - } - - uvdata[base+0] = (HMM_Vec2){src.x, src.y+src.h}; - uvdata[base+1] = (HMM_Vec2){src.x+src.w, src.y+src.h}; - uvdata[base+2] = (HMM_Vec2){src.x, src.y}; - uvdata[base+3] = (HMM_Vec2){src.x+src.w, src.y}; - - colordata[base+0] = color; - colordata[base+1] = color; - colordata[base+2] = color; - colordata[base+3] = color; - } - - // Check old mesh - JSValue old_mesh = JS_UNDEFINED; - if (argc > 1) - old_mesh = argv[1]; - - // Needed sizes - size_t pos_size = sizeof(*posdata)*verts; - size_t uv_size = sizeof(*uvdata)*verts; - size_t color_size = sizeof(*colordata)*verts; - - BufferCheckResult pos_chk = get_or_extend_buffer(js, old_mesh, "pos", pos_size, JS_TYPED_ARRAY_FLOAT32, 2, 1, 0); - BufferCheckResult uv_chk = get_or_extend_buffer(js, old_mesh, "uv", uv_size, JS_TYPED_ARRAY_FLOAT32, 2, 1, 0); - BufferCheckResult color_chk = get_or_extend_buffer(js, old_mesh, "color", color_size, JS_TYPED_ARRAY_FLOAT32, 4, 1, 0); - - int need_new_all = pos_chk.need_new || uv_chk.need_new || color_chk.need_new; - - ret = JS_NewObject(js); - - if (need_new_all) { - // Create all new buffers - JSValue new_pos = make_gpu_buffer(js, posdata, pos_size, JS_TYPED_ARRAY_FLOAT32, 2, 1,0); - JSValue new_uv = make_gpu_buffer(js, uvdata, uv_size, JS_TYPED_ARRAY_FLOAT32, 2, 1,0); - JSValue new_color = make_gpu_buffer(js, colordata, color_size, JS_TYPED_ARRAY_FLOAT32, 0, 1,0); - - JS_SetProperty(js, ret, pos, new_pos); - JS_SetProperty(js, ret, uv, new_uv); - JS_SetProperty(js, ret, color, new_color); - - // Indices - JSValue indices = make_quad_indices_buffer(js, quads); - JS_SetProperty(js, ret, indices, indices); - } else { - // Reuse the old buffers - // Just copy data into existing buffers via their pointers - memcpy(pos_chk.ptr, posdata, pos_size); - memcpy(uv_chk.ptr, uvdata, uv_size); - memcpy(color_chk.ptr, colordata, color_size); - - // Duplicate old references since we're returning a new object - JS_SetProperty(js, ret, pos, JS_DupValue(js, pos_chk.val)); - JS_SetProperty(js, ret, uv, JS_DupValue(js, uv_chk.val)); - JS_SetProperty(js, ret, color, JS_DupValue(js, color_chk.val)); - - // Indices can remain the same if they were also large enough. If using a shared global index buffer: - JSValue indices = make_quad_indices_buffer(js, quads); - JS_SetProperty(js, ret, indices, indices); - } - - JS_SetProperty(js, ret, vertices, number2js(js, verts)); - JS_SetProperty(js, ret, count, number2js(js, count)); - JS_SetPropertyStr(js,ret,"num_indices", number2js(js,count)); - - // Free temporary CPU arrays - free(posdata); - free(uvdata); - free(colordata); - - // Free old buffer values if they were fetched - if (!JS_IsUndefined(pos_chk.val)) JS_FreeValue(js, pos_chk.val); - if (!JS_IsUndefined(uv_chk.val)) JS_FreeValue(js, uv_chk.val); - if (!JS_IsUndefined(color_chk.val)) JS_FreeValue(js, color_chk.val); - - return ret; -) - -JSC_CCALL(gpu_make_quad, - size_t quads = 1; - size_t verts = quads*4; - size_t count = quads*6; - - // Prepare arrays on CPU - HMM_Vec2 *posdata = malloc(sizeof(*posdata)*verts); - HMM_Vec2 *uvdata = malloc(sizeof(*uvdata)*verts); - HMM_Vec4 *colordata = malloc(sizeof(*colordata)*verts); - HMM_Vec4 usecolor = (HMM_Vec4){1,1,1,1}; - posdata[0] = (HMM_Vec2){0,1}; - posdata[1] = (HMM_Vec2){1,1}; - posdata[2] = (HMM_Vec2){0,0}; - posdata[3] = (HMM_Vec2){1,0}; - - uvdata[0] = (HMM_Vec2){0,0}; - uvdata[1] = (HMM_Vec2){1,0}; - uvdata[2] = (HMM_Vec2){0,1}; - uvdata[3] = (HMM_Vec2){1,1}; - - colordata[0] = usecolor; - colordata[1] = usecolor; - colordata[2] = usecolor; - colordata[3] = usecolor; - - ret = JS_NewObject(js); - JSValue pos = make_gpu_buffer(js, posdata, sizeof(*posdata)*verts, JS_TYPED_ARRAY_FLOAT32, 2, 1,0); - JSValue uv = make_gpu_buffer(js, uvdata, sizeof(*uvdata)*verts, JS_TYPED_ARRAY_FLOAT32, 2, 1,0); - JSValue color = make_gpu_buffer(js, colordata, sizeof(*colordata)*verts, JS_TYPED_ARRAY_FLOAT32, 0, 1,0); - - JS_SetProperty(js, ret, pos, pos); - JS_SetProperty(js, ret, uv, uv); - JS_SetProperty(js, ret, color, color); - JSValue indices = make_quad_indices_buffer(js, quads); - JS_SetProperty(js, ret, indices, indices); - - JS_SetProperty(js, ret, vertices, number2js(js, verts)); - JS_SetProperty(js, ret, count, number2js(js, count)); - JS_SetPropertyStr(js,ret,"num_indices", number2js(js,count)); - - // Free temporary CPU arrays - free(posdata); - free(uvdata); - free(colordata); - - return ret; -) - - -JSC_CCALL(gpu_driver, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - ret = JS_NewString(js, SDL_GetGPUDeviceDriver(gpu)); -) - -static JSValue js_gpu_make_shader(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { - if (argc < 1 || !JS_IsObject(argv[0])) - return JS_ThrowTypeError(js, "make_shader expects an object with code, stage, num_samplers, num_textures, num_storage_buffers, num_uniform_buffers"); - - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self); - - JSValue obj = argv[0]; - - SDL_GPUShaderCreateInfo info = {0}; - - // code - JSValue code_val = JS_GetPropertyStr(js, obj, "code"); - size_t code_size; - void *code_data = JS_GetArrayBuffer(js, &code_size, code_val); - JS_FreeValue(js, code_val); - if (!code_data) - return JS_ThrowTypeError(js, "shader.code must be an ArrayBuffer"); - - // stage - JSValue stage_val = JS_GetPropertyStr(js, obj, "stage"); - const char *stage_str = JS_ToCString(js, stage_val); - if (stage_str) { - if (!strcmp(stage_str, "vertex")) info.stage = SDL_GPU_SHADERSTAGE_VERTEX; - else if (!strcmp(stage_str, "fragment")) info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT; - JS_FreeCString(js, stage_str); - } // TODO: Make this its own function - JS_FreeValue(js, stage_val); - - // num_samplers - JS_GETPROP(js, info.num_samplers, obj, num_samplers, number) - JS_GETPROP(js, info.num_storage_textures, obj, num_textures, number) - JS_GETPROP(js, info.num_storage_buffers, obj, num_storage_buffers, number) - JS_GETPROP(js, info.num_uniform_buffers, obj, num_uniform_buffers, number) - JS_GETPROP(js, info.format, obj, format, SDL_GPUShaderFormat) - - JSValue entry_val = JS_GetPropertyStr(js,obj,"entrypoint"); - info.entrypoint = JS_ToCString(js,entry_val); - JS_FreeValue(js,entry_val); - - info.code_size = code_size; - info.code = code_data; - info.props = 0; // No extension properties by default - - JS_FreeCString(js,info.entrypoint); - - SDL_GPUShader *shader = SDL_CreateGPUShader(gpu, &info); - if (!shader) - return JS_ThrowReferenceError(js, "Unable to create shader: %s", SDL_GetError()); - - return SDL_GPUShader2js(js, shader); -} - -JSC_CCALL(gpu_acquire_cmd_buffer, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self); - SDL_GPUCommandBuffer *cb = SDL_AcquireGPUCommandBuffer(gpu); - if (!cb) return JS_ThrowReferenceError(js,"Unable to acquire command buffer: %s", SDL_GetError()); - // Wrap cb in a JS object - return SDL_GPUCommandBuffer2js(js, cb); -) - -/* takes argv - 0: a command buffer to write to - 1: a buffer or array of buffers to upload - 2: an optional transfer buffer to use; if undefined a temporary one is used -*/ -JSC_CCALL(gpu_upload, - JSValue js_cmd = argv[0]; - JSValue js_buffers = argv[1]; - JSValue js_transfer = argv[2]; - - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self); - if (!gpu) - return JS_ThrowTypeError(js, "Invalid GPU device"); - - SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, js_cmd); - if (!cmds) - return JS_ThrowTypeError(js, "Invalid command buffer"); - - if (!JS_IsArray(js, js_buffers)) - return JS_ThrowTypeError(js, "buffers must be an array"); - - size_t len = js_arrlen(js, js_buffers); - if (len == 0) - return JS_DupValue(js, js_transfer); // No data to upload, just return existing transfer buffer - - struct { - SDL_GPUBuffer *gpu_buffer; - void *data; - size_t size; - } *items = malloc(sizeof(*items) * len); - - if (!items) - return JS_ThrowOutOfMemory(js); - - size_t total_size_needed = 0; - - for (size_t i = 0; i < len; i++) { - JSValue js_buf = JS_GetPropertyUint32(js, js_buffers, i); - - if (JS_IsUndefined(js_buf)) - continue; - - gpu_buffer_unpack(js, gpu, js_buf, &items[i].size, &items[i].data, &items[i].gpu_buffer); - total_size_needed += items[i].size; - JS_FreeValue(js, js_buf); - } - - SDL_GPUTransferBuffer *transfer = js2SDL_GPUTransferBuffer(js,js_transfer); - if (transfer) { - // ensure it's large enough - size_t transfer_size = js_getnum_str(js,js_transfer, "size"); - if (transfer_size < total_size_needed) { - total_size_needed *= 1.5; - transfer = SDL_CreateGPUTransferBuffer( gpu, &(SDL_GPUTransferBufferCreateInfo){ - .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, - .size = total_size_needed - } - ); - ret = SDL_GPUTransferBuffer2js(js,transfer); - JS_SetPropertyStr(js,ret,"size", number2js(js,total_size_needed)); - } else - ret = JS_DupValue(js,js_transfer); // supplied transfer buffer is fine so we use it - } else { - transfer = SDL_CreateGPUTransferBuffer( gpu, &(SDL_GPUTransferBufferCreateInfo){ - .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, - .size = total_size_needed - } - ); - ret = SDL_GPUTransferBuffer2js(js,transfer); - JS_SetPropertyStr(js,ret,"size", number2js(js,total_size_needed)); - } - - SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(cmds); - if (!copy_pass) { - free(items); - return JS_ThrowReferenceError(js, "Failed to begin copy pass"); - } - - void *mapped_data = SDL_MapGPUTransferBuffer(gpu, transfer, false); - if (!mapped_data) { - SDL_EndGPUCopyPass(copy_pass); - free(items); - return JS_ThrowReferenceError(js, "Failed to map transfer buffer: %s", SDL_GetError()); - } - - // Copy all data into the mapped transfer buffer - size_t current_offset = 0; - for (size_t i = 0; i < len; i++) { - memcpy((char*)mapped_data + current_offset, items[i].data, items[i].size); - current_offset += items[i].size; - } - SDL_UnmapGPUTransferBuffer(gpu, transfer); - - // Issue uploads for each item - current_offset = 0; - for (size_t i = 0; i < len; i++) { - SDL_UploadToGPUBuffer( - copy_pass, - &(SDL_GPUTransferBufferLocation){ - .transfer_buffer = transfer, - .offset = current_offset - }, - &(SDL_GPUBufferRegion){ - .buffer = items[i].gpu_buffer, - .offset = 0, - .size = items[i].size - }, - false - ); - current_offset += items[i].size; - } - - SDL_EndGPUCopyPass(copy_pass); - free(items); -) - -JSC_CCALL(gpu_wait_for_fences, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - int n = js_arrlen(js,argv[0]); - SDL_GPUFence *fences[n]; - for (int i = 0; i < n; i++) { - JSValue a = JS_GetPropertyUint32(js,argv[0],i); - fences[i] = js2SDL_GPUFence(js,a); - JS_FreeValue(js,a); - } - - int wait_all = JS_ToBool(js,argv[1]); - return JS_NewBool(js,SDL_WaitForGPUFences(gpu,wait_all,fences,n)); -) - -JSC_CCALL(gpu_query_fence, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - SDL_GPUFence *fence = js2SDL_GPUFence(js,argv[0]); - return JS_NewBool(js,SDL_QueryGPUFence(gpu,fence)); -) - -JSC_CCALL(gpu_shader_format, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - SDL_GPUShaderFormat fmt = SDL_GetGPUShaderFormats(gpu); - if (!fmt) return JS_ThrowReferenceError(js, "Shader format available invalid."); - - JSValue arr = JS_NewArray(js); - int i = 0; - if (fmt & SDL_GPU_SHADERFORMAT_PRIVATE) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, ".private")); - if (fmt & SDL_GPU_SHADERFORMAT_SPIRV) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "spv")); - if (fmt & SDL_GPU_SHADERFORMAT_DXBC) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "dxbc")); - if (fmt & SDL_GPU_SHADERFORMAT_DXIL) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "dxil")); - if (fmt & SDL_GPU_SHADERFORMAT_MSL) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "msl")); - if (fmt & SDL_GPU_SHADERFORMAT_METALLIB) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "metallib")); - return arr; -) - -JSC_CCALL(gpu_compute_pipeline, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - SDL_GPUComputePipelineCreateInfo info = {0}; - JSValue pipe = argv[0]; - JS_GETPROP(js, info.num_samplers, pipe, num_samplers, number) - JS_GETPROP(js,info.num_readonly_storage_textures,pipe,num_readonly_storage_textures,number) - JS_GETPROP(js,info.num_readonly_storage_buffers,pipe,num_readonly_storage_buffers,number) - JS_GETPROP(js,info.num_readwrite_storage_textures,pipe,num_readwrite_storage_textures,number) - JS_GETPROP(js,info.num_readwrite_storage_buffers,pipe,num_readwrite_storage_buffers,number) - JS_GETPROP(js,info.threadcount_x,pipe,threadcount_x,number) - JS_GETPROP(js,info.threadcount_y,pipe,threadcount_y,number) - JS_GETPROP(js,info.threadcount_z,pipe,threadcount_z,number) - JS_GETPROP(js,info.entrypoint,pipe,entrypoint,cstring) - - - JSValue shader = JS_GetPropertyStr(js,pipe,"shader"); - info.code = JS_GetArrayBuffer(js,&info.code_size, shader); - JS_FreeValue(js,shader); - - SDL_GPUComputePipeline *pipeline = SDL_CreateGPUComputePipeline(gpu, &info); - JS_FreeCString(js,info.entrypoint); - if (!pipeline) return JS_ThrowReferenceError(js,"Could not create compute pipeline: %s", SDL_GetError()); - return SDL_GPUComputePipeline2js(js,pipeline); -) - -static inline void tile_region(text_vert **verts, rect src_uv, rect dst, float tex_w, float tex_h, bool tile_x, bool tile_y) -{ - // Convert the incoming UV rect into pixel coords - rect src_px = { - .x = src_uv.x * tex_w, - .y = src_uv.y * tex_h, - .w = src_uv.w * tex_w, - .h = src_uv.h * tex_h - }; - - // If src_px or dst is degenerate, early out - if (src_px.w <= 0.0f || src_px.h <= 0.0f || dst.w <= 0.0f || dst.h <= 0.0f) - return; - - // How many full tiles horizontally/vertically? - // If not tiling in a dimension, we treat it as exactly 1 tile (no leftover). - float cols_f, rows_f; - float remain_wf = 0.0f; - float remain_hf = 0.0f; - - if (tile_x) { - remain_wf = modff(dst.w / src_px.w, &cols_f); - } else { - // Only 1 "column" covering entire width, no leftover - cols_f = 1.0f; - remain_wf = 0.0f; - } - - if (tile_y) { - remain_hf = modff(dst.h / src_px.h, &rows_f); - } else { - // Only 1 "row" covering entire height, no leftover - rows_f = 1.0f; - remain_hf = 0.0f; - } - - int cols = (int)cols_f; - int rows = (int)rows_f; - - // The leftover portion in screen coords (pixels) - float remain_dst_w = remain_wf * src_px.w; - float remain_dst_h = remain_hf * src_px.h; - - // Build the UV rect for a “full” tile - rect src_full = src_uv; - - // Partial leftover in UV - float remain_src_w_uv = remain_dst_w / tex_w; - float remain_src_h_uv = remain_dst_h / tex_h; - - // For partial leftover in X dimension - rect src_partial_x = src_full; - src_partial_x.w = remain_src_w_uv; - - // For partial leftover in Y dimension - rect src_partial_y = src_full; - src_partial_y.h = remain_src_h_uv; - - // For partial leftover in both X & Y - rect src_partial_xy = src_full; - src_partial_xy.w = remain_src_w_uv; - src_partial_xy.h = remain_src_h_uv; - - // Each tile is drawn 1:1 in screen coords - float tile_w = tile_x ? src_px.w : dst.w; // If not tiling horizontally, match the entire dst width - float tile_h = tile_y ? src_px.h : dst.h; // If not tiling vertically, match the entire dst height - - rect curr_dst; - curr_dst.w = tile_w; - curr_dst.h = tile_h; - curr_dst.y = dst.y; - - // Loop over rows - for (int y = 0; y < rows; y++) { - curr_dst.x = dst.x; - - // Loop over columns - for (int x = 0; x < cols; x++) { - add_quad(verts, &src_full, &curr_dst); - curr_dst.x += tile_w; - } - - // Right-side leftover tile (only if tile_x is true) - if (tile_x && remain_dst_w > 0.0f) { - rect partial_dst = { - .x = curr_dst.x, .y = curr_dst.y, - .w = remain_dst_w, .h = tile_h - }; - add_quad(verts, &src_partial_x, &partial_dst); - } - curr_dst.y += tile_h; - } - - // Bottom leftover row (only if tile_y is true) - if (tile_y && remain_dst_h > 0.0f) { - rect partial_row_dst; - partial_row_dst.w = tile_w; - partial_row_dst.h = remain_dst_h; - partial_row_dst.y = curr_dst.y; - partial_row_dst.x = dst.x; - - // Full columns in leftover row - for (int x = 0; x < cols; x++) { - add_quad(verts, &src_partial_y, &partial_row_dst); - partial_row_dst.x += tile_w; - } - - // Partial leftover corner (both X & Y leftover) - if (tile_x && remain_dst_w > 0.0f) { - rect partial_corner_dst = { - .x = partial_row_dst.x, .y = partial_row_dst.y, - .w = remain_dst_w, .h = remain_dst_h - }; - add_quad(verts, &src_partial_xy, &partial_corner_dst); - } - } -} - -JSC_CCALL(gpu_slice9, - JSValue jstex = argv[0]; - rect dst = js2rect(js, argv[1]); - - // Full texture in UV coords - rect src = { - .x = 0, .y = 0, - .w = 1, .h = 1 - }; - - // The “slice” LRTB in PIXELS, but we convert to UV below - lrtb src_slice = js2lrtb(js, argv[2]); - lrtb dst_slice = src_slice; - - HMM_Vec2 size; - JS_GETPROP(js, size.x, jstex, width, number) - JS_GETPROP(js, size.y, jstex, height, number) - - JSValue info = argv[3]; - int tile_top, tile_bottom, tile_left, tile_right, center_x, center_y; - JS_GETPROP(js,tile_top, info, tile_top, bool) - JS_GETPROP(js,tile_bottom,info,tile_bottom,bool) - JS_GETPROP(js,tile_left,info,tile_left,bool) - JS_GETPROP(js,tile_right,info,tile_right,bool) - JS_GETPROP(js, center_x, info, tile_center_x, bool) - JS_GETPROP(js, center_y, info, tile_center_y, bool) - - // Convert the slice edges from pixel to UV - src_slice.l /= size.x; - src_slice.r /= size.x; - src_slice.t /= size.y; - src_slice.b /= size.y; - - text_vert *verts = NULL; - rect curr_src; - rect curr_dst; - - // bottom-left corner (single quad) - curr_src = src; - curr_src.w = src_slice.l; - curr_src.h = src_slice.b; - - curr_dst = dst; - curr_dst.w = dst_slice.l; - curr_dst.h = dst_slice.b; - add_quad(&verts, &curr_src, &curr_dst); - - // top-left corner (single quad) - curr_src = src; - curr_src.x = src.x; - curr_src.y = src.y + src.h - src_slice.t; - curr_src.w = src_slice.l; - curr_src.h = src_slice.t; - - curr_dst = dst; - curr_dst.x = dst.x; - curr_dst.y = dst.y + dst.h - dst_slice.t; - curr_dst.w = dst_slice.l; - curr_dst.h = dst_slice.t; - add_quad(&verts, &curr_src, &curr_dst); - - // bottom-right corner (single quad) - curr_src = src; - curr_src.x = src.x + src.w - src_slice.r; - curr_src.y = src.y; - curr_src.w = src_slice.r; - curr_src.h = src_slice.b; - - curr_dst = dst; - curr_dst.x = dst.x + dst.w - dst_slice.r; - curr_dst.y = dst.y; - curr_dst.w = dst_slice.r; - curr_dst.h = dst_slice.b; - add_quad(&verts, &curr_src, &curr_dst); - - // top-right corner (single quad) - curr_src = src; - curr_src.x = src.x + src.w - src_slice.r; - curr_src.y = src.y + src.h - src_slice.t; - curr_src.w = src_slice.r; - curr_src.h = src_slice.t; - - curr_dst = dst; - curr_dst.x = dst.x + dst.w - dst_slice.r; - curr_dst.y = dst.y + dst.h - dst_slice.t; - curr_dst.w = dst_slice.r; - curr_dst.h = dst_slice.t; - add_quad(&verts, &curr_src, &curr_dst); - - // left bar (tiled) - curr_src = src; - curr_src.x = src.x; - curr_src.y = src.y + src_slice.b; - curr_src.w = src_slice.l; - curr_src.h = src.h - src_slice.t - src_slice.b; - - curr_dst = dst; - curr_dst.x = dst.x; - curr_dst.y = dst.y + dst_slice.b; - curr_dst.w = dst_slice.l; - curr_dst.h = dst.h - dst_slice.t - dst_slice.b; - tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_left,tile_left); - - // right bar (tiled) - curr_src = src; - curr_src.x = src.x + src.w - src_slice.r; - curr_src.y = src.y + src_slice.b; - curr_src.w = src_slice.r; - curr_src.h = src.h - src_slice.t - src_slice.b; - - curr_dst = dst; - curr_dst.x = dst.x + dst.w - dst_slice.r; - curr_dst.y = dst.y + dst_slice.b; - curr_dst.w = dst_slice.r; - curr_dst.h = dst.h - dst_slice.t - dst_slice.b; - tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_right,tile_right); - - // bottom bar (tiled) - curr_src = src; - curr_src.x = src.x + src_slice.l; - curr_src.y = src.y; - curr_src.w = src.w - src_slice.l - src_slice.r; - curr_src.h = src_slice.b; - - curr_dst = dst; - curr_dst.x = dst.x + dst_slice.l; - curr_dst.y = dst.y; - curr_dst.w = dst.w - dst_slice.l - dst_slice.r; - curr_dst.h = dst_slice.b; - tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_bottom,tile_bottom); - - // top bar (tiled) - curr_src = src; - curr_src.x = src.x + src_slice.l; - curr_src.y = src.y + src.h - src_slice.t; - curr_src.w = src.w - src_slice.l - src_slice.r; - curr_src.h = src_slice.t; - - curr_dst = dst; - curr_dst.x = dst.x + dst_slice.l; - curr_dst.y = dst.y + dst.h - dst_slice.t; - curr_dst.w = dst.w - dst_slice.l - dst_slice.r; - curr_dst.h = dst_slice.t; - tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_top,tile_top); - - // center (tiled) - curr_src = src; - curr_src.x = src.x + src_slice.l; - curr_src.y = src.y + src_slice.b; - curr_src.w = src.w - src_slice.l - src_slice.r; - curr_src.h = src.h - src_slice.t - src_slice.b; - - curr_dst = dst; - curr_dst.x = dst.x + dst_slice.l; - curr_dst.y = dst.y + dst_slice.b; - curr_dst.w = dst.w - dst_slice.l - dst_slice.r; - curr_dst.h = dst.h - dst_slice.t - dst_slice.b; - tile_region(&verts, curr_src, curr_dst, size.x, size.y, center_x,center_y); - - JSValue mesh = quads_to_mesh(js, verts); - arrfree(verts); - ret = mesh; -) - -JSC_CCALL(gpu_tile, - HMM_Vec2 size; - JSValue jstex = argv[0]; - JS_GETATOM(js,size.x,jstex,width,number) - JS_GETATOM(js, size.y, jstex, height, number) - - rect src_pixels = js2rect(js, argv[1]); // 'src' as pixel dimensions - rect dst = js2rect(js, argv[2]); // 'dst' as screen coords - - int tilex, tiley; - JSValue jstile = argv[3]; - JS_GETPROP(js,tilex,jstile,repeat_x,bool) - JS_GETPROP(js,tiley,jstile,repeat_y,bool) - - text_vert *verts = NULL; - tile_region(&verts, src_pixels, dst, size.x, size.y,tilex,tiley); - ret = quads_to_mesh(js,verts); - arrfree(verts); -) - -static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = { - MIST_FUNC_DEF(gpu, claim_window, 1), - MIST_FUNC_DEF(gpu, make_pipeline, 1), // loads pipeline state into an object - MIST_FUNC_DEF(gpu,compute_pipeline,1), - MIST_FUNC_DEF(gpu, set_swapchain, 2), - MIST_FUNC_DEF(gpu,sort_sprite,2), - MIST_FUNC_DEF(gpu, make_sampler,1), - MIST_FUNC_DEF(gpu, load_texture, 2), - MIST_FUNC_DEF(gpu, texture, 1), - MIST_FUNC_DEF(gpu, make_quad, 0), - MIST_FUNC_DEF(gpu, driver, 0), - MIST_FUNC_DEF(gpu, make_shader, 1), - MIST_FUNC_DEF(gpu, acquire_cmd_buffer, 0), - MIST_FUNC_DEF(gpu, upload, 3), - MIST_FUNC_DEF(gpu, wait_for_fences, 2), - MIST_FUNC_DEF(gpu, query_fence, 1), - MIST_FUNC_DEF(gpu, shader_format, 0), -}; - -JSC_CCALL(renderpass_bind_pipeline, - SDL_GPURenderPass *r = js2SDL_GPURenderPass(js,self); - SDL_GPUGraphicsPipeline *pipe = js2SDL_GPUGraphicsPipeline(js,argv[0]); - SDL_BindGPUGraphicsPipeline(r,pipe); -) - -JSC_CCALL(renderpass_draw_indexed, - SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); - SDL_DrawGPUIndexedPrimitives(pass, js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]), js2number(js,argv[4])); -) - -JSC_CCALL(renderpass_draw, - SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); - SDL_DrawGPUPrimitives(pass, js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3])); -) - -JSC_CCALL(renderpass_bind_buffers, - SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); - int first = js2number(js,argv[0]); - JSValue buffers = argv[1]; - int len = js_arrlen(js,buffers); - SDL_GPUBufferBinding bindings[len]; - for (int i = 0; i < len; i++) { - JSValue buffer = JS_GetPropertyUint32(js,buffers,i); - bindings[i].offset = 0; - gpu_buffer_unpack(js,global_gpu, buffer, NULL, NULL,&bindings[i].buffer); - JS_FreeValue(js,buffer); - } - - SDL_BindGPUVertexBuffers( - pass, - first, - bindings, - len); -) - -JSC_CCALL(renderpass_bind_index_buffer, - SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); - SDL_GPUBufferBinding bind; - bind.offset = 0; - gpu_buffer_unpack(js,global_gpu,argv[0], NULL, NULL, &bind.buffer); - int elen; - JSValue b = argv[0]; - JS_GETPROP(js, elen, b, elen, number) - - SDL_BindGPUIndexBuffer(pass,&bind,elen == 2 ? SDL_GPU_INDEXELEMENTSIZE_16BIT : SDL_GPU_INDEXELEMENTSIZE_32BIT); -) - -JSC_CCALL(renderpass_end, - SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); - SDL_EndGPURenderPass(pass); -) - -JSC_CCALL(renderpass_bind_samplers, - SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); - int first_slot = js2number(js,argv[1]); - JSValue arr = argv[2]; - int num = js_arrlen(js,arr); - SDL_GPUTextureSamplerBinding binds[num]; - for (int i = 0; i < num; i++) { - JSValue val = JS_GetPropertyUint32(js,arr,i); - JSValue tex = JS_GetPropertyStr(js,val,"texture"); - JSValue smp = JS_GetPropertyStr(js,val,"sampler"); - binds[i].texture = js2SDL_GPUTexture(js,tex); - binds[i].sampler = js2SDL_GPUSampler(js,smp); - JS_FreeValue(js,tex); - JS_FreeValue(js,smp); - JS_FreeValue(js,val); - } - int vertex = JS_ToBool(js,argv[0]); - if (vertex) - SDL_BindGPUVertexSamplers(pass, first_slot, binds, num); - else - SDL_BindGPUFragmentSamplers(pass, first_slot, binds, num); -) - -JSC_CCALL(renderpass_bind_storage_buffers, - -) - -JSC_CCALL(renderpass_bind_storage_textures, - -) - -JSC_CCALL(renderpass_viewport, - SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); - rect r = js2rect(js,argv[0]); - SDL_GPUViewport vp ={0}; - vp.x = r.x; - vp.y = r.y; - vp.w = r.w; - vp.h = r.h; - vp.min_depth = 0; - vp.max_depth = 1; - SDL_SetGPUViewport(pass,&vp); -) - -JSC_CCALL(renderpass_scissor, - SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); - rect r = js2rect(js,argv[0]); - SDL_Rect rr; - rr.x = r.x; - rr.y = r.y; - rr.w = r.w; - rr.h = r.h; - SDL_SetGPUScissor(pass,&rr); -) - -static const JSCFunctionListEntry js_SDL_GPURenderPass_funcs[] = { - MIST_FUNC_DEF(renderpass, bind_pipeline, 1), - MIST_FUNC_DEF(renderpass, viewport, 1), - MIST_FUNC_DEF(renderpass, scissor, 1), - MIST_FUNC_DEF(renderpass, draw, 4), - MIST_FUNC_DEF(renderpass, draw_indexed, 5), - MIST_FUNC_DEF(renderpass, end, 0), - MIST_FUNC_DEF(renderpass, bind_index_buffer, 1), - MIST_FUNC_DEF(renderpass, bind_buffers, 2), - MIST_FUNC_DEF(renderpass, bind_samplers, 3), - MIST_FUNC_DEF(renderpass, bind_storage_buffers, 2), - MIST_FUNC_DEF(renderpass, bind_storage_textures, 2), -}; - -JSC_CCALL(cmd_render_pass, - SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); - - if (!JS_IsObject(argv[0])) return JS_ThrowTypeError(js, "render_pass: Expected a render pass descriptor object"); - JSValue passObj = argv[0]; - - JSValue colorTargetsVal = JS_GetPropertyStr(js, passObj, "color_targets"); - if (!JS_IsArray(js, colorTargetsVal)) - return JS_ThrowTypeError(js, "render_pass: colorTargets must be an array"); - - uint32_t colorCount = js_arrlen(js, colorTargetsVal); - SDL_GPUColorTargetInfo colortars[colorCount]; - SDL_GPUDepthStencilTargetInfo depthtar; - int has_depth = 0; - - // Fill colorInfos from JS array - for (uint32_t i = 0; i < colorCount; i++) { - JSValue ctargetVal = JS_GetPropertyUint32(js, colorTargetsVal, i); - colortars[i] = js2SDL_GPUColorTargetInfo(js,ctargetVal); - JS_FreeValue(js, ctargetVal); - } - - // Optional depth_stencil - JSValue depthval = JS_GetPropertyStr(js, passObj, "depth_stencil"); - if (!JS_IsUndefined(depthval)) { - has_depth = 1; - JS_GETPROP(js, depthtar.texture, depthval, texture, SDL_GPUTexture) - JS_GETPROP(js, depthtar.load_op, depthval, load, SDL_GPULoadOp) - JS_GETPROP(js, depthtar.store_op, depthval, store, SDL_GPUStoreOp) - JS_GETPROP(js, depthtar.stencil_load_op, depthval, stencil_load, SDL_GPULoadOp) - JS_GETPROP(js, depthtar.stencil_store_op, depthval, stencil_store, SDL_GPUStoreOp) - JS_GETPROP(js,depthtar.clear_depth, depthval, clear, number) - JS_GETPROP(js,depthtar.clear_stencil,depthval,clear_stencil,number) - } - JS_FreeValue(js, depthval); - - SDL_GPURenderPass *pass = SDL_BeginGPURenderPass( - cmds, - colortars, - colorCount, - has_depth ? &depthtar : NULL - ); - - JS_FreeValue(js, colorTargetsVal); - - if (!pass) return JS_ThrowInternalError(js, "render_pass: Failed to begin render pass"); - - return SDL_GPURenderPass2js(js, pass); -) - -JSC_CCALL(cmd_bind_vertex_buffer, - SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); - int slot; - JS_ToInt32(js, &slot, argv[0]); - SDL_GPUBuffer *buffer = js2SDL_GPUBuffer(js, argv[1]); - size_t offset = 0; - if (argc > 2) JS_ToIndex(js, &offset, argv[2]); - SDL_BindGPUVertexBuffers(cmds, slot, &(SDL_GPUBufferBinding){.buffer = buffer, .offset = offset}, 1); -) - -JSC_CCALL(cmd_bind_index_buffer, - SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); - SDL_GPUBuffer *buffer = js2SDL_GPUBuffer(js, argv[0]); - size_t offset = 0; - if (argc > 1) JS_ToIndex(js, &offset, argv[1]); - SDL_BindGPUIndexBuffer(cmds, &(SDL_GPUBufferBinding){.buffer = buffer, .offset = offset}, SDL_GPU_INDEXELEMENTSIZE_16BIT); -) - -JSC_CCALL(cmd_bind_fragment_sampler, - SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); - int slot; - JS_ToInt32(js, &slot, argv[0]); - SDL_GPUTexture *tex = js2SDL_GPUTexture(js, argv[1]); - SDL_GPUSampler *sampler = js2SDL_GPUSampler(js, argv[2]); - SDL_BindGPUFragmentSamplers(cmds, slot, &(SDL_GPUTextureSamplerBinding){.texture = tex, .sampler = sampler}, 1); -) - -JSC_CCALL(cmd_push_vertex_uniform_data, - SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); - int slot; - JS_ToInt32(js, &slot, argv[0]); - size_t buf_size; - void *data = JS_GetArrayBuffer(js, &buf_size, argv[1]); - SDL_PushGPUVertexUniformData(cmds, slot, data, buf_size); -) - -JSC_CCALL(cmd_push_fragment_uniform_data, - SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); - int slot; - JS_ToInt32(js, &slot, argv[0]); - size_t buf_size; - void *data = JS_GetArrayBuffer(js, &buf_size, argv[1]); - SDL_PushGPUFragmentUniformData(cmds, slot, data, buf_size); -) - -JSC_CCALL(cmd_push_compute_uniform_data, - SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); - int slot; - JS_ToInt32(js, &slot, argv[0]); - size_t buf_size; - void *data = JS_GetArrayBuffer(js, &buf_size, argv[1]); - SDL_PushGPUComputeUniformData(cmds, slot, data, buf_size); -) - -JSC_CCALL(cmd_submit, - SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js,self); - SDL_GPUFence *fence = SDL_SubmitGPUCommandBufferAndAcquireFence(cmds); - return SDL_GPUFence2js(js,fence); -) - -JSC_CCALL(cmd_hud, - SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js,self); - HMM_Vec2 size = js2vec2(js,argv[0]); - HMM_Mat4 proj = HMM_Orthographic_RH_NO(0,size.x,0,size.y,-1,1); - shader_globals data = {0}; - data.world_to_projection = proj; - data.projection_to_world = HMM_InvGeneralM4(proj); - data.viewport_min_z = -1, - data.viewport_max_z = 1; - data.render_size = size; - data.view_to_projection = proj; - data.viewport_size = (HMM_Vec2){1,1}; - data.viewport_offset = (HMM_Vec2){0,0}; - data.time = SDL_GetTicksNS() / 1000000000.0f; - SDL_PushGPUVertexUniformData(cmds, js2number(js,argv[1]), &data, sizeof(data)); -) - -JSC_CCALL(cmd_camera, - SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); - shader_globals data = camera_globals(js, argv[0]); - SDL_PushGPUVertexUniformData(cmds, js2number(js,argv[1]), &data, sizeof(data)); -) - -JSC_SCALL(cmd_push_debug_group, - SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self); - SDL_PushGPUDebugGroup(cmd,str); -) - -JSC_CCALL(cmd_pop_debug_group, - SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self); - SDL_PopGPUDebugGroup(cmd); -) - -JSC_SCALL(cmd_debug_label, - SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self); - SDL_InsertGPUDebugLabel(cmd, str); -) - -SDL_GPUBlitRegion js2SDL_GPUBlitRegion(JSContext *js, JSValue v) -{ - SDL_GPUBlitRegion info = {0}; - if (JS_GetClassID(v) == js_SDL_GPUTexture_id) { - // texture path - JSValue tex = v; - info.texture = js2SDL_GPUTexture(js,tex); - info.mip_level = 0, - info.layer_or_depth_plane = 0; - info.x = 0; - info.y = 0; - JS_GETPROP(js,info.w,tex,width,number) - JS_GETPROP(js,info.h,tex,height,number) - return info; - } - JSValue tex = JS_GetPropertyStr(js, v, "texture"); - info.texture = js2SDL_GPUTexture(js,tex); - JS_GETPROP(js,info.mip_level,v,mip_level, number) - JS_GETPROP(js,info.mip_level,v,layer,number) - JS_GETPROP(js,info.x,v,x,number) - JS_GETPROP(js,info.y,v,y,number) - JS_GETPROP(js,info.w,v,width,number) - JS_GETPROP(js,info.h,v,height,number) - - if (!info.w) JS_GETPROP(js,info.w,tex,width,number) - if (!info.h) JS_GETPROP(js,info.h,tex,height,number) - - JS_FreeValue(js,tex); - return info; -} - -JSC_CCALL(cmd_blit, - SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self); - SDL_GPUBlitInfo info = {0}; - JSValue v = argv[0]; - JS_GETPROP(js,info.source,v,src,SDL_GPUBlitRegion) - JS_GETPROP(js,info.destination,v,dst,SDL_GPUBlitRegion) - JS_GETPROP(js,info.load_op,v,load,SDL_GPULoadOp) - JS_GETPROP(js,info.flip_mode,v,flip,SDL_FlipMode) - JS_GETPROP(js,info.filter,v,filter,SDL_GPUFilter) - JS_GETPROP(js,info.clear_color,v,color,SDL_FColor) - SDL_BlitGPUTexture(cmd,&info); -) - -JSC_CCALL(cmd_cancel, - SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self); - SDL_CancelGPUCommandBuffer(cmd); -) - -JSC_CCALL(cmd_compute_pass, - SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self); - JSValue textures = argv[0]; - JSValue buffers = argv[1]; - - int t_n = js_arrlen(js,textures); - SDL_GPUStorageTextureReadWriteBinding t_bind[t_n]; - for (int i = 0; i < t_n; i++) { - JSValue T = JS_GetPropertyUint32(js,textures,i); - JS_GETPROP(js, t_bind[i].texture, T, texture, SDL_GPUTexture) - JS_GETPROP(js,t_bind[i].mip_level,T,mip, number) - JS_GETPROP(js,t_bind[i].layer,T,layer, number) - JS_FreeValue(js,T); - } - - int b_n = js_arrlen(js,buffers); - SDL_GPUStorageBufferReadWriteBinding b_bind[b_n]; - for (int i = 0; i < b_n; i++) { - JSValue T = JS_GetPropertyUint32(js,buffers,i); - JS_GETPROP(js,b_bind[i].buffer, T,buffer,SDL_GPUBuffer) - } - - SDL_GPUComputePass *pass = SDL_BeginGPUComputePass(cmd,t_bind,t_n,b_bind,b_n); - if (!pass) return JS_ThrowReferenceError(js, "Unable to begin compute pass: %s", SDL_GetError()); - return SDL_GPUComputePass2js(js,pass); -) - -static const JSCFunctionListEntry js_SDL_GPUCommandBuffer_funcs[] = { - MIST_FUNC_DEF(cmd, render_pass, 1), - MIST_FUNC_DEF(cmd, compute_pass, 2), - MIST_FUNC_DEF(cmd, swapchain_pass, 1), - MIST_FUNC_DEF(cmd, acquire_swapchain,0), - MIST_FUNC_DEF(cmd, bind_vertex_buffer, 2), - MIST_FUNC_DEF(cmd, bind_index_buffer, 1), - MIST_FUNC_DEF(cmd, bind_fragment_sampler, 3), - MIST_FUNC_DEF(cmd, push_vertex_uniform_data, 2), - MIST_FUNC_DEF(cmd, push_fragment_uniform_data, 2), - MIST_FUNC_DEF(cmd, push_compute_uniform_data, 2), - MIST_FUNC_DEF(cmd, submit, 0), - MIST_FUNC_DEF(cmd, cancel, 0), - MIST_FUNC_DEF(cmd, camera, 2), - MIST_FUNC_DEF(cmd, hud, 2), - MIST_FUNC_DEF(cmd, push_debug_group, 1), - MIST_FUNC_DEF(cmd, pop_debug_group, 0), - MIST_FUNC_DEF(cmd, debug_label, 1), - MIST_FUNC_DEF(cmd, blit, 1), -}; - -JSC_SCALL(buffer_name, - SDL_GPUBuffer *buffer = js2SDL_GPUBuffer(js,self); - SDL_GPUDevice *gpu; - JS_GETPROP(js, gpu, self, gpu, SDL_GPUDevice) - SDL_SetGPUBufferName(gpu,buffer,str); -) - -static const JSCFunctionListEntry js_SDL_GPUBuffer_funcs[] = { - MIST_FUNC_DEF(buffer, name, 1), -}; - -JSC_CCALL(compute_dispatch, - SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self); - SDL_DispatchGPUCompute(pass,js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2])); -) - -JSC_CCALL(compute_end, - SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self); - SDL_EndGPUComputePass(pass); -) - -JSC_CCALL(compute_pipeline, - SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self); - SDL_GPUComputePipeline *pipeline = js2SDL_GPUComputePipeline(js,argv[0]); - SDL_BindGPUComputePipeline(pass,pipeline); -) - -JSC_CCALL(compute_samplers, - SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self); - JSValue samplers = argv[0]; - int n = js_arrlen(js,samplers); - SDL_GPUTextureSamplerBinding b[n]; - for (int i = 0; i < n; i++) { - JSValue s = JS_GetPropertyUint32(js,samplers,i); - b[i] = js2SDL_GPUTextureSamplerBinding(js,s); - JS_FreeValue(js,s); - } - SDL_BindGPUComputeSamplers(pass,js2number(js,argv[1]),b,n); -) - -JSC_CCALL(compute_storage_textures, - SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self); - JSValue textures = argv[0]; - int n = js_arrlen(js,textures); - SDL_GPUTexture *b[n]; - for (int i = 0; i < n; i++) { - JSValue s = JS_GetPropertyUint32(js,textures,i); - b[i] = js2SDL_GPUTexture(js,s); - JS_FreeValue(js,s); - } - SDL_BindGPUComputeStorageTextures(pass,js2number(js,argv[1]),b,n); -) - -JSC_CCALL(compute_storage_buffers, - SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self); - JSValue buffers = argv[0]; - int n = js_arrlen(js,buffers); - SDL_GPUBuffer *b[n]; - for (int i = 0; i < n; i++) { - JSValue s = JS_GetPropertyUint32(js,buffers,i); - b[i] = js2SDL_GPUBuffer(js,s); - JS_FreeValue(js,s); - } - SDL_BindGPUComputeStorageTextures(pass,js2number(js,argv[1]),b,n); -) - -static const JSCFunctionListEntry js_SDL_GPUComputePass_funcs[] = { - MIST_FUNC_DEF(compute, dispatch, 3), - MIST_FUNC_DEF(compute, end, 0), - MIST_FUNC_DEF(compute, pipeline, 1), - MIST_FUNC_DEF(compute, samplers, 2), - MIST_FUNC_DEF(compute, storage_buffers, 2), - MIST_FUNC_DEF(compute, storage_textures, 2), -}; - typedef struct { const char *name; SDL_PixelFormat fmt; } fmt_entry; static const fmt_entry k_fmt_table[] = { @@ -5670,42 +1926,6 @@ static const JSCFunctionListEntry js_SDL_Surface_funcs[] = { JS_CGETSET_DEF("pitch", js_surface_get_pitch, NULL), }; -JSC_CCALL(camera_frame, - SDL_ClearError(); - SDL_Camera *cam = js2SDL_Camera(js,self); - if (!cam) return JS_ThrowReferenceError(js,"Self was not a camera: %s", SDL_GetError()); - SDL_Surface *surf = SDL_AcquireCameraFrame(cam, NULL); - if (!surf) { - const char *msg = SDL_GetError(); - if (msg[0] != 0) - return JS_ThrowReferenceError(js,"Could not get camera frame: %s", SDL_GetError()); - else return JS_UNDEFINED; - } - return SDL_Surface2js(js,surf); - SDL_Surface *newsurf = SDL_CreateSurface(surf->w, surf->h, surf->format); - SDL_ReleaseCameraFrame(cam,surf); - - int didit = SDL_BlitSurface(surf, NULL, newsurf, NULL); - if (!didit) { - SDL_DestroySurface(newsurf); - return JS_ThrowReferenceError(js, "Could not blit: %s", SDL_GetError()); - } - - return SDL_Surface2js(js,newsurf); -) - -JSC_CCALL(camera_release_frame, - SDL_Camera *cam = js2SDL_Camera(js,self); - SDL_Surface *surf = js2SDL_Surface(js,argv[0]); - SDL_ReleaseCameraFrame(cam,surf); -) - -static const JSCFunctionListEntry js_SDL_Camera_funcs[] = -{ - MIST_FUNC_DEF(camera, frame, 0), - MIST_FUNC_DEF(camera, release_frame, 1), -}; - JSC_CCALL(texture_mode, SDL_Texture *tex = js2SDL_Texture(js,self); SDL_SetTextureScaleMode(tex,js2number(js,argv[0])); @@ -5715,69 +1935,6 @@ static const JSCFunctionListEntry js_SDL_Texture_funcs[] = { MIST_FUNC_DEF(texture, mode, 1), }; -JSC_SCALL(texture_name, - SDL_GPUTexture *texture = js2SDL_GPUTexture(js,self); - SDL_GPUDevice *gpu; - JS_GETPROP(js,gpu,self,gpu,SDL_GPUDevice) - SDL_SetGPUTextureName(gpu,texture,str); -) - -static const JSCFunctionListEntry js_SDL_GPUTexture_funcs[] = { - MIST_FUNC_DEF(texture, name, 1), -}; - -JSC_CCALL(input_mouse_lock, SDL_CaptureMouse(JS_ToBool(js,argv[0]))) -JSC_CCALL(input_mouse_show, - if (JS_ToBool(js,argv[0])) - SDL_ShowCursor(); - else - SDL_HideCursor(); -) - -JSC_CCALL(input_cursor_set, - SDL_Cursor *c = js2SDL_Cursor(js,argv[0]); - if (!SDL_SetCursor(c)) - return JS_ThrowReferenceError(js, "could not set cursor: %s", SDL_GetError()); -) - -JSC_CCALL(input_keyname, - return JS_NewString(js, SDL_GetKeyName(js2number(js,argv[0]))); -) - -JSC_CCALL(input_keymod, - return js_keymod(js); -) - -JSC_CCALL(input_mousestate, - float x,y; - SDL_MouseButtonFlags flags = SDL_GetMouseState(&x,&y); - JSValue m = JS_NewObject(js); - JS_SetProperty(js,m,x, number2js(js,x)); - JS_SetProperty(js,m,y,number2js(js,y)); - - if (flags & SDL_BUTTON_LMASK) - JS_SetPropertyStr(js, m, "left", JS_NewBool(js, 1)); - if (flags & SDL_BUTTON_MMASK) - JS_SetPropertyStr(js, m, "middle", JS_NewBool(js, 1)); - if (flags & SDL_BUTTON_RMASK) - JS_SetPropertyStr(js, m, "right", JS_NewBool(js, 1)); - if (flags & SDL_BUTTON_X1MASK) - JS_SetPropertyStr(js, m, "x1", JS_NewBool(js, 1)); - if (flags & SDL_BUTTON_X2MASK) - JS_SetPropertyStr(js, m, "x2", JS_NewBool(js, 1)); - - return m; -) - -static const JSCFunctionListEntry js_input_funcs[] = { - MIST_FUNC_DEF(input, mouse_show, 1), - MIST_FUNC_DEF(input, mouse_lock, 1), - MIST_FUNC_DEF(input, cursor_set, 1), - MIST_FUNC_DEF(input, keyname, 1), - MIST_FUNC_DEF(input, keymod, 0), - MIST_FUNC_DEF(input, mousestate, 0), -}; - JSC_CCALL(os_guid, SDL_GUID guid; randombytes(guid.data, 16); @@ -5820,530 +1977,6 @@ static const JSCFunctionListEntry js_debug_funcs[] = { MIST_FUNC_DEF(debug, dump_obj, 1), }; -JSC_SCALL(io_rm, - if (!PHYSFS_delete(str)) ret = JS_ThrowReferenceError(js,"could not remove %s. %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); -) - -JSC_SCALL(io_mkdir, - if (!PHYSFS_mkdir(str)) ret = JS_ThrowReferenceError(js,"could not make directory %s. %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); -) - -JSC_SCALL(io_exists, ret = JS_NewBool(js,PHYSFS_exists(str)); ) - -JSC_SCALL(io_stat, - PHYSFS_Stat stat; - if (!PHYSFS_stat(str, &stat)) - return JS_ThrowReferenceError(js, "%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - - ret = JS_NewObject(js); - JS_SetPropertyStr(js,ret,"filesize", number2js(js,stat.filesize)); - JS_SetPropertyStr(js,ret,"modtime", number2js(js,stat.modtime)); - JS_SetPropertyStr(js,ret,"createtime", number2js(js,stat.createtime)); - JS_SetPropertyStr(js,ret,"accesstime", number2js(js,stat.accesstime)); -) - -JSC_SCALL(io_slurpbytes, - PHYSFS_File *f = PHYSFS_openRead(str); - if (!f) { - ret = JS_ThrowReferenceError(js,"physfs error when slurping %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - goto END; - } - PHYSFS_Stat stat; - PHYSFS_stat(str,&stat); - void *data = malloc(stat.filesize); - PHYSFS_readBytes(f,data,stat.filesize); - PHYSFS_close(f); - ret = JS_NewArrayBufferCopy(js,data,stat.filesize); - - END: -) - -JSC_SCALL(io_slurp, - PHYSFS_File *f = PHYSFS_openRead(str); - if (!f) { - ret = JS_ThrowReferenceError(js,"physfs error when slurping %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - goto END; - } - PHYSFS_Stat stat; - PHYSFS_stat(str,&stat); - void *data = malloc(stat.filesize); - PHYSFS_readBytes(f,data,stat.filesize); - PHYSFS_close(f); - ret = JS_NewStringLen(js,data, stat.filesize); - - free(data); - - END: -) - -size_t js_physfs_write(JSContext *js, PHYSFS_File *f, JSValue val) -{ - size_t len; - size_t wrote; - if (JS_IsString(val)) { - const char *data = JS_ToCStringLen(js,&len,val); - wrote = PHYSFS_writeBytes(f,data,len); - JS_FreeCString(js,data); - } else { - unsigned char *data = JS_GetArrayBuffer(js,&len,val); - wrote = PHYSFS_writeBytes(f,data,len); - } - - if (wrote < len) wrote = -1; - return wrote; -} - -JSC_SCALL(io_slurpwrite, - PHYSFS_File *f = PHYSFS_openWrite(str); - if (!f) { - ret = JS_ThrowReferenceError(js,"could not write to %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - goto END; - } - size_t wrote = js_physfs_write(js,f,argv[1]); - - PHYSFS_close(f); - if (wrote == -1) - ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - - END: -) - -JSC_SSCALL(io_mount, - if (!PHYSFS_mount(str,str2,0)) ret = JS_ThrowReferenceError(js,"Unable to mount %s at %s: %s", str, str2, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); -) - -JSC_SCALL(io_unmount, - if (!PHYSFS_unmount(str)) ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); -) - -JSC_SCALL(io_writepath, - if (!PHYSFS_setWriteDir(str)) ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); -) - -struct globdata { - JSContext *js; - JSValue arr; - char **globs; - char *glob; - int idx; - int recurse; -}; - -int globfs_cb(struct globdata *data, char *dir, char *file) -{ - int needfree = 0; - char *path; - if (dir[0] == 0) path = file; - else { - path = malloc(strlen(dir) + strlen(file) + 2); - path[0] = 0; - strcat(path,dir); - strcat(path,"/"); - strcat(path,file); - needfree = 1; - } - - char **glob = data->globs; - - while (*glob != NULL) { - if (wildmatch(*glob, path, WM_WILDSTAR) == WM_MATCH) - goto END; - glob++; - } - - PHYSFS_Stat stat; - PHYSFS_stat(path, &stat); - if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY) { - PHYSFS_enumerate(path, globfs_cb, data); - goto END; - } - - JS_SetPropertyUint32(data->js, data->arr, data->idx++, JS_NewString(data->js,path)); - - END: - if (needfree) free(path); - return 1; -} - -JSC_SSCALL(io_match, - if (wildmatch(str, str2, WM_PATHNAME | WM_PERIOD | WM_WILDSTAR) == WM_MATCH) - ret = JS_NewBool(js,1); - else - ret = JS_NewBool(js,0); -) - -JSC_CCALL(io_globfs, - ret = JS_NewArray(js); - struct globdata data; - data.js = js; - data.arr = ret; - data.idx = 0; - int globs_len = js_arrlen(js,argv[0]); - const char *globs[globs_len+1]; - for (int i = 0; i < globs_len; i++) { - JSValue g = JS_GetPropertyUint32(js,argv[0],i); - globs[i] = JS_ToCString(js,g); - JS_FreeValue(js,g); - } - - globs[globs_len] = NULL; - data.globs = globs; - - const char *path = NULL; - if (!JS_IsUndefined(argv[1])) path = JS_ToCString(js,argv[1]); - PHYSFS_enumerate(path, globfs_cb, &data); - - for (int i = 0; i < globs_len; i++) - JS_FreeCString(js,globs[i]); - - ret = data.arr; - JS_FreeCString(js,path); -) - -static int enumerate_cb(void *udata, const char *dir, const char *fname) -{ - struct globdata *data = (struct globdata*)udata; - char *path; - int needfree = 0; - - if (dir[0] == 0) path = (char*)fname; - else { - size_t dlen = strlen(dir); - int ends_slash = (dlen && dir[dlen - 1] == '/'); - path = malloc(dlen + strlen(fname) + (ends_slash ? 1 : 2)); - if (ends_slash) sprintf(path, "%s%s", dir, fname); else sprintf(path, "%s/%s", dir, fname); - needfree = 1; - } - - PHYSFS_Stat st; - if (!PHYSFS_stat(path, &st)) { - if (needfree) free(path); - return 1; - } - - /* If it's a directory and we're recursing, enumerate it further. */ - if (st.filetype == PHYSFS_FILETYPE_DIRECTORY && data->recurse) - PHYSFS_enumerate(path, enumerate_cb, data); - - /* Add this item (file or directory) to the JS array. */ - JS_SetPropertyUint32(data->js, data->arr, data->idx++, JS_NewString(data->js, path)); - - if (needfree) free(path); - return 1; /* continue enumerating */ -} - -JSC_SCALL(io_enumerate, - /* First argument: str (directory name) */ - /* Second argument: boolean => recurse or not */ - ret = JS_NewArray(js); - - struct globdata data; - data.js = js; - data.arr = ret; - data.idx = 0; - data.glob = NULL; /* not used here */ - data.globs = NULL; /* not used here */ - data.recurse = 0; - - if (!JS_IsUndefined(argv[1])) /* parse second arg if provided */ - data.recurse = JS_ToBool(js, argv[1]); - - /* Enumerate the directory given by 'str'. */ - PHYSFS_enumerate(str, enumerate_cb, &data); - - /* Return the JS array we filled. */ - ret = data.arr; -) - -JSC_CCALL(io_basedir, return JS_NewString(js,PHYSFS_getBaseDir())) -JSC_SSCALL(io_prefdir, return JS_NewString(js,PHYSFS_getPrefDir(str, str2))) - -JSC_SCALL(io_open, - PHYSFS_File *f = PHYSFS_openWrite(str); - if (!f) - ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - else - ret = PHYSFS_File2js(js,f); -) - -JSC_SCALL(io_realdir, - const char *real = PHYSFS_getRealDir(str); - if (!real) - ret = JS_UNDEFINED; - else - ret = JS_NewString(js,real); -) - -JSC_CCALL(io_searchpath, - ret = JS_NewArray(js); - char **paths = PHYSFS_getSearchPath(); - for (int i = 0; paths[i] != NULL; i++) - JS_SetPropertyUint32(js,ret,i,JS_NewString(js,paths[i])); -) - -JSC_CCALL(io_mount_core, - int mount = JS_ToBool(js,argv[0]); - if (!mount) - PHYSFS_unmount("core.zip"); - else - prosperon_mount_core(); -) - -JSC_SCALL(io_is_directory, - PHYSFS_Stat stat; - int good = PHYSFS_stat(str, &stat); - if (!good) - ret = JS_NewBool(js, 0); - else - ret = JS_NewBool(js, stat.filetype == PHYSFS_FILETYPE_DIRECTORY); -) - -static const JSCFunctionListEntry js_io_funcs[] = { - MIST_FUNC_DEF(io, rm, 1), - MIST_FUNC_DEF(io, mkdir, 1), - MIST_FUNC_DEF(io,stat,1), - MIST_FUNC_DEF(io, globfs, 2), - MIST_FUNC_DEF(io, match, 2), - MIST_FUNC_DEF(io, exists, 1), - MIST_FUNC_DEF(io, mount, 2), - MIST_FUNC_DEF(io,unmount,1), - MIST_FUNC_DEF(io,slurp,1), - MIST_FUNC_DEF(io,slurpbytes,1), - MIST_FUNC_DEF(io,slurpwrite,2), - MIST_FUNC_DEF(io,writepath, 1), - MIST_FUNC_DEF(io,basedir, 0), - MIST_FUNC_DEF(io, prefdir, 2), - MIST_FUNC_DEF(io, realdir, 1), - MIST_FUNC_DEF(io, open, 1), - MIST_FUNC_DEF(io, searchpath, 0), - MIST_FUNC_DEF(io, enumerate, 2), - MIST_FUNC_DEF(io, mount_core, 1), - MIST_FUNC_DEF(io, is_directory, 1), -}; - -JSC_CCALL(file_close, - PHYSFS_File *f = js2PHYSFS_File(js,self); - PHYSFS_close(f); -) - -JSC_CCALL(file_write, - PHYSFS_File *f = js2PHYSFS_File(js,self); - size_t wrote = js_physfs_write(js,f,argv[0]); - if (wrote == -1) - return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); -) - -JSC_CCALL(file_buffer, - PHYSFS_File *f = js2PHYSFS_File(js,self); - size_t size = js2number(js,argv[0]); - if (!PHYSFS_setBuffer(f,size)) - return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); -) - -JSC_CCALL(file_tell, - PHYSFS_File *f = js2PHYSFS_File(js,self); - size_t tell = PHYSFS_tell(f); - if (tell == -1) - return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - - return number2js(js,tell); -) - -JSC_CCALL(file_eof, - PHYSFS_File *f = js2PHYSFS_File(js,self); - return JS_NewBool(js, PHYSFS_eof(f)); -) - -static const JSCFunctionListEntry js_PHYSFS_File_funcs[] = { - MIST_FUNC_DEF(file, close, 0), - MIST_FUNC_DEF(file, write, 1), - MIST_FUNC_DEF(file, buffer, 1), - MIST_FUNC_DEF(file, tell, 0), - MIST_FUNC_DEF(file, eof, 0), -}; - -JSC_GETSET_APPLY(transform, pos, vec3) -JSC_GETSET_APPLY(transform, scale, vec3f) -JSC_GETSET_APPLY(transform, rotation, quat) -JSC_CCALL(transform_move, - transform *t = js2transform(js,self); - transform_move(t, js2vec3(js,argv[0])); -) - -JSC_CCALL(transform_lookat, - HMM_Vec3 point = js2vec3(js,argv[0]); - transform *go = js2transform(js,self); - HMM_Mat4 m = HMM_LookAt_RH(go->pos, point, vUP); - go->rotation = HMM_M4ToQ_RH(m); - transform_apply(go); -) - -JSC_CCALL(transform_rotate, - HMM_Vec3 axis = js2vec3(js,argv[0]); - transform *t = js2transform(js,self); - HMM_Quat rot = HMM_QFromAxisAngle_RH(axis, js2angle(js,argv[1])); - t->rotation = HMM_MulQ(t->rotation,rot); - transform_apply(t); -) - -JSC_CCALL(transform_angle, - HMM_Vec3 axis = js2vec3(js,argv[0]); - transform *t = js2transform(js,self); - if (axis.x) return angle2js(js,HMM_Q_Roll(t->rotation)); - if (axis.y) return angle2js(js,HMM_Q_Pitch(t->rotation)); - if (axis.z) return angle2js(js,HMM_Q_Yaw(t->rotation)); - return angle2js(js,0); -) - -JSC_CCALL(transform_direction, - transform *t = js2transform(js,self); - return vec32js(js, HMM_QVRot(js2vec3(js,argv[0]), t->rotation)); -) - -JSC_CCALL(transform_phys2d, - transform *t = js2transform(js,self); - HMM_Vec2 v = js2vec2(js,argv[0]); - float av = js2number(js,argv[1]); - float dt = js2number(js,argv[2]); - transform_move(t, (HMM_Vec3){v.x*dt,v.y*dt,0}); - HMM_Quat rot = HMM_QFromAxisAngle_RH((HMM_Vec3){0,0,1}, av*dt); - t->rotation = HMM_MulQ(t->rotation, rot); - transform_apply(t); -) - -JSC_CCALL(transform_unit, - transform *t = js2transform(js,self); - t->pos = v3zero; - t->rotation = QUAT1; - t->scale = v3one; - transform_apply(t); -) - -JSC_CCALL(transform_trs, - transform *t = js2transform(js,self); - t->pos = JS_IsUndefined(argv[0]) ? v3zero : js2vec3(js,argv[0]); - t->rotation = JS_IsUndefined(argv[1]) ? QUAT1 : js2quat(js,argv[1]); - t->scale = JS_IsUndefined(argv[2]) ? v3one : js2vec3(js,argv[2]); - transform_apply(t); -) - -JSC_CCALL(transform_rect, - transform *t = js2transform(js,self); - rect r = js2rect(js,argv[0]); - t->pos = (HMM_Vec3){r.x,r.y,0}; - t->scale = (HMM_Vec3){r.w,r.h,1}; - t->rotation = QUAT1; - transform_apply(t); -) - -JSC_CCALL(transform_array, - transform *t = js2transform(js,self); - HMM_Mat4 m= transform2mat(t); - ret = JS_NewArray(js); - for (int i = 0; i < 16; i++) - JS_SetPropertyUint32(js,ret,i, number2js(js,m.em[i])); -) - -static JSValue js_transform_get_change_hook(JSContext *js, JSValueConst self) -{ - transform *t = js2transform(js,self); - return JS_DupValue(js,t->change_hook); -} - -static JSValue js_transform_set_change_hook(JSContext *js, JSValueConst self, JSValue v) -{ - transform *t = js2transform(js,self); - if (!JS_IsUndefined(v) && !JS_IsFunction(js,v)) return JS_ThrowReferenceError(js, "Hook must be a function."); - JS_FreeValue(js,t->change_hook); - t->change_hook = JS_DupValue(js,v); - return JS_UNDEFINED; -} - -static JSValue js_transform_get_parent(JSContext *js, JSValueConst self) -{ - transform *t = js2transform(js,self); - if (t->parent) return JS_DupValue(js,t->jsparent); - return JS_UNDEFINED; -} - -static JSValue js_transform_set_parent(JSContext *js, JSValueConst self, JSValue v) -{ - transform *p = js2transform(js,v); - if (!JS_IsUndefined(v) && !p) - return JS_ThrowReferenceError(js,"Parent must be another transform."); - - transform *t = js2transform(js,self); - - if (t == p) - return JS_ThrowReferenceError(js, "A transform cannot be its own parent."); - - if (t->parent) { - transform *cur_parent = t->parent; - JS_FreeValue(js,t->jsparent); - t->jsparent = JS_UNDEFINED; - - for (int i = 0; i < arrlen(cur_parent->children); i++) { - if (cur_parent->children[i] == t) { - arrdelswap(cur_parent->children,i); - break; - } - } - - for (int i = 0; i < arrlen(cur_parent->jschildren); i++) { - if (JS_SameValue(js,cur_parent->jschildren[i],self)) { - JS_FreeValue(js,cur_parent->jschildren[i]); - arrdelswap(cur_parent->jschildren,i); - break; - } - } - } - - t->parent = p; - t->jsparent = JS_DupValue(js,v); - - if (p) { - arrput(p->children, t); - JSValue child = JS_DupValue(js,self); - arrput(p->jschildren,child); - } - - transform_apply(t); - - return JS_UNDEFINED; -} - -JSC_CCALL(transform_torect, - transform *t = js2transform(js,self); - return rect2js(js,transform2rect(t)); -) - -JSC_CCALL(transform_children, - transform *t = js2transform(js,self); - ret = JS_NewArray(js); - for (int i = 0; i < arrlen(t->jschildren); i++) - JS_SetPropertyUint32(js,ret,i,JS_DupValue(js,t->jschildren[i])); -) - -static const JSCFunctionListEntry js_transform_funcs[] = { - CGETSET_ADD(transform, pos), - CGETSET_ADD(transform, scale), - CGETSET_ADD(transform, rotation), - CGETSET_ADD(transform, parent), - CGETSET_ADD(transform, change_hook), - MIST_FUNC_DEF(transform, trs, 3), - MIST_FUNC_DEF(transform, phys2d, 3), - MIST_FUNC_DEF(transform, move, 1), - MIST_FUNC_DEF(transform, rotate, 2), - MIST_FUNC_DEF(transform, angle, 1), - MIST_FUNC_DEF(transform, lookat, 1), - MIST_FUNC_DEF(transform, direction, 1), - MIST_FUNC_DEF(transform, unit, 0), - MIST_FUNC_DEF(transform, rect, 1), - MIST_FUNC_DEF(transform, array, 0), - MIST_FUNC_DEF(transform, torect, 0), - MIST_FUNC_DEF(transform, children, 0), -}; - JSC_CCALL(datastream_time, return number2js(js,plm_get_time(js2datastream(js,self)->plm)); ) JSC_CCALL(datastream_seek, ds_seek(js2datastream(js,self), js2number(js,argv[0]))) JSC_CCALL(datastream_advance, ds_advance(js2datastream(js,self), js2number(js,argv[0]))) @@ -6383,128 +2016,11 @@ static const JSCFunctionListEntry js_font_funcs[] = { MIST_FUNC_DEF(font, text_size, 3), }; -JSC_CCALL(geometry_rect_intersection, - rect a = js2rect(js,argv[0]); - rect b = js2rect(js,argv[1]); - rect c; - SDL_GetRectIntersectionFloat(&a, &b, &c); - return rect2js(js,c); -) - -JSC_CCALL(geometry_rect_intersects, - rect a = js2rect(js,argv[0]); - rect b = js2rect(js,argv[1]); - return JS_NewBool(js, SDL_HasRectIntersectionFloat(&a,&b)); -) - -JSC_CCALL(geometry_rect_inside, - rect inner = js2rect(js,argv[0]); - rect outer = js2rect(js,argv[1]); - return JS_NewBool(js, - inner.x >= outer.x && - inner.x + inner.w <= outer.x + outer.w && - inner.y >= outer.y && - inner.y + inner.h <= outer.y + outer.h - ); -) - -JSC_CCALL(geometry_rect_random, - rect a = js2rect(js,argv[0]); - return vec22js(js,(HMM_Vec2){ - a.x + rand_range(js,-0.5,0.5)*a.w, - a.y + rand_range(js,-0.5,0.5)*a.h - }); -) - -JSC_CCALL(geometry_rect_point_inside, - rect a = js2rect(js,argv[0]); - HMM_Vec2 p = js2vec2(js,argv[1]); - return JS_NewBool(js,p.x >= a.x && p.x <= a.x+a.w && p.y <= a.y+a.h && p.y >= a.y); -) - -JSC_CCALL(geometry_cwh2rect, - HMM_Vec2 c = js2vec2(js,argv[0]); - HMM_Vec2 wh = js2vec2(js,argv[1]); - rect r; - r.x = c.x; - r.y = c.y; - r.w = wh.x; - r.h = wh.y; - return rect2js(js,r); -) - -JSC_CCALL(geometry_rect_pos, - rect r = js2rect(js,argv[0]); - return vec22js(js,(HMM_Vec2){ - .x = r.x, - .y = r.y - }); -) - -JSC_CCALL(geometry_rect_move, - rect r = js2rect(js,argv[0]); - HMM_Vec2 move = js2vec2(js,argv[1]); -// cblas_saxpy(2, 1.0f, move.e, 1, &r, 1); - r.x += move.x; - r.y += move.y; - return rect2js(js,r); -) - -/*static inline float fmin(float a, float b) -{ - if (a < b) return a; - return b; -} - -static inline float fmax(float a, float b) -{ - if (a > b) return a; - return b; -} -*/ -JSC_CCALL(geometry_rect_expand, - rect a = js2rect(js,argv[0]); - rect b = js2rect(js,argv[1]); - rect c = {0}; - c.x = fmin(a.x,b.x); - c.y = fmin(a.y,b.y); - c.w = fmax(a.x+a.w,b.x+b.w); - c.h = fmax(a.y+a.h,b.y+b.h); - return rect2js(js,c); -) - -static const JSCFunctionListEntry js_geometry_funcs[] = { - MIST_FUNC_DEF(geometry, rect_intersection, 2), - MIST_FUNC_DEF(geometry, rect_intersects, 2), - MIST_FUNC_DEF(geometry, rect_expand, 2), - MIST_FUNC_DEF(geometry, rect_inside, 2), - MIST_FUNC_DEF(geometry, rect_random, 1), - MIST_FUNC_DEF(geometry, cwh2rect, 2), - MIST_FUNC_DEF(geometry, rect_point_inside, 2), - MIST_FUNC_DEF(geometry, rect_pos, 1), - MIST_FUNC_DEF(geometry, rect_move, 2), - MIST_FUNC_DEF(gpu, tile, 4), - MIST_FUNC_DEF(gpu, slice9, 3), -}; - -JSC_SCALL(os_env, - char *env = getenv(str); - if (env) ret = JS_NewString(js,env); -) - JSValue js_os_sys(JSContext *js, JSValue self, int argc, JSValue *argv) { return JS_NewString(js, SDL_GetPlatform()); } -JSC_CCALL(os_exit, exit(js2number(js,argv[0]));) -JSC_CCALL(os_gc, JS_RunGC(JS_GetRuntime(js)) ) -JSC_CCALL(os_now, return number2js(js, (double)SDL_GetTicksNS()/1000000000.0)) -JSC_CCALL(os_mem_limit, JS_SetMemoryLimit(JS_GetRuntime(js), js2number(js,argv[0]))) -JSC_CCALL(os_gc_threshold, JS_SetGCThreshold(JS_GetRuntime(js), js2number(js,argv[0]))) -JSC_CCALL(os_max_stacksize, JS_SetMaxStackSize(JS_GetRuntime(js), js2number(js,argv[0]))) -JSC_CCALL(os_rt_info, return JS_GetRTInfo(JS_GetRuntime(js),js)) - JSC_CCALL(os_dump_atoms, return js_dump_atoms(js); ) @@ -6513,97 +2029,6 @@ JSC_CCALL(os_dump_shapes, return js_dump_shapes(js); ) -JSC_CCALL(os_calc_mem, - return number2js(js,JS_MyValueSize(JS_GetRuntime(js), argv[0])); -) - -#define JSOBJ_ADD_FIELD(OBJ, STRUCT, FIELD, TYPE) \ -JS_SetPropertyStr(js, OBJ, #FIELD, TYPE##2js(js,STRUCT.FIELD));\ - -#define JSJMEMRET(FIELD) JSOBJ_ADD_FIELD(ret, jsmem, FIELD, number) - -JSC_CCALL(os_memstate, - JSMemoryUsage jsmem; - JS_FillMemoryState(JS_GetRuntime(js), &jsmem); - ret = JS_NewObject(js); - JSJMEMRET(malloc_size) - JSJMEMRET(malloc_limit) - JSJMEMRET(memory_used_size) - JSJMEMRET(gc_threshold) - JSJMEMRET(malloc_count) - JSJMEMRET(memory_used_count) - JSJMEMRET(atom_count) - JSJMEMRET(atom_size) - JSJMEMRET(str_count) - JSJMEMRET(str_size) - JSJMEMRET(obj_count) - JSJMEMRET(obj_size) - JSJMEMRET(prop_count) - JSJMEMRET(prop_size) - JSJMEMRET(shape_count) - JSJMEMRET(shape_size) - JSJMEMRET(js_func_count) - JSJMEMRET(js_func_size) - JSJMEMRET(js_func_code_size) - JSJMEMRET(js_func_pc2line_count) - JSJMEMRET(js_func_pc2line_size) - JSJMEMRET(c_func_count) - JSJMEMRET(array_count) - JSJMEMRET(fast_array_count) - JSJMEMRET(fast_array_elements) - JSJMEMRET(binary_object_count) - JSJMEMRET(binary_object_size) -) - -JSC_CCALL(os_mallinfo, - ret = JS_UNDEFINED; - /*struct mallinfo jsmem = mallinfo(); - ret = JS_NewObject(js); - JSJMEMRET(arena); - JSJMEMRET(ordblks); - JSJMEMRET(smblks); - JSJMEMRET(hblks); - JSJMEMRET(hblkhd); - JSJMEMRET(usmblks); - JSJMEMRET(uordblks); - JSJMEMRET(fordblks); - JSJMEMRET(keepcost);*/ -) - -JSValue js_os_rusage(JSContext *js, JSValue self, int argc, JSValue *argv) { - JSValue ret = JS_NewObject(js); - -#ifndef _WIN32 - struct rusage jsmem; - getrusage(RUSAGE_SELF, &jsmem); - JSJMEMRET(ru_maxrss); - JSJMEMRET(ru_ixrss); - JSJMEMRET(ru_idrss); - JSJMEMRET(ru_isrss); - JSJMEMRET(ru_minflt); - JSJMEMRET(ru_majflt); - JSJMEMRET(ru_nswap); - JSJMEMRET(ru_inblock); - JSJMEMRET(ru_oublock); - JSJMEMRET(ru_msgsnd); - JSJMEMRET(ru_msgrcv); - JSJMEMRET(ru_nsignals); - JSJMEMRET(ru_nvcsw); - JSJMEMRET(ru_nivcsw); -#endif - - return ret; -} - -JSC_CCALL(os_mem, return js_get_memory_usage(js)) -JSC_CCALL(os_value_id, - return number2js(js,(intptr_t)JS_VALUE_GET_PTR(self)); -) - -JSC_SSCALL(os_eval, - ret = JS_Eval(js, str2, strlen(str2), str, JS_EVAL_FLAG_STRICT); -) - // input: (encoded image data of jpg, png, bmp, tiff) JSC_CCALL(os_make_texture, size_t len; @@ -6781,15 +2206,16 @@ JSC_CCALL(os_make_surface, ret = SDL_Surface2js(js, surface); ) +// TODO: Implement this correctly JSC_CCALL(os_make_cursor, - if (SDL_GetCurrentThreadID() != main_thread) + /*if (SDL_GetCurrentThreadID() != main_thread) return JS_ThrowInternalError(js, "This can only be called from the root actor."); SDL_Surface *s = js2SDL_Surface(js,argv[0]); HMM_Vec2 hot = js2vec2(js,argv[1]); SDL_Cursor *c = SDL_CreateColorCursor(s, hot.x, hot.y); if (!c) return JS_ThrowReferenceError(js,"couldn't make cursor: %s", SDL_GetError()); - return SDL_Cursor2js(js,c); + return SDL_Cursor2js(js,c);*/ ) JSC_CCALL(os_make_font, @@ -6809,7 +2235,7 @@ JSC_CCALL(os_make_transform, t->self = ret; ) JSC_CCALL(os_make_sprite, - sprite *sp = make_sprite(); + /*sprite *sp = make_sprite(); JS_GETATOM(js, sp->pos, argv[0], pos, vec2) JS_GETATOM(js, sp->center, argv[0], center, vec2) JS_GETATOM(js, sp->skew, argv[0], skew, vec2) @@ -6818,7 +2244,7 @@ JSC_CCALL(os_make_sprite, JS_GETATOM(js, sp->layer, argv[0], layer, number) JS_GETATOM(js, sp->color, argv[0], color, color) sprite_apply(sp); - return sprite2js(js,sp); + return sprite2js(js,sp);*/ ) JSC_SCALL(os_system, ret = number2js(js,system(str)); ) @@ -6902,7 +2328,7 @@ JSC_CCALL(os_make_video, JSC_CCALL(os_rectpack, int width = js2number(js,argv[0]); int height = js2number(js,argv[1]); - int num = js_arrlen(js,argv[2]); + int num = JS_ArrayLength(js,argv[2]); stbrp_context ctx[1]; stbrp_rect rects[num]; @@ -6968,7 +2394,7 @@ JSC_CCALL(os_battery_seconds, JSC_CCALL(os_insertion_sort, JSValue arr = argv[0]; JSValue cmp = argv[1]; - int len = js_arrlen(js, arr); + int len = JS_ArrayLength(js, arr); for (int i = 1; i < len; i++) { JSValue key = JS_GetPropertyUint32(js, arr, i); @@ -7026,7 +2452,7 @@ JSC_CCALL(os_cull_sprites, camera_rect.w = info.render_size.x; camera_rect.h = info.render_size.y; - int len = js_arrlen(js,sprites); + int len = JS_ArrayLength(js,sprites); for (int i = 0; i < len; i++) { JSValue sub = JS_GetPropertyUint32(js,sprites,i); @@ -7273,52 +2699,6 @@ JSC_CCALL(os_destroy, rt->need_stop = 1; ) -static const JSCFunctionListEntry js_os_funcs[] = { - MIST_FUNC_DEF(os, make_transform, 0), - MIST_FUNC_DEF(os, clean_transforms, 0), - - MIST_FUNC_DEF(os, platform, 0), - MIST_FUNC_DEF(os, arch, 0), - MIST_FUNC_DEF(os, totalmem, 0), - MIST_FUNC_DEF(os, freemem, 0), - MIST_FUNC_DEF(os, hostname, 0), - MIST_FUNC_DEF(os, version, 0), - JS_CGETSET_DEF("trace", js_os_get_trace, js_os_set_trace), - - MIST_FUNC_DEF(os, kill, 1), - MIST_FUNC_DEF(os, exit, 1), - - MIST_FUNC_DEF(os, now, 0), - - MIST_FUNC_DEF(os, openurl, 1), - - MIST_FUNC_DEF(os, sleep, 1), - MIST_FUNC_DEF(os, battery_pct, 0), - MIST_FUNC_DEF(os, battery_voltage, 0), - MIST_FUNC_DEF(os, battery_seconds, 0), - MIST_FUNC_DEF(os, power_state, 0), - - MIST_FUNC_DEF(os, on, 2), - - MIST_FUNC_DEF(os, rusage, 0), - MIST_FUNC_DEF(os, mallinfo, 0), - - // dangerous ones that need disabled for shipping - MIST_FUNC_DEF(os, env, 1), - MIST_FUNC_DEF(os, system, 1), - MIST_FUNC_DEF(os, createprocess, 0), - MIST_FUNC_DEF(os, createactor, 1), - - MIST_FUNC_DEF(os, mailbox_push, 2), - MIST_FUNC_DEF(os, mailbox_exist, 1), - MIST_FUNC_DEF(actor, delay, 2), - MIST_FUNC_DEF(actor, removetimer, 1), - MIST_FUNC_DEF(os, register_actor, 2), - MIST_FUNC_DEF(os, unneeded, 2), - MIST_FUNC_DEF(os, destroy, 0), - MIST_FUNC_DEF(os, ioactor, 0), -}; - JSC_CCALL(js_dump_class, return js_get_object_class_distribution(js)) JSC_CCALL(js_dump_type_overheads, return js_get_object_type_overheads(js)) JSC_CCALL(js_dump_objects, return js_dump_objects(js)) @@ -7342,6 +2722,110 @@ JSC_CCALL(js_cycle_hook, JSC_CCALL(js_stack_info, return js_dump_stack_info(js)) +JSC_CCALL(os_mem, return js_get_memory_usage(js)) + +#define JSOBJ_ADD_FIELD(OBJ, STRUCT, FIELD, TYPE) \ +JS_SetPropertyStr(js, OBJ, #FIELD, TYPE##2js(js,STRUCT.FIELD));\ + +#define JSJMEMRET(FIELD) JSOBJ_ADD_FIELD(ret, jsmem, FIELD, number) + +JSC_CCALL(os_memstate, + JSMemoryUsage jsmem; + JS_FillMemoryState(JS_GetRuntime(js), &jsmem); + ret = JS_NewObject(js); + JSJMEMRET(malloc_size) + JSJMEMRET(malloc_limit) + JSJMEMRET(memory_used_size) + JSJMEMRET(gc_threshold) + JSJMEMRET(malloc_count) + JSJMEMRET(memory_used_count) + JSJMEMRET(atom_count) + JSJMEMRET(atom_size) + JSJMEMRET(str_count) + JSJMEMRET(str_size) + JSJMEMRET(obj_count) + JSJMEMRET(obj_size) + JSJMEMRET(prop_count) + JSJMEMRET(prop_size) + JSJMEMRET(shape_count) + JSJMEMRET(shape_size) + JSJMEMRET(js_func_count) + JSJMEMRET(js_func_size) + JSJMEMRET(js_func_code_size) + JSJMEMRET(js_func_pc2line_count) + JSJMEMRET(js_func_pc2line_size) + JSJMEMRET(c_func_count) + JSJMEMRET(array_count) + JSJMEMRET(fast_array_count) + JSJMEMRET(fast_array_elements) + JSJMEMRET(binary_object_count) + JSJMEMRET(binary_object_size) +) + +JSC_CCALL(os_gc, JS_RunGC(JS_GetRuntime(js)) ) + +JSC_SSCALL(os_eval, + ret = JS_Eval(js,str,strlen(str),str2,JS_EVAL_TYPE_GLOBAL); +) + +JSC_CCALL(os_mem_limit, JS_SetMemoryLimit(JS_GetRuntime(js), js2number(js,argv[0]))) + +JSC_CCALL(os_gc_threshold, JS_SetGCThreshold(JS_GetRuntime(js), js2number(js,argv[0]))) + +JSC_CCALL(os_max_stacksize, JS_SetMaxStackSize(JS_GetRuntime(js), js2number(js,argv[0]))) +JSC_CCALL(os_rt_info, return JS_GetRTInfo(JS_GetRuntime(js),js)) + +JSC_CCALL(os_calc_mem, + JSMemoryUsage mu; + JS_ComputeMemoryUsage(JS_GetRuntime(js),&mu); + ret = JS_NewObject(js); + JS_SetPropertyStr(js,ret,"malloc_size",number2js(js,mu.malloc_size)); + JS_SetPropertyStr(js,ret,"malloc_limit",number2js(js,mu.malloc_limit)); + JS_SetPropertyStr(js,ret,"memory_used_size",number2js(js,mu.memory_used_size)); + JS_SetPropertyStr(js,ret,"malloc_count",number2js(js,mu.malloc_count)); + JS_SetPropertyStr(js,ret,"memory_used_count",number2js(js,mu.memory_used_count)); + JS_SetPropertyStr(js,ret,"atom_count",number2js(js,mu.atom_count)); + JS_SetPropertyStr(js,ret,"atom_size",number2js(js,mu.atom_size)); + JS_SetPropertyStr(js,ret,"str_count",number2js(js,mu.str_count)); + JS_SetPropertyStr(js,ret,"str_size",number2js(js,mu.str_size)); + JS_SetPropertyStr(js,ret,"obj_count",number2js(js,mu.obj_count)); + JS_SetPropertyStr(js,ret,"obj_size",number2js(js,mu.obj_size)); + JS_SetPropertyStr(js,ret,"prop_count",number2js(js,mu.prop_count)); + JS_SetPropertyStr(js,ret,"prop_size",number2js(js,mu.prop_size)); + JS_SetPropertyStr(js,ret,"shape_count",number2js(js,mu.shape_count)); + JS_SetPropertyStr(js,ret,"shape_size",number2js(js,mu.shape_size)); + JS_SetPropertyStr(js,ret,"js_func_count",number2js(js,mu.js_func_count)); + JS_SetPropertyStr(js,ret,"js_func_size",number2js(js,mu.js_func_size)); + JS_SetPropertyStr(js,ret,"js_func_code_size",number2js(js,mu.js_func_code_size)); + JS_SetPropertyStr(js,ret,"js_func_pc2line_count",number2js(js,mu.js_func_pc2line_count)); + JS_SetPropertyStr(js,ret,"js_func_pc2line_size",number2js(js,mu.js_func_pc2line_size)); + JS_SetPropertyStr(js,ret,"c_func_count",number2js(js,mu.c_func_count)); + JS_SetPropertyStr(js,ret,"array_count",number2js(js,mu.array_count)); + JS_SetPropertyStr(js,ret,"fast_array_count",number2js(js,mu.fast_array_count)); + JS_SetPropertyStr(js,ret,"fast_array_elements",number2js(js,mu.fast_array_elements)); + JS_SetPropertyStr(js,ret,"binary_object_count",number2js(js,mu.binary_object_count)); + JS_SetPropertyStr(js,ret,"binary_object_size",number2js(js,mu.binary_object_size)); +) +/* +JSC_CCALL(os_memstate, +#ifdef _WIN32 + MEMORYSTATUSEX statex; + statex.dwLength = sizeof (statex); + GlobalMemoryStatusEx (&statex); + ret = JS_NewObject(js); + JS_SetPropertyStr(js,ret,"load",number2js(js,statex.dwMemoryLoad)); + JS_SetPropertyStr(js,ret,"physical",number2js(js,statex.ullTotalPhys)); + JS_SetPropertyStr(js,ret,"availphysical",number2js(js,statex.ullAvailPhys)); + JS_SetPropertyStr(js,ret,"pagefile",number2js(js,statex.ullTotalPageFile)); + JS_SetPropertyStr(js,ret,"availpagefile",number2js(js,statex.ullAvailPageFile)); + JS_SetPropertyStr(js,ret,"virtual",number2js(js,statex.ullTotalVirtual)); + JS_SetPropertyStr(js,ret,"availvirtual",number2js(js,statex.ullAvailVirtual)); + JS_SetPropertyStr(js,ret,"availextended",number2js(js,statex.ullAvailExtendedVirtual)); +#else + ret = JS_NewObject(js); +#endif +)*/ + static const JSCFunctionListEntry js_js_funcs[] = { MIST_FUNC_DEF(js, cycle_hook,1), MIST_FUNC_DEF(os, dump_shapes, 0), @@ -7414,6 +2898,253 @@ JSC_CCALL(graphics_save_jpg, return JS_ThrowInternalError(js, "Could not write png"); ) +JSC_CCALL(gpu_make_sprite_queue, + sprite *sprites = NULL; + size_t quads = 0; + int needfree = 1; + + 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++) + JS_DupValue(js,sprites[i].image); + } else { + quads = JS_ArrayLength(js, argv[0]); + if (quads == 0) + return JS_ThrowReferenceError(js, "Expected an array of sprites with length > 0."); + + arrsetcap(sprites, quads); + + for (int i = 0; i < quads; i++) { + JSValue sub = JS_GetPropertyUint32(js, argv[0], i); + sprite *jsp = js2sprite(js, sub); + if (jsp) { + arrput(sprites, *jsp); + JS_DupValue(js,jsp->image); + } + else { + sprite sp = {0}; + JS_GETATOM(js, sp.pos, sub, pos, vec2) + JS_GETATOM(js, sp.center, sub, center, vec2) + JS_GETATOM(js, sp.skew, sub, skew, vec2) + JS_GETATOM(js, sp.scale, sub, scale, vec2) + JS_GETATOM(js, sp.color,sub,color,color) + JS_GETATOM(js, sp.layer,sub,layer,number) + sp.image = JS_GetProperty(js,sub,image); + sprite_apply(&sp); + arrput(sprites,sp); + } + JS_FreeValue(js, sub); + } + } + + qsort(sprites, quads, sizeof(sprite), sort_sprite); + + struct quad_buffers buffers = quad_buffers_new(quads*4); + + const HMM_Vec2 local[4] = { {0,0}, {1,0}, {0,1}, {1,1} }; + + rect uv; + rect uv_px; + JSValue cur_img = JS_UNDEFINED; + + for (size_t i = 0; i < quads; i++) { + sprite *s = &sprites[i]; + if (JS_IsUndefined(cur_img) || !JS_StrictEq(js, s->image, cur_img)) { + cur_img = s->image; + JS_GETATOM(js, uv, cur_img, rect, rect) + JS_GETATOM(js, uv_px, cur_img, rect_px, rect) + } + + HMM_Vec2 px_size = { + uv_px.w * s->scale.X, + uv_px.h * s->scale.Y + }; + + HMM_Vec2 anchor = { + px_size.X * s->center.X, + px_size.Y * s->center.Y + }; + + size_t base = i * 4; + + for (int v = 0; v < 4; v++) { + HMM_Vec2 lp = { + local[v].X * px_size.X - anchor.X, + local[v].Y * px_size.Y - anchor.Y + }; + + HMM_Vec2 world = HMM_AddV2(s->pos, HMM_MulM2V2(s->affine, lp)); + + buffers.pos[base + v] = world; + buffers.color[base + v] = s->color; + } + + /* UVs are still top-left-origin pixel coords, so keep previous packing */ + buffers.uv[base + 0] = (HMM_Vec2){ uv.x, uv.y + uv.h }; + buffers.uv[base + 1] = (HMM_Vec2){ uv.x + uv.w, uv.y + uv.h }; + buffers.uv[base + 2] = (HMM_Vec2){ uv.x, uv.y }; + buffers.uv[base + 3] = (HMM_Vec2){ uv.x + uv.w, uv.y }; + } + + JSValue mesh = quadbuffers_to_mesh(js, buffers); + + ret = JS_NewArray(js); + int first_index = 0; + int count = 0; + int n = 0; + JSValue img = JS_UNDEFINED; + + for (int i = 0; i < quads; i++) { + if (!JS_SameValue(js, sprites[i].image, img)) { + if (count > 0) { + JSValue q = JS_NewObject(js); + JS_SetPropertyStr(js, q, "type", JS_NewString(js, "geometry")); + JS_SetPropertyStr(js, q, "mesh", JS_DupValue(js, mesh)); + JS_SetPropertyStr(js, q, "pipeline", JS_DupValue(js, argv[2])); + JS_SetPropertyStr(js, q, "image", img); + JS_SetPropertyStr(js, q, "first_index", number2js(js, first_index)); + JS_SetPropertyStr(js, q, "num_indices", number2js(js, count * 6)); + JS_SetPropertyUint32(js, ret, n++, q); + } + first_index = i*6; + count = 1; + img = JS_DupValue(js, sprites[i].image); + } else count++; + JS_FreeValue(js,sprites[i].image); + } + + if (count > 0) { + JSValue q = JS_NewObject(js); + JS_SetPropertyStr(js, q, "type", JS_NewString(js, "geometry")); + JS_SetPropertyStr(js, q, "mesh", JS_DupValue(js, mesh)); + JS_SetPropertyStr(js, q, "pipeline", JS_DupValue(js, argv[2])); + JS_SetPropertyStr(js, q, "image", img); + JS_SetPropertyStr(js, q, "first_index", number2js(js, first_index)); + JS_SetPropertyStr(js, q, "num_indices", number2js(js, count * 6)); + JS_SetPropertyUint32(js, ret, n++, q); + } + + if (needfree) + arrfree(sprites); + + JS_FreeValue(js, mesh); +) + +JSC_CCALL(gpu_make_sprite_mesh, + size_t quads = JS_ArrayLength(js, argv[0]); + size_t verts = quads*4; + size_t count = quads*6; + + // Prepare arrays on CPU + HMM_Vec2 *posdata = malloc(sizeof(*posdata)*verts); + HMM_Vec2 *uvdata = malloc(sizeof(*uvdata)*verts); + HMM_Vec4 *colordata = malloc(sizeof(*colordata)*verts); + + for (int i = 0; i < quads; i++) { + JSValue sub = JS_GetPropertyUint32(js,argv[0],i); + + transform *tr; + rect src; + HMM_Vec4 color; + JS_GETATOM(js,src,sub,src,rect) + JS_GETATOM(js,color,sub,color,color) + JS_GETATOM(js,tr,sub,transform,transform) + JS_FreeValue(js,sub); + + size_t base = i*4; + if (tr) { + HMM_Mat3 trmat = transform2mat3(tr); + for (int j = 0; j < 4; j++) + posdata[base+j] = HMM_MulM3V3(trmat, base_quad[j]).xy; + } else { + rect dst; + JS_GETATOM(js,dst,sub,rect,rect); + posdata[base+0] = (HMM_Vec2){dst.x,dst.y}; + posdata[base + 1] = (HMM_Vec2){ dst.x+dst.w, dst.y }; + posdata[base + 2] = (HMM_Vec2){ dst.x, dst.y+dst.h }; + posdata[base + 3] = (HMM_Vec2){ dst.x+dst.w, dst.y+dst.h }; + } + + uvdata[base+0] = (HMM_Vec2){src.x, src.y+src.h}; + uvdata[base+1] = (HMM_Vec2){src.x+src.w, src.y+src.h}; + uvdata[base+2] = (HMM_Vec2){src.x, src.y}; + uvdata[base+3] = (HMM_Vec2){src.x+src.w, src.y}; + + colordata[base+0] = color; + colordata[base+1] = color; + colordata[base+2] = color; + colordata[base+3] = color; + } + + // Check old mesh + JSValue old_mesh = JS_UNDEFINED; + if (argc > 1) + old_mesh = argv[1]; + + // Needed sizes + size_t pos_size = sizeof(*posdata)*verts; + size_t uv_size = sizeof(*uvdata)*verts; + size_t color_size = sizeof(*colordata)*verts; + + BufferCheckResult pos_chk = get_or_extend_buffer(js, old_mesh, "pos", pos_size, JS_TYPED_ARRAY_FLOAT32, 2, 1, 0); + BufferCheckResult uv_chk = get_or_extend_buffer(js, old_mesh, "uv", uv_size, JS_TYPED_ARRAY_FLOAT32, 2, 1, 0); + BufferCheckResult color_chk = get_or_extend_buffer(js, old_mesh, "color", color_size, JS_TYPED_ARRAY_FLOAT32, 4, 1, 0); + + int need_new_all = pos_chk.need_new || uv_chk.need_new || color_chk.need_new; + + ret = JS_NewObject(js); + + if (need_new_all) { + // Create all new buffers + JSValue new_pos = make_gpu_buffer(js, posdata, pos_size, JS_TYPED_ARRAY_FLOAT32, 2, 1,0); + JSValue new_uv = make_gpu_buffer(js, uvdata, uv_size, JS_TYPED_ARRAY_FLOAT32, 2, 1,0); + JSValue new_color = make_gpu_buffer(js, colordata, color_size, JS_TYPED_ARRAY_FLOAT32, 0, 1,0); + + JS_SetPropertyStr(js, ret, "pos", new_pos); + JS_SetPropertyStr(js, ret, "uv", new_uv); + JS_SetPropertyStr(js, ret, "color", new_color); + + // Indices + JSValue indices = make_quad_indices_buffer(js, quads); + JS_SetPropertyStr(js, ret, "indices", indices); + } else { + // Reuse the old buffers + // Just copy data into existing buffers via their pointers + memcpy(pos_chk.ptr, posdata, pos_size); + memcpy(uv_chk.ptr, uvdata, uv_size); + memcpy(color_chk.ptr, colordata, color_size); + + // Duplicate old references since we're returning a new object + JS_SetPropertyStr(js, ret, "pos", JS_DupValue(js, pos_chk.val)); + JS_SetPropertyStr(js, ret, "uv", JS_DupValue(js, uv_chk.val)); + JS_SetPropertyStr(js, ret, "color", JS_DupValue(js, color_chk.val)); + + // Indices can remain the same if they were also large enough. If using a shared global index buffer: + JSValue indices = make_quad_indices_buffer(js, quads); + JS_SetPropertyStr(js, ret, "indices", indices); + } + + JS_SetPropertyStr(js, ret, "vertices", number2js(js, verts)); + JS_SetPropertyStr(js, ret, "count", number2js(js, count)); + JS_SetPropertyStr(js,ret,"num_indices", number2js(js,count)); + + // Free temporary CPU arrays + free(posdata); + free(uvdata); + free(colordata); + + // Free old buffer values if they were fetched + if (!JS_IsUndefined(pos_chk.val)) JS_FreeValue(js, pos_chk.val); + if (!JS_IsUndefined(uv_chk.val)) JS_FreeValue(js, uv_chk.val); + if (!JS_IsUndefined(color_chk.val)) JS_FreeValue(js, color_chk.val); + + return ret; +) + static const JSCFunctionListEntry js_graphics_funcs[] = { MIST_FUNC_DEF(gpu, make_sprite_mesh, 2), MIST_FUNC_DEF(gpu, make_sprite_queue, 4), @@ -7633,45 +3364,6 @@ static const JSCFunctionListEntry js_rtree_funcs[] = { MIST_FUNC_DEF(rtree,values,0), }; -JSC_GETSET(sprite, pos, vec2) -JSC_GETSET(sprite, center, vec2) -JSC_GETSET(sprite, layer, number) -JSC_GETSET(sprite, color, color) -JSC_GETSET(sprite, skew, vec2) -JSC_GETSET(sprite, scale, vec2) -JSC_GETSET(sprite, rotation, number) - -JSC_CCALL(sprite_move, - sprite *sp = js2sprite(js,self); - HMM_Vec2 mv = js2vec2(js,argv[0]); - sp->pos.x += mv.x; - sp->pos.y += mv.y; -) - -JSC_CCALL(sprite_set_affine, - sprite *sp = js2sprite(js,self); - sprite_apply(sp); -) - -JSC_CCALL(sprite_set_image, - sprite *sp = js2sprite(js,self); - if (!JS_IsUndefined(sp->image)) - JS_FreeValue(js,sp->image); - sp->image = JS_DupValue(js, argv[0]); -) - -static const JSCFunctionListEntry js_sprite_funcs[] = { - MIST_FUNC_DEF(sprite, set_affine, 0), - MIST_FUNC_DEF(sprite, set_image, 1), - MIST_FUNC_DEF(sprite, move, 1), - JS_CGETSET_DEF("pos", js_sprite_get_pos, js_sprite_set_pos), - JS_CGETSET_DEF("scale", js_sprite_get_scale, js_sprite_set_scale), - JS_CGETSET_DEF("skew", js_sprite_get_skew, js_sprite_set_skew), - JS_CGETSET_DEF("layer", js_sprite_get_layer, js_sprite_set_layer), - JS_CGETSET_DEF("color", js_sprite_get_color, js_sprite_set_color), - JS_CGETSET_DEF("center", js_sprite_get_center, js_sprite_set_center), - JS_CGETSET_DEF("rotation", js_sprite_get_rotation, js_sprite_set_rotation), -}; #define JSSTATIC(NAME, PARENT) \ js_##NAME = JS_NewObject(js); \ @@ -7685,19 +3377,47 @@ JSValue js_miniz_use(JSContext *js); JSValue js_tracy_use(JSContext *js); #endif -MISTUSE(io) -MISTUSE(os) -MISTUSE(input) -MISTUSE(math) -MISTUSE(spline) -MISTUSE(geometry) -MISTUSE(js) -MISTUSE(graphics) -MISTUSE(util) -MISTUSE(video) -MISTUSE(camera) -MISTUSE(debug) -MISTUSE(sdl_audio) +JSValue js_spline_use(JSContext *js) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,js_spline_funcs,countof(js_spline_funcs)); + return mod; +} + +JSValue js_js_use(JSContext *js) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,js_js_funcs,countof(js_js_funcs)); + return mod; +} + +JSValue js_graphics_use(JSContext *js) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,js_graphics_funcs,countof(js_graphics_funcs)); + return mod; +} + +JSValue js_util_use(JSContext *js) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,js_util_funcs,countof(js_util_funcs)); + return mod; +} + +JSValue js_video_use(JSContext *js) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,js_video_funcs,countof(js_video_funcs)); + return mod; +} + +JSValue js_debug_use(JSContext *js) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,js_debug_funcs,countof(js_debug_funcs)); + return mod; +} + + +JSC_CCALL(os_value_id, + JS_SetPropertyStr(js, argv[0], "id", number2js(js, (uintptr_t)JS_VALUE_GET_PTR(argv[0]))); + return argv[0]; +) #include "qjs_crypto.h" #include "qjs_time.h" @@ -7713,13 +3433,13 @@ void ffi_load(JSContext *js) m_seedRand(&rt->mrand, time(NULL)); - arrput(rt->module_registry, MISTLINE(io)); - arrput(rt->module_registry, MISTLINE(os)); - arrput(rt->module_registry, MISTLINE(input)); + arrput(rt->module_registry, ((ModuleEntry){"io", js_io_use})); + arrput(rt->module_registry, ((ModuleEntry){"os", js_os_use})); + arrput(rt->module_registry, ((ModuleEntry){"input", js_input_use})); arrput(rt->module_registry, MISTLINE(time)); - arrput(rt->module_registry, MISTLINE(math)); + arrput(rt->module_registry, ((ModuleEntry){"math", js_math_use})); arrput(rt->module_registry, MISTLINE(spline)); - arrput(rt->module_registry, MISTLINE(geometry)); + arrput(rt->module_registry, ((ModuleEntry){"geometry", js_geometry_use})); arrput(rt->module_registry, MISTLINE(graphics)); arrput(rt->module_registry, MISTLINE(js)); arrput(rt->module_registry, MISTLINE(util)); @@ -7729,7 +3449,7 @@ void ffi_load(JSContext *js) arrput(rt->module_registry, MISTLINE(layout)); arrput(rt->module_registry, MISTLINE(miniz)); arrput(rt->module_registry, MISTLINE(imgui)); - arrput(rt->module_registry, MISTLINE(camera)); + arrput(rt->module_registry, ((ModuleEntry){"camera", js_camera_use})); arrput(rt->module_registry, MISTLINE(debug)); arrput(rt->module_registry, MISTLINE(dmon)); arrput(rt->module_registry, MISTLINE(nota)); @@ -7739,7 +3459,7 @@ void ffi_load(JSContext *js) arrput(rt->module_registry, MISTLINE(crypto)); arrput(rt->module_registry, MISTLINE(blob)); arrput(rt->module_registry, MISTLINE(http)); - arrput(rt->module_registry, MISTLINE(sdl_audio)); + arrput(rt->module_registry, ((ModuleEntry){"sdl_audio", js_sdl_audio_use})); #ifdef TRACY_ENABLE arrput(rt->module_registry, MISTLINE(tracy)); @@ -7756,40 +3476,21 @@ void ffi_load(JSContext *js) QJSCLASSPREP_FUNCS(SDL_Window) QJSCLASSPREP_FUNCS(SDL_Surface) QJSCLASSPREP_FUNCS(SDL_Texture) - QJSCLASSPREP_NO_FUNCS(SDL_Cursor) - QJSCLASSPREP_FUNCS(SDL_Camera) - - QJSCLASSPREP_FUNCS(SDL_AudioStream) + // Initialize extracted modules which handle their own class registrations + js_input_use(js); // Handles input + js_camera_use(js); // Handles camera + js_sdl_audio_use(js); // Handles SDL_AudioStream and other SDL classes + js_sdl_gpu_use(js); // Handles all GPU classes + js_io_use(js); // Handles PHYSFS_File + QJSCLASSPREP_FUNCS(renderer_ctx) - - QJSCLASSPREP_FUNCS(SDL_GPUDevice) - QJSCLASSPREP_FUNCS(SDL_GPUTexture) - QJSCLASSPREP_FUNCS(SDL_GPUCommandBuffer) - QJSCLASSPREP_FUNCS(SDL_GPURenderPass) - QJSCLASSPREP_FUNCS(SDL_GPUComputePass) - - QJSCLASSPREP_NO_FUNCS(SDL_GPUCopyPass) - QJSCLASSPREP_NO_FUNCS(SDL_GPUFence) - QJSCLASSPREP_NO_FUNCS(SDL_GPUTransferBuffer) - QJSCLASSPREP_NO_FUNCS(SDL_GPUShader) - QJSCLASSPREP_NO_FUNCS(SDL_GPUSampler) - QJSCLASSPREP_NO_FUNCS(SDL_GPUGraphicsPipeline) - QJSCLASSPREP_NO_FUNCS(SDL_GPUComputePipeline) - - QJSCLASSPREP_FUNCS(sprite) - QJSCLASSPREP_FUNCS(SDL_GPUBuffer) - QJSCLASSPREP_FUNCS(PHYSFS_File) - QJSCLASSPREP_FUNCS(transform); QJSCLASSPREP_FUNCS(font); QJSCLASSPREP_FUNCS(datastream); JS_SetPropertyStr(js, globalThis, "use_dyn", JS_NewCFunction(js,js_os_use_dyn,"use_dyn", 1)); JS_SetPropertyStr(js, globalThis, "use_embed", JS_NewCFunction(js,js_os_use_embed,"use_embed", 1)); - QJSGLOBALCLASS(os); - QJSGLOBALCLASS(console); - JSValue jsobject = JS_GetPropertyStr(js,globalThis, "Object"); JS_SetPropertyStr(js, jsobject, "id", JS_NewCFunction(js, js_os_value_id, "id", 1)); JS_FreeValue(js,jsobject); diff --git a/source/jsffi.h b/source/jsffi.h index a6f9b51b..3606cea4 100644 --- a/source/jsffi.h +++ b/source/jsffi.h @@ -2,7 +2,79 @@ #define FFI_H #include +#include "HandmadeMath.h" +#include "render.h" +#include "stb_ds.h" + +#define JS_SetProperty(js, tar, str, val) JS_SetPropertyStr(js, tar, #str, val) +#define JS_GetProperty(js, tar, atom) JS_GetPropertyStr(js, tar, #atom) + +SDL_Window *global_window; + +// Core FFI functions void ffi_load(JSContext *js); int js_print_exception(JSContext *js, JSValue v); +// Common type definitions - rect and colorf are defined in render.h + +// Common conversion functions used across modules +JSValue rect2js(JSContext *js, rect r); +JSValue vec22js(JSContext *js, HMM_Vec2 v); +JSValue vec32js(JSContext *js, HMM_Vec3 v); +JSValue vec42js(JSContext *js, HMM_Vec4 v); +JSValue quat2js(JSContext *js, HMM_Quat q); +JSValue color2js(JSContext *js, colorf c); +JSValue number2js(JSContext *js, double d); +JSValue angle2js(JSContext *js, double a); + +rect js2rect(JSContext *js, JSValue v); +HMM_Vec2 js2vec2(JSContext *js, JSValue v); +HMM_Vec3 js2vec3(JSContext *js, JSValue v); +HMM_Vec4 js2vec4(JSContext *js, JSValue v); +HMM_Quat js2quat(JSContext *js, JSValue v); +colorf js2color(JSContext *js, JSValue v); +double js2number(JSContext *js, JSValue v); +double js2angle(JSContext *js, JSValue v); + +// Forward declaration for MTRand from prosperon.h +typedef struct tagMTRand MTRand; + +// Random number generation functions +double genRand(MTRand *mrand); +uint32_t genRandLong(MTRand *mrand); +void m_seedRand(MTRand *mrand, uint32_t seed); +double rand_range(JSContext *js, double min, double max); + +// Common data structures +struct lrtb { + float l; + float r; + float t; + float b; +}; +typedef struct lrtb lrtb; +typedef struct text_vert text_vert; + +// Common macros for property access +#define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\ +JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \ +TARGET = js2##TYPE(JS, __##PROP##__v); \ +JS_FreeValue(JS,__##PROP##__v); }\ + +#define JS_GETATOM(JS, TARGET, VALUE, ATOM, TYPE) {\ +JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#ATOM); \ +TARGET = js2##TYPE(JS, __##PROP##__v); \ +JS_FreeValue(JS,__##PROP##__v); }\ + +// Common conversion functions +lrtb js2lrtb(JSContext *js, JSValue v); +int js2bool(JSContext *js, JSValue v); +JSValue make_gpu_buffer(JSContext *js, void *data, size_t size, int type, int elements, int copy, int index); +JSValue make_quad_indices_buffer(JSContext *js, int quads); +JSValue quads_to_mesh(JSContext *js, text_vert *buffer); + +// SDL type conversion functions +SDL_Window *js2SDL_Window(JSContext *js, JSValue v); +JSValue SDL_Window2js(JSContext *js, SDL_Window *w); + #endif diff --git a/source/prosperon.c b/source/prosperon.c index 4c190076..22ec5a71 100644 --- a/source/prosperon.c +++ b/source/prosperon.c @@ -49,11 +49,11 @@ static struct { char *key; prosperon_rt *value; } *actors = NULL; static unsigned char *zip_buffer_global = NULL; static char *prosperon = NULL; -static prosperon_rt *io_actor = NULL; +prosperon_rt *io_actor = NULL; static Uint32 queue_event; -static SDL_AtomicInt shutdown; +static SDL_AtomicInt engine_shutdown; static SDL_Thread **runners = NULL; static Uint32 actor_remove_cb(prosperon_rt *actor, Uint32 id, Uint32 interval) @@ -673,7 +673,7 @@ void script_evalf(JSContext *js, const char *format, ...) static int crank_actor(void *data) { - while (!SDL_GetAtomicInt(&shutdown)) { + while (!SDL_GetAtomicInt(&engine_shutdown)) { SDL_LockMutex(queue_mutex); prosperon_rt *actor = NULL; if (arrlen(ready_queue) > 0) { @@ -720,7 +720,7 @@ static void exit_handler(void) script_evalf(js, "prosperon.dispatch('exit')"); } - SDL_SetAtomicInt(&shutdown, 1); + SDL_SetAtomicInt(&engine_shutdown, 1); int status; SDL_BroadcastCondition(queue_cond); for (int i = 0; i < arrlen(runners); i++) diff --git a/source/qjs_geometry.c b/source/qjs_geometry.c new file mode 100644 index 00000000..47903405 --- /dev/null +++ b/source/qjs_geometry.c @@ -0,0 +1,447 @@ +#include "qjs_geometry.h" +#include "jsffi.h" +#include "qjs_macros.h" + +#include +#include +#include "HandmadeMath.h" +#include "prosperon.h" +#include "font.h" + + +// GEOMETRY FUNCTIONS + +JSC_CCALL(geometry_rect_intersection, + rect a = js2rect(js,argv[0]); + rect b = js2rect(js,argv[1]); + rect c; + SDL_GetRectIntersectionFloat(&a, &b, &c); + return rect2js(js,c); +) + +JSC_CCALL(geometry_rect_intersects, + rect a = js2rect(js,argv[0]); + rect b = js2rect(js,argv[1]); + return JS_NewBool(js, SDL_HasRectIntersectionFloat(&a,&b)); +) + +JSC_CCALL(geometry_rect_inside, + rect inner = js2rect(js,argv[0]); + rect outer = js2rect(js,argv[1]); + return JS_NewBool(js, + inner.x >= outer.x && + inner.x + inner.w <= outer.x + outer.w && + inner.y >= outer.y && + inner.y + inner.h <= outer.y + outer.h + ); +) + +JSC_CCALL(geometry_rect_random, + rect a = js2rect(js,argv[0]); + return vec22js(js,(HMM_Vec2){ + a.x + rand_range(js,-0.5,0.5)*a.w, + a.y + rand_range(js,-0.5,0.5)*a.h + }); +) + +JSC_CCALL(geometry_rect_point_inside, + rect a = js2rect(js,argv[0]); + HMM_Vec2 p = js2vec2(js,argv[1]); + return JS_NewBool(js,p.x >= a.x && p.x <= a.x+a.w && p.y <= a.y+a.h && p.y >= a.y); +) + +JSC_CCALL(geometry_cwh2rect, + HMM_Vec2 c = js2vec2(js,argv[0]); + HMM_Vec2 wh = js2vec2(js,argv[1]); + rect r; + r.x = c.x; + r.y = c.y; + r.w = wh.x; + r.h = wh.y; + return rect2js(js,r); +) + +JSC_CCALL(geometry_rect_pos, + rect r = js2rect(js,argv[0]); + return vec22js(js,(HMM_Vec2){ + .x = r.x, + .y = r.y + }); +) + +JSC_CCALL(geometry_rect_move, + rect r = js2rect(js,argv[0]); + HMM_Vec2 move = js2vec2(js,argv[1]); + r.x += move.x; + r.y += move.y; + return rect2js(js,r); +) + +JSC_CCALL(geometry_rect_expand, + rect a = js2rect(js,argv[0]); + rect b = js2rect(js,argv[1]); + rect c = {0}; + c.x = fmin(a.x,b.x); + c.y = fmin(a.y,b.y); + c.w = fmax(a.x+a.w,b.x+b.w); + c.h = fmax(a.y+a.h,b.y+b.h); + return rect2js(js,c); +) + +// Helper functions for geometry operations + + + +static inline void add_quad(text_vert **verts, rect *restrict src, rect *restrict dst) +{ + text_vert v = (text_vert){ + .pos = (HMM_Vec2){dst->x, dst->y}, + .uv = (HMM_Vec2){src->x,src->y}, + .color = (HMM_Vec4){1,1,1,1} + }; + arrput(*verts, v); + + v = (text_vert){ + .pos = (HMM_Vec2){dst->x+dst->w, dst->y}, + .uv = (HMM_Vec2){src->x+src->w,src->y}, + .color = (HMM_Vec4){1,1,1,1} + }; + arrput(*verts, v); + + v = (text_vert){ + .pos = (HMM_Vec2){dst->x, dst->y+dst->h}, + .uv = (HMM_Vec2){src->x,src->y+src->h}, + .color = (HMM_Vec4){1,1,1,1} + }; + arrput(*verts, v); + + v = (text_vert){ + .pos = (HMM_Vec2){dst->x+dst->w, dst->y+dst->h}, + .uv = (HMM_Vec2){src->x+src->w,src->y+src->h}, + .color = (HMM_Vec4){1,1,1,1} + }; + arrput(*verts, v); +} + +static inline void tile_region(text_vert **verts, rect src_uv, rect dst, float tex_w, float tex_h, bool tile_x, bool tile_y) +{ + // Convert the incoming UV rect into pixel coords + rect src_px = { + .x = src_uv.x * tex_w, + .y = src_uv.y * tex_h, + .w = src_uv.w * tex_w, + .h = src_uv.h * tex_h + }; + + // If src_px or dst is degenerate, early out + if (src_px.w <= 0.0f || src_px.h <= 0.0f || dst.w <= 0.0f || dst.h <= 0.0f) + return; + + // How many full tiles horizontally/vertically? + // If not tiling in a dimension, we treat it as exactly 1 tile (no leftover). + float cols_f, rows_f; + float remain_wf = 0.0f; + float remain_hf = 0.0f; + + if (tile_x) { + remain_wf = modff(dst.w / src_px.w, &cols_f); + } else { + // Only 1 "column" covering entire width, no leftover + cols_f = 1.0f; + remain_wf = 0.0f; + } + + if (tile_y) { + remain_hf = modff(dst.h / src_px.h, &rows_f); + } else { + // Only 1 "row" covering entire height, no leftover + rows_f = 1.0f; + remain_hf = 0.0f; + } + + int cols = (int)cols_f; + int rows = (int)rows_f; + + // The leftover portion in screen coords (pixels) + float remain_dst_w = remain_wf * src_px.w; + float remain_dst_h = remain_hf * src_px.h; + + // Build the UV rect for a "full" tile + rect src_full = src_uv; + + // Partial leftover in UV + float remain_src_w_uv = remain_dst_w / tex_w; + float remain_src_h_uv = remain_dst_h / tex_h; + + // For partial leftover in X dimension + rect src_partial_x = src_full; + src_partial_x.w = remain_src_w_uv; + + // For partial leftover in Y dimension + rect src_partial_y = src_full; + src_partial_y.h = remain_src_h_uv; + + // For partial leftover in both X & Y + rect src_partial_xy = src_full; + src_partial_xy.w = remain_src_w_uv; + src_partial_xy.h = remain_src_h_uv; + + // Each tile is drawn 1:1 in screen coords + float tile_w = tile_x ? src_px.w : dst.w; // If not tiling horizontally, match the entire dst width + float tile_h = tile_y ? src_px.h : dst.h; // If not tiling vertically, match the entire dst height + + rect curr_dst; + curr_dst.w = tile_w; + curr_dst.h = tile_h; + curr_dst.y = dst.y; + + // Loop over rows + for (int y = 0; y < rows; y++) { + curr_dst.x = dst.x; + + // Loop over columns + for (int x = 0; x < cols; x++) { + add_quad(verts, &src_full, &curr_dst); + curr_dst.x += tile_w; + } + + // Right-side leftover tile (only if tile_x is true) + if (tile_x && remain_dst_w > 0.0f) { + rect partial_dst = { + .x = curr_dst.x, .y = curr_dst.y, + .w = remain_dst_w, .h = tile_h + }; + add_quad(verts, &src_partial_x, &partial_dst); + } + curr_dst.y += tile_h; + } + + // Bottom leftover row (only if tile_y is true) + if (tile_y && remain_dst_h > 0.0f) { + rect partial_row_dst; + partial_row_dst.w = tile_w; + partial_row_dst.h = remain_dst_h; + partial_row_dst.y = curr_dst.y; + partial_row_dst.x = dst.x; + + // Full columns in leftover row + for (int x = 0; x < cols; x++) { + add_quad(verts, &src_partial_y, &partial_row_dst); + partial_row_dst.x += tile_w; + } + + // Partial leftover corner (both X & Y leftover) + if (tile_x && remain_dst_w > 0.0f) { + rect partial_corner_dst = { + .x = partial_row_dst.x, .y = partial_row_dst.y, + .w = remain_dst_w, .h = remain_dst_h + }; + add_quad(verts, &src_partial_xy, &partial_corner_dst); + } + } +} + + +JSC_CCALL(gpu_slice9, + JSValue jstex = argv[0]; + rect dst = js2rect(js, argv[1]); + + // Full texture in UV coords + rect src = { + .x = 0, .y = 0, + .w = 1, .h = 1 + }; + + // The "slice" LRTB in PIXELS, but we convert to UV below + lrtb src_slice = js2lrtb(js, argv[2]); + lrtb dst_slice = src_slice; + + HMM_Vec2 size; + JS_GETPROP(js, size.x, jstex, width, number) + JS_GETPROP(js, size.y, jstex, height, number) + + JSValue info = argv[3]; + int tile_top, tile_bottom, tile_left, tile_right, center_x, center_y; + JS_GETPROP(js,tile_top, info, tile_top, bool) + JS_GETPROP(js,tile_bottom,info,tile_bottom,bool) + JS_GETPROP(js,tile_left,info,tile_left,bool) + JS_GETPROP(js,tile_right,info,tile_right,bool) + JS_GETPROP(js, center_x, info, tile_center_x, bool) + JS_GETPROP(js, center_y, info, tile_center_y, bool) + + // Convert the slice edges from pixel to UV + src_slice.l /= size.x; + src_slice.r /= size.x; + src_slice.t /= size.y; + src_slice.b /= size.y; + + text_vert *verts = NULL; + rect curr_src; + rect curr_dst; + + // bottom-left corner (single quad) + curr_src = src; + curr_src.w = src_slice.l; + curr_src.h = src_slice.b; + + curr_dst = dst; + curr_dst.w = dst_slice.l; + curr_dst.h = dst_slice.b; + add_quad(&verts, &curr_src, &curr_dst); + + // top-left corner (single quad) + curr_src = src; + curr_src.x = src.x; + curr_src.y = src.y + src.h - src_slice.t; + curr_src.w = src_slice.l; + curr_src.h = src_slice.t; + + curr_dst = dst; + curr_dst.x = dst.x; + curr_dst.y = dst.y + dst.h - dst_slice.t; + curr_dst.w = dst_slice.l; + curr_dst.h = dst_slice.t; + add_quad(&verts, &curr_src, &curr_dst); + + // bottom-right corner (single quad) + curr_src = src; + curr_src.x = src.x + src.w - src_slice.r; + curr_src.y = src.y; + curr_src.w = src_slice.r; + curr_src.h = src_slice.b; + + curr_dst = dst; + curr_dst.x = dst.x + dst.w - dst_slice.r; + curr_dst.y = dst.y; + curr_dst.w = dst_slice.r; + curr_dst.h = dst_slice.b; + add_quad(&verts, &curr_src, &curr_dst); + + // top-right corner (single quad) + curr_src = src; + curr_src.x = src.x + src.w - src_slice.r; + curr_src.y = src.y + src.h - src_slice.t; + curr_src.w = src_slice.r; + curr_src.h = src_slice.t; + + curr_dst = dst; + curr_dst.x = dst.x + dst.w - dst_slice.r; + curr_dst.y = dst.y + dst.h - dst_slice.t; + curr_dst.w = dst_slice.r; + curr_dst.h = dst_slice.t; + add_quad(&verts, &curr_src, &curr_dst); + + // left bar (tiled) + curr_src = src; + curr_src.x = src.x; + curr_src.y = src.y + src_slice.b; + curr_src.w = src_slice.l; + curr_src.h = src.h - src_slice.t - src_slice.b; + + curr_dst = dst; + curr_dst.x = dst.x; + curr_dst.y = dst.y + dst_slice.b; + curr_dst.w = dst_slice.l; + curr_dst.h = dst.h - dst_slice.t - dst_slice.b; + tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_left,tile_left); + + // right bar (tiled) + curr_src = src; + curr_src.x = src.x + src.w - src_slice.r; + curr_src.y = src.y + src_slice.b; + curr_src.w = src_slice.r; + curr_src.h = src.h - src_slice.t - src_slice.b; + + curr_dst = dst; + curr_dst.x = dst.x + dst.w - dst_slice.r; + curr_dst.y = dst.y + dst_slice.b; + curr_dst.w = dst_slice.r; + curr_dst.h = dst.h - dst_slice.t - dst_slice.b; + tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_right,tile_right); + + // bottom bar (tiled) + curr_src = src; + curr_src.x = src.x + src_slice.l; + curr_src.y = src.y; + curr_src.w = src.w - src_slice.l - src_slice.r; + curr_src.h = src_slice.b; + + curr_dst = dst; + curr_dst.x = dst.x + dst_slice.l; + curr_dst.y = dst.y; + curr_dst.w = dst.w - dst_slice.l - dst_slice.r; + curr_dst.h = dst_slice.b; + tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_bottom,tile_bottom); + + // top bar (tiled) + curr_src = src; + curr_src.x = src.x + src_slice.l; + curr_src.y = src.y + src.h - src_slice.t; + curr_src.w = src.w - src_slice.l - src_slice.r; + curr_src.h = src_slice.t; + + curr_dst = dst; + curr_dst.x = dst.x + dst_slice.l; + curr_dst.y = dst.y + dst.h - dst_slice.t; + curr_dst.w = dst.w - dst_slice.l - dst_slice.r; + curr_dst.h = dst_slice.t; + tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_top,tile_top); + + // center (tiled) + curr_src = src; + curr_src.x = src.x + src_slice.l; + curr_src.y = src.y + src_slice.b; + curr_src.w = src.w - src_slice.l - src_slice.r; + curr_src.h = src.h - src_slice.t - src_slice.b; + + curr_dst = dst; + curr_dst.x = dst.x + dst_slice.l; + curr_dst.y = dst.y + dst_slice.b; + curr_dst.w = dst.w - dst_slice.l - dst_slice.r; + curr_dst.h = dst.h - dst_slice.t - dst_slice.b; + tile_region(&verts, curr_src, curr_dst, size.x, size.y, center_x,center_y); + + JSValue mesh = quads_to_mesh(js, verts); + arrfree(verts); + ret = mesh; +) + +JSC_CCALL(gpu_tile, + HMM_Vec2 size; + JSValue jstex = argv[0]; + JS_GETATOM(js,size.x,jstex,width,number) + JS_GETATOM(js, size.y, jstex, height, number) + + rect src_pixels = js2rect(js, argv[1]); // 'src' as pixel dimensions + rect dst = js2rect(js, argv[2]); // 'dst' as screen coords + + int tilex, tiley; + JSValue jstile = argv[3]; + JS_GETPROP(js,tilex,jstile,repeat_x,bool) + JS_GETPROP(js,tiley,jstile,repeat_y,bool) + + text_vert *verts = NULL; + tile_region(&verts, src_pixels, dst, size.x, size.y,tilex,tiley); + ret = quads_to_mesh(js,verts); + arrfree(verts); +) + +static const JSCFunctionListEntry js_geometry_funcs[] = { + MIST_FUNC_DEF(geometry, rect_intersection, 2), + MIST_FUNC_DEF(geometry, rect_intersects, 2), + MIST_FUNC_DEF(geometry, rect_expand, 2), + MIST_FUNC_DEF(geometry, rect_inside, 2), + MIST_FUNC_DEF(geometry, rect_random, 1), + MIST_FUNC_DEF(geometry, cwh2rect, 2), + MIST_FUNC_DEF(geometry, rect_point_inside, 2), + MIST_FUNC_DEF(geometry, rect_pos, 1), + MIST_FUNC_DEF(geometry, rect_move, 2), + MIST_FUNC_DEF(gpu, tile, 4), + MIST_FUNC_DEF(gpu, slice9, 3), +}; + +JSValue js_geometry_use(JSContext *js) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,js_geometry_funcs,countof(js_geometry_funcs)); + return mod; +} diff --git a/source/qjs_geometry.h b/source/qjs_geometry.h new file mode 100644 index 00000000..38cc99d9 --- /dev/null +++ b/source/qjs_geometry.h @@ -0,0 +1,8 @@ +#ifndef QJS_GEOMETRY_H +#define QJS_GEOMETRY_H + +#include "quickjs.h" + +JSValue js_geometry_use(JSContext *ctx); + +#endif /* QJS_GEOMETRY_H */ diff --git a/source/qjs_imgui.cpp b/source/qjs_imgui.cpp index f84e9adb..2730e146 100644 --- a/source/qjs_imgui.cpp +++ b/source/qjs_imgui.cpp @@ -22,15 +22,6 @@ SDL_Window *js2SDL_Window(JSContext *js, JSValue v); static int START = 0; -static inline int js_arrlen(JSContext *js, JSValue v) -{ - JSValue len = JS_GetPropertyStr(js, v, "length"); - int intlen; - JS_ToInt32(js, &intlen, len); - JS_FreeValue(js, len); - return intlen; -} - static inline double js2number(JSContext *js, JSValue v) { double n; @@ -176,12 +167,12 @@ void fill_plotdata(JSContext *js, JSValue v, JSValue last) /* arrsetlen(plotdata, 0); if (JS_IsArray(js,js_getpropidx(v, 0))) { - for (int i = 0; i < js_arrlen(js, v); i++) + for (int i = 0; i < JS_ArrayLength(js, v); i++) arrput(plotdata, js2vec2(js, js_getpropidx(v, i))); } else { // Fill it with the x axis being the array index - for (int i = 0; i < js_arrlen(js, v); i++) { + for (int i = 0; i < JS_ArrayLength(js, v); i++) { if (JS_IsUndefined(js_getpropidx(v,i))) continue; ImVec2 c; c.x = i; @@ -205,13 +196,13 @@ PLOT_FN(digitalplot, PlotDigital,,0) JSC_SCALL(imgui_barplot, fill_plotdata(js, argv[1], JS_UNDEFINED); - ImPlot::PlotBars(str, &plotdata[0].x, &plotdata[0].y, js_arrlen(js, argv[1]), js2number(js, argv[2]), 0, 0, sizeof(ImVec2)); + ImPlot::PlotBars(str, &plotdata[0].x, &plotdata[0].y, JS_ArrayLength(js, argv[1]), js2number(js, argv[2]), 0, 0, sizeof(ImVec2)); ) JSC_SCALL(imgui_histogramplot, size_t offset, len, per_e; JSValue typed = JS_GetTypedArrayBuffer(js, argv[1], &offset, &len, &per_e); - ImPlot::PlotHistogram(str, JS_GetArrayBuffer(js, NULL, typed), js_arrlen(js, argv[1])); + ImPlot::PlotHistogram(str, JS_GetArrayBuffer(js, NULL, typed), JS_ArrayLength(js, argv[1])); JS_FreeValue(js, typed); ) @@ -290,7 +281,7 @@ JSC_SCALL(imgui_slider, float high = JS_IsUndefined(argv[3]) ? 1.0 : js2number(js, argv[3]); if (JS_IsArray(js, argv[1])) { - int n = js_arrlen(js, argv[1]); + int n = JS_ArrayLength(js, argv[1]); float a[n]; js2floatarr(argv[1], n, a); @@ -394,7 +385,7 @@ JSC_SCALL(imgui_tab, JSC_SCALL(imgui_listbox, /* char **arr = js2strarr(argv[1]); - int n = js_arrlen(js, argv[1]); + int n = JS_ArrayLength(js, argv[1]); int idx = js2number(js, argv[2]); ImGui::ListBox(str, &idx, arr, n, 4); for (int i = 0; i < n; i++) @@ -495,7 +486,7 @@ JSC_SCALL(imgui_dndtarget, ) JSC_SCALL(imgui_color, -/* int n = js_arrlen(js, argv[1]); +/* int n = JS_ArrayLength(js, argv[1]); float color[n]; js2floatarr(argv[1],n,color); diff --git a/source/qjs_io.c b/source/qjs_io.c new file mode 100644 index 00000000..25cc42b2 --- /dev/null +++ b/source/qjs_io.c @@ -0,0 +1,382 @@ +#include "qjs_io.h" +#include "jsffi.h" +#include "qjs_macros.h" + +#include +#include +#include +#include "wildmatch.h" + +// Forward declaration for external function +extern void prosperon_mount_core(void); + +// Helper function for array length using QuickJS +// JS_ArrayLength removed - use JS_ArrayLength directly + +// PHYSFS_File free function +static void PHYSFS_File_free(JSRuntime *rt, PHYSFS_File *f) +{ + PHYSFS_close(f); +} + +// Class definition for PHYSFS_File +QJSCLASS(PHYSFS_File,) + +// Helper function for writing to PHYSFS +static size_t js_physfs_write(JSContext *js, PHYSFS_File *f, JSValue val) +{ + size_t len; + size_t wrote; + if (JS_IsString(val)) { + const char *data = JS_ToCStringLen(js,&len,val); + wrote = PHYSFS_writeBytes(f,data,len); + JS_FreeCString(js,data); + } else { + unsigned char *data = JS_GetArrayBuffer(js,&len,val); + wrote = PHYSFS_writeBytes(f,data,len); + } + + if (wrote < len) wrote = -1; + return wrote; +} + +// Glob data structure for filesystem operations +struct globdata { + JSContext *js; + JSValue arr; + char **globs; + char *glob; + int idx; + int recurse; +}; + +// Callback for globfs +static int globfs_cb(struct globdata *data, char *dir, char *file) +{ + int needfree = 0; + char *path; + if (dir[0] == 0) path = file; + else { + path = malloc(strlen(dir) + strlen(file) + 2); + path[0] = 0; + strcat(path,dir); + strcat(path,"/"); + strcat(path,file); + needfree = 1; + } + + char **glob = data->globs; + + while (*glob != NULL) { + if (wildmatch(*glob, path, WM_WILDSTAR) == WM_MATCH) + goto END; + glob++; + } + + PHYSFS_Stat stat; + PHYSFS_stat(path, &stat); + if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY) { + PHYSFS_enumerate(path, globfs_cb, data); + goto END; + } + + JS_SetPropertyUint32(data->js, data->arr, data->idx++, JS_NewString(data->js,path)); + + END: + if (needfree) free(path); + return 1; +} + +// Callback for enumerate +static int enumerate_cb(void *udata, const char *dir, const char *fname) +{ + struct globdata *data = (struct globdata*)udata; + char *path; + int needfree = 0; + + if (dir[0] == 0) path = (char*)fname; + else { + size_t dlen = strlen(dir); + int ends_slash = (dlen && dir[dlen - 1] == '/'); + path = malloc(dlen + strlen(fname) + (ends_slash ? 1 : 2)); + if (ends_slash) sprintf(path, "%s%s", dir, fname); else sprintf(path, "%s/%s", dir, fname); + needfree = 1; + } + + PHYSFS_Stat st; + if (!PHYSFS_stat(path, &st)) { + if (needfree) free(path); + return 1; + } + + /* If it's a directory and we're recursing, enumerate it further. */ + if (st.filetype == PHYSFS_FILETYPE_DIRECTORY && data->recurse) + PHYSFS_enumerate(path, enumerate_cb, data); + + /* Add this item (file or directory) to the JS array. */ + JS_SetPropertyUint32(data->js, data->arr, data->idx++, JS_NewString(data->js, path)); + + if (needfree) free(path); + return 1; /* continue enumerating */ +} + +// I/O FUNCTIONS + +JSC_SCALL(io_rm, + if (!PHYSFS_delete(str)) ret = JS_ThrowReferenceError(js,"could not remove %s. %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); +) + +JSC_SCALL(io_mkdir, + if (!PHYSFS_mkdir(str)) ret = JS_ThrowReferenceError(js,"could not make directory %s. %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); +) + +JSC_SCALL(io_exists, ret = JS_NewBool(js,PHYSFS_exists(str)); ) + +JSC_SCALL(io_stat, + PHYSFS_Stat stat; + if (!PHYSFS_stat(str, &stat)) + return JS_ThrowReferenceError(js, "%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + + ret = JS_NewObject(js); + JS_SetPropertyStr(js,ret,"filesize", number2js(js,stat.filesize)); + JS_SetPropertyStr(js,ret,"modtime", number2js(js,stat.modtime)); + JS_SetPropertyStr(js,ret,"createtime", number2js(js,stat.createtime)); + JS_SetPropertyStr(js,ret,"accesstime", number2js(js,stat.accesstime)); +) + +JSC_SCALL(io_slurpbytes, + PHYSFS_File *f = PHYSFS_openRead(str); + if (!f) { + ret = JS_ThrowReferenceError(js,"physfs error when slurping %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + goto END; + } + PHYSFS_Stat stat; + PHYSFS_stat(str,&stat); + void *data = malloc(stat.filesize); + PHYSFS_readBytes(f,data,stat.filesize); + PHYSFS_close(f); + ret = JS_NewArrayBufferCopy(js,data,stat.filesize); + + END: +) + +JSC_SCALL(io_slurp, + PHYSFS_File *f = PHYSFS_openRead(str); + if (!f) { + ret = JS_ThrowReferenceError(js,"physfs error when slurping %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + goto END; + } + PHYSFS_Stat stat; + PHYSFS_stat(str,&stat); + void *data = malloc(stat.filesize); + PHYSFS_readBytes(f,data,stat.filesize); + PHYSFS_close(f); + ret = JS_NewStringLen(js,data, stat.filesize); + + free(data); + + END: +) + +JSC_SCALL(io_slurpwrite, + PHYSFS_File *f = PHYSFS_openWrite(str); + if (!f) { + ret = JS_ThrowReferenceError(js,"could not write to %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + goto END; + } + size_t wrote = js_physfs_write(js,f,argv[1]); + + PHYSFS_close(f); + if (wrote == -1) + ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + + END: +) + +JSC_SSCALL(io_mount, + if (!PHYSFS_mount(str,str2,0)) ret = JS_ThrowReferenceError(js,"Unable to mount %s at %s: %s", str, str2, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); +) + +JSC_SCALL(io_unmount, + if (!PHYSFS_unmount(str)) ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); +) + +JSC_SCALL(io_writepath, + if (!PHYSFS_setWriteDir(str)) ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); +) + +JSC_SSCALL(io_match, + if (wildmatch(str, str2, WM_PATHNAME | WM_PERIOD | WM_WILDSTAR) == WM_MATCH) + ret = JS_NewBool(js,1); + else + ret = JS_NewBool(js,0); +) + +JSC_CCALL(io_globfs, + ret = JS_NewArray(js); + struct globdata data; + data.js = js; + data.arr = ret; + data.idx = 0; + int globs_len = JS_ArrayLength(js,argv[0]); + const char *globs[globs_len+1]; + for (int i = 0; i < globs_len; i++) { + JSValue g = JS_GetPropertyUint32(js,argv[0],i); + globs[i] = JS_ToCString(js,g); + JS_FreeValue(js,g); + } + + globs[globs_len] = NULL; + data.globs = globs; + + const char *path = NULL; + if (!JS_IsUndefined(argv[1])) path = JS_ToCString(js,argv[1]); + PHYSFS_enumerate(path, globfs_cb, &data); + + for (int i = 0; i < globs_len; i++) + JS_FreeCString(js,globs[i]); + + ret = data.arr; + JS_FreeCString(js,path); +) + +JSC_SCALL(io_enumerate, + /* First argument: str (directory name) */ + /* Second argument: boolean => recurse or not */ + ret = JS_NewArray(js); + + struct globdata data; + data.js = js; + data.arr = ret; + data.idx = 0; + data.glob = NULL; /* not used here */ + data.globs = NULL; /* not used here */ + data.recurse = 0; + + if (!JS_IsUndefined(argv[1])) /* parse second arg if provided */ + data.recurse = JS_ToBool(js, argv[1]); + + /* Enumerate the directory given by 'str'. */ + PHYSFS_enumerate(str, enumerate_cb, &data); + + /* Return the JS array we filled. */ + ret = data.arr; +) + +JSC_CCALL(io_basedir, return JS_NewString(js,PHYSFS_getBaseDir())) + +JSC_SSCALL(io_prefdir, return JS_NewString(js,PHYSFS_getPrefDir(str, str2))) + +JSC_SCALL(io_open, + PHYSFS_File *f = PHYSFS_openWrite(str); + if (!f) + ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + else + ret = PHYSFS_File2js(js,f); +) + +JSC_SCALL(io_realdir, + const char *real = PHYSFS_getRealDir(str); + if (!real) + ret = JS_UNDEFINED; + else + ret = JS_NewString(js,real); +) + +JSC_CCALL(io_searchpath, + ret = JS_NewArray(js); + char **paths = PHYSFS_getSearchPath(); + for (int i = 0; paths[i] != NULL; i++) + JS_SetPropertyUint32(js,ret,i,JS_NewString(js,paths[i])); +) + +JSC_CCALL(io_mount_core, + int mount = JS_ToBool(js,argv[0]); + if (!mount) + PHYSFS_unmount("core.zip"); + else + prosperon_mount_core(); +) + +JSC_SCALL(io_is_directory, + PHYSFS_Stat stat; + int good = PHYSFS_stat(str, &stat); + if (!good) + ret = JS_NewBool(js, 0); + else + ret = JS_NewBool(js, stat.filetype == PHYSFS_FILETYPE_DIRECTORY); +) + +// FILE FUNCTIONS + +JSC_CCALL(file_close, + PHYSFS_File *f = js2PHYSFS_File(js,self); + PHYSFS_close(f); +) + +JSC_CCALL(file_write, + PHYSFS_File *f = js2PHYSFS_File(js,self); + size_t wrote = js_physfs_write(js,f,argv[0]); + if (wrote == -1) + return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); +) + +JSC_CCALL(file_buffer, + PHYSFS_File *f = js2PHYSFS_File(js,self); + size_t size = js2number(js,argv[0]); + if (!PHYSFS_setBuffer(f,size)) + return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); +) + +JSC_CCALL(file_tell, + PHYSFS_File *f = js2PHYSFS_File(js,self); + size_t tell = PHYSFS_tell(f); + if (tell == -1) + return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + + return number2js(js,tell); +) + +JSC_CCALL(file_eof, + PHYSFS_File *f = js2PHYSFS_File(js,self); + return JS_NewBool(js, PHYSFS_eof(f)); +) + +static const JSCFunctionListEntry js_io_funcs[] = { + MIST_FUNC_DEF(io, rm, 1), + MIST_FUNC_DEF(io, mkdir, 1), + MIST_FUNC_DEF(io,stat,1), + MIST_FUNC_DEF(io, globfs, 2), + MIST_FUNC_DEF(io, match, 2), + MIST_FUNC_DEF(io, exists, 1), + MIST_FUNC_DEF(io, mount, 2), + MIST_FUNC_DEF(io,unmount,1), + MIST_FUNC_DEF(io,slurp,1), + MIST_FUNC_DEF(io,slurpbytes,1), + MIST_FUNC_DEF(io,slurpwrite,2), + MIST_FUNC_DEF(io,writepath, 1), + MIST_FUNC_DEF(io,basedir, 0), + MIST_FUNC_DEF(io, prefdir, 2), + MIST_FUNC_DEF(io, realdir, 1), + MIST_FUNC_DEF(io, open, 1), + MIST_FUNC_DEF(io, searchpath, 0), + MIST_FUNC_DEF(io, enumerate, 2), + MIST_FUNC_DEF(io, mount_core, 1), + MIST_FUNC_DEF(io, is_directory, 1), +}; + +static const JSCFunctionListEntry js_PHYSFS_File_funcs[] = { + MIST_FUNC_DEF(file, close, 0), + MIST_FUNC_DEF(file, write, 1), + MIST_FUNC_DEF(file, buffer, 1), + MIST_FUNC_DEF(file, tell, 0), + MIST_FUNC_DEF(file, eof, 0), +}; + +JSValue js_io_use(JSContext *js) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,js_io_funcs,countof(js_io_funcs)); + return mod; +} + +// Note: PHYSFS_File class functions are registered via QJSCLASSPREP_FUNCS(PHYSFS_File) in main FFI loading \ No newline at end of file diff --git a/source/qjs_io.h b/source/qjs_io.h new file mode 100644 index 00000000..70798882 --- /dev/null +++ b/source/qjs_io.h @@ -0,0 +1,8 @@ +#ifndef QJS_IO_H +#define QJS_IO_H + +#include "quickjs.h" + +JSValue js_io_use(JSContext *ctx); + +#endif /* QJS_IO_H */ diff --git a/source/qjs_macros.h b/source/qjs_macros.h index 24e0d0e9..a56dd958 100644 --- a/source/qjs_macros.h +++ b/source/qjs_macros.h @@ -21,6 +21,12 @@ return ret; \ } +#define JSC_CCALL_EXTERN(NAME, ...) JSValue js_##NAME (JSContext *js, JSValue self, int argc, JSValue *argv) { \ + JSValue ret = JS_UNDEFINED; \ + __VA_ARGS__ ;\ + return ret; \ +} + #define JSC_SCALL(NAME, ...) JSC_CCALL(NAME, \ const char *str = JS_ToCString(js,argv[0]); \ __VA_ARGS__ ;\ @@ -136,6 +142,29 @@ JSValue TYPE##2js(JSContext *js, TYPE *n) { \ return j; }\ \ +#define QJSCLASSMARK_EXTERN(TYPE, ...)\ +static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\ +TYPE *n = JS_GetOpaque(val, js_##TYPE##_id);\ +TracyCFreeN(n, #TYPE); \ +TYPE##_free(rt,n);}\ +static JSClassDef js_##TYPE##_class = {\ + .class_name = #TYPE,\ + .finalizer = js_##TYPE##_finalizer,\ + .gc_mark = js_##TYPE##_mark,\ +};\ +extern JSClassID js_##TYPE##_id;\ +TYPE *js2##TYPE (JSContext *js, JSValue val) { \ + if (JS_GetClassID(val) != js_##TYPE##_id) return NULL; \ + return JS_GetOpaque(val,js_##TYPE##_id); \ +}\ +JSValue TYPE##2js(JSContext *js, TYPE *n) { \ + JSValue j = JS_NewObjectClass(js,js_##TYPE##_id);\ + JS_SetOpaque(j,n);\ + __VA_ARGS__ \ + TracyCAllocN(n, 1, #TYPE); \ + return j; }\ +\ + #define QJSGLOBALCLASS(NAME) \ JSValue NAME = JS_NewObject(js); \ JS_SetPropertyFunctionList(js, NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs)); \ @@ -153,11 +182,6 @@ JSValue TYPE##_proto = JS_NewObject(js); \ JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \ JS_SetPropertyStr(js, c_types, #TYPE, JS_DupValue(js,TYPE##_proto)); \ -#define MISTUSE(NAME) \ -JSValue js_##NAME##_use(JSContext *js) { \ - JSValue mod = JS_NewObject(js); \ - JS_SetPropertyFunctionList(js,mod,js_##NAME##_funcs,countof(js_##NAME##_funcs)); \ - return mod; } \ #define countof(x) (sizeof(x)/sizeof((x)[0])) diff --git a/source/qjs_math.c b/source/qjs_math.c new file mode 100644 index 00000000..b0920ce2 --- /dev/null +++ b/source/qjs_math.c @@ -0,0 +1,509 @@ +#include "qjs_math.h" +#include "jsffi.h" +#include "qjs_macros.h" + +#include +#include +#include "HandmadeMath.h" +#include "prosperon.h" + + +// Utility function to get number from array index +static double js_getnum_uint32(JSContext *js, JSValue v, unsigned int i) +{ + JSValue val = JS_GetPropertyUint32(js,v,i); + double ret = js2number(js, val); + JS_FreeValue(js,val); + return ret; +} + +// Convert JS array to float array +static float *js2floats(JSContext *js, JSValue v, size_t *len) +{ + *len = JS_ArrayLength(js,v); + float *arr = malloc(sizeof(float)* *len); + for (int i = 0; i < *len; i++) + arr[i] = js_getnum_uint32(js,v,i); + return arr; +} + +// Convert float array to JS array +static JSValue floats2array(JSContext *js, float *vals, size_t len) { + JSValue arr = JS_NewArray(js); + for (size_t i = 0; i < len; i++) { + JS_SetPropertyUint32(js, arr, i, number2js(js, vals[i])); + } + return arr; +} + +// Calculate vector length +static double arr_vec_length(JSContext *js,JSValue v) +{ + int len = JS_ArrayLength(js,v); + switch(len) { + case 2: return HMM_LenV2(js2vec2(js,v)); + case 3: return HMM_LenV3(js2vec3(js,v)); + case 4: return HMM_LenV4(js2vec4(js,v)); + } + + double sum = 0; + for (int i = 0; i < len; i++) + sum += pow(js_getnum_uint32(js, v, i), 2); + + return sqrt(sum); +} + + +// GCD helper function +static int gcd(int a, int b) { + if (b == 0) + return a; + return gcd(b, a % b); +} + +// MATH FUNCTIONS + + + +JSC_CCALL(math_rotate, + HMM_Vec2 vec = js2vec2(js,argv[0]); + double angle = js2angle(js, argv[1]); + HMM_Vec2 pivot = JS_IsUndefined(argv[2]) ? v2zero : js2vec2(js,argv[2]); + vec = HMM_SubV2(vec,pivot); + + float r = HMM_LenV2(vec); + angle += atan2f(vec.y, vec.x); + + vec.x = r * cosf(angle); + vec.y = r * sinf(angle); + + vec = HMM_AddV2(vec,pivot); + + return vec22js(js, vec); +) + +JSC_CCALL(math_norm, + int len = JS_ArrayLength(js,argv[0]); + + switch(len) { + case 2: return vec22js(js,HMM_NormV2(js2vec2(js,argv[0]))); + case 3: return vec32js(js, HMM_NormV3(js2vec3(js,argv[0]))); + case 4: return vec42js(js,HMM_NormV4(js2vec4(js,argv[0]))); + } + + double length = arr_vec_length(js,argv[0]); + JSValue newarr = JS_NewArray(js); + + for (int i = 0; i < len; i++) + JS_SetPropertyUint32(js, newarr, i, number2js(js,js_getnum_uint32(js, argv[0],i)/length)); + + ret = newarr; +) + +JSC_CCALL(math_angle_between, + int len = JS_ArrayLength(js,argv[0]); + switch(len) { + case 2: return angle2js(js,HMM_AngleV2(js2vec2(js,argv[0]), js2vec2(js,argv[1]))); + case 3: return angle2js(js,HMM_AngleV3(js2vec3(js,argv[0]), js2vec3(js,argv[1]))); + case 4: return angle2js(js,HMM_AngleV4(js2vec4(js,argv[0]), js2vec4(js,argv[1]))); + } + return JS_ThrowReferenceError(js, "Input array must have a length between 2 and 4."); +) + +JSC_CCALL(math_lerp, + double s = js2number(js,argv[0]); + double f = js2number(js,argv[1]); + double t = js2number(js,argv[2]); + + ret = number2js(js,(f-s)*t+s); +) + +JSC_CCALL(math_gcd, ret = number2js(js,gcd(js2number(js,argv[0]), js2number(js,argv[1]))); ) + +JSC_CCALL(math_lcm, + double a = js2number(js,argv[0]); + double b = js2number(js,argv[1]); + ret = number2js(js,(a*b)/gcd(a,b)); +) + +JSC_CCALL(math_clamp, + double x = js2number(js,argv[0]); + double l = js2number(js,argv[1]); + double h = js2number(js,argv[2]); + return number2js(js,x > h ? h : x < l ? l : x); +) + +JSC_CCALL(math_angledist, + double a1 = js2number(js,argv[0]); + double a2 = js2number(js,argv[1]); + a1 = fmod(a1,1); + a2 = fmod(a2,1); + double dist = a2-a1; + if (dist == 0) return number2js(js,dist); + if (dist > 0) { + if (dist > 0.5) return number2js(js,dist-1); + return number2js(js,dist); + } + + if (dist < -0.5) return number2js(js,dist+1); + + return number2js(js,dist); +) + +JSC_CCALL(math_jitter, + double n = js2number(js,argv[0]); + double pct = js2number(js,argv[1]); + + return number2js(js,n + (rand_range(js,-pct,pct)*n)); +) + +JSC_CCALL(math_mean, + double len = JS_ArrayLength(js,argv[0]); + double sum = 0; + for (int i = 0; i < len; i++) + sum += js_getnum_uint32(js, argv[0], i); + + return number2js(js,sum/len); +) + +JSC_CCALL(math_sum, + double sum = 0.0; + int len = JS_ArrayLength(js,argv[0]); + for (int i = 0; i < len; i++) + sum += js_getnum_uint32(js, argv[0], i); + + return number2js(js,sum); +) + +JSC_CCALL(math_sigma, + int len = JS_ArrayLength(js,argv[0]); + double sum = 0; + for (int i = 0; i < len; i++) + sum += js_getnum_uint32(js, argv[0], i); + + double mean = sum/(double)len; + sum = 0; + for (int i = 0; i < len; i++) + sum += pow(js_getnum_uint32(js, argv[0], i) - mean, 2); + + return number2js(js,sqrt(sum/len)); +) + +JSC_CCALL(math_median, + int len = JS_ArrayLength(js,argv[0]); + double vals[len]; + for (int i = 0; i < len; i++) + vals[i] = js_getnum_uint32(js, argv[0], i); + + // Simple bubble sort for median calculation + for (int i = 0; i < len-1; i++) { + for (int j = 0; j < len-i-1; j++) { + if (vals[j] > vals[j+1]) { + double temp = vals[j]; + vals[j] = vals[j+1]; + vals[j+1] = temp; + } + } + } + + if (len % 2 == 0) + return number2js(js,(vals[len/2-1] + vals[len/2]) / 2.0); + else + return number2js(js,vals[len/2]); +) + +JSC_CCALL(math_length, return number2js(js,arr_vec_length(js,argv[0])); ) + +JSC_CCALL(math_from_to, + double start = js2number(js,argv[0]); + double end = js2number(js,argv[1]); + double step = js2number(js,argv[2]); + int inclusive = JS_ToBool(js,argv[3]); + int arr = JS_ToBool(js,argv[4]); + + JSValue jsarr = JS_NewArray(js); + int i = 0; + for (double val = start; val <= end; val += step) { + if (val == end && !inclusive) break; + JS_SetPropertyUint32(js, jsarr, i++, number2js(js, val)); + } + + return jsarr; +) + +JSC_CCALL(math_rand, + MTRand *mrand = &((prosperon_rt*)JS_GetContextOpaque(js))->mrand; + return number2js(js, genRand(mrand)); +) + +JSC_CCALL(math_randi, + MTRand *mrand = &((prosperon_rt*)JS_GetContextOpaque(js))->mrand; + return number2js(js, genRandLong(mrand)); +) + +JSC_CCALL(math_srand, + MTRand *mrand = &((prosperon_rt*)JS_GetContextOpaque(js))->mrand; + m_seedRand(mrand, js2number(js,argv[0])); +) + +JSC_CCALL(math_dot, + size_t alen, blen; + float *a = js2floats(js,argv[0], &alen); + float *b = js2floats(js,argv[1], &blen); + float dot = 0; + size_t len = alen < blen? alen : blen; + for (size_t i = 0; i < len; i++) + dot += a[i] * b[i]; + + free(a); + free(b); + return number2js(js,dot); +) + +JSC_CCALL(math_project, + size_t alen, blen; + float *a = js2floats(js, argv[0], &alen); + float *b = js2floats(js, argv[1], &blen); + + if (!a || !b) { + free(a); + free(b); + return JS_UNDEFINED; + } + + // We'll work up to the smaller length + size_t len = (alen < blen) ? alen : blen; + if (len == 0) { + free(a); + free(b); + return JS_UNDEFINED; + } + + // Compute dot products: a·b and b·b + float ab = 0, bb = 0; + for (size_t i = 0; i < len; i++) { + ab += a[i] * b[i]; + bb += b[i] * b[i]; + } + + // Build the result array + float *proj = (float*)malloc(sizeof(float) * len); + if (!proj) { + free(a); + free(b); + return JS_EXCEPTION; // or some error + } + + float scale = (bb != 0.0f) ? (ab / bb) : 0.0f; + for (size_t i = 0; i < len; i++) + proj[i] = scale * b[i]; + + ret = floats2array(js, proj, len); + + free(a); + free(b); + free(proj); +) + +JSC_CCALL(math_midpoint, + size_t alen, blen; + float *a = js2floats(js, argv[0], &alen); + float *b = js2floats(js, argv[1], &blen); + + if (!a || !b) { + free(a); + free(b); + return JS_UNDEFINED; + } + + size_t len = (alen < blen) ? alen : blen; + if (len == 0) { + free(a); + free(b); + return JS_UNDEFINED; + } + + float *m = (float*)malloc(sizeof(float) * len); + if (!m) { + free(a); + free(b); + return JS_EXCEPTION; + } + + for (size_t i = 0; i < len; i++) + m[i] = (a[i] + b[i]) * 0.5f; + + ret = floats2array(js, m, len); + + free(a); + free(b); + free(m); +) + +JSC_CCALL(math_reflect, + size_t alen, blen; + float *a = js2floats(js, argv[0], &alen); + float *b = js2floats(js, argv[1], &blen); + + if (!a || !b) { + free(a); + free(b); + return JS_UNDEFINED; + } + + size_t len = (alen < blen) ? alen : blen; + if (len == 0) { + free(a); + free(b); + return JS_UNDEFINED; + } + + // Reflect vector a across normal b + // reflection = a - 2 * (a·b) * b + float ab = 0, bb = 0; + for (size_t i = 0; i < len; i++) { + ab += a[i] * b[i]; + bb += b[i] * b[i]; + } + + float *result = (float*)malloc(sizeof(float) * len); + if (!result) { + free(a); + free(b); + return JS_EXCEPTION; + } + + float scale = (bb != 0.0f) ? (2.0f * ab / bb) : 0.0f; + for (size_t i = 0; i < len; i++) + result[i] = a[i] - scale * b[i]; + + ret = floats2array(js, result, len); + + free(a); + free(b); + free(result); +) + +JSC_CCALL(math_direction, + size_t alen, blen; + float *a = js2floats(js, argv[0], &alen); + float *b = js2floats(js, argv[1], &blen); + + if (!a || !b) { + free(a); + free(b); + return JS_UNDEFINED; + } + + size_t len = (alen < blen) ? alen : blen; + if (len == 0) { + free(a); + free(b); + return JS_UNDEFINED; + } + + // Direction vector from a to b (normalized) + float *dir = (float*)malloc(sizeof(float) * len); + if (!dir) { + free(a); + free(b); + return JS_EXCEPTION; + } + + float mag = 0; + for (size_t i = 0; i < len; i++) { + dir[i] = b[i] - a[i]; + mag += dir[i] * dir[i]; + } + + mag = sqrtf(mag); + if (mag > 0.0f) { + for (size_t i = 0; i < len; i++) + dir[i] /= mag; + } + + ret = floats2array(js, dir, len); + + free(a); + free(b); + free(dir); +) + +JSC_CCALL(math_angle, + size_t len; + float *v = js2floats(js, argv[0], &len); + + if (!v || len < 2) { + free(v); + return JS_UNDEFINED; + } + + // Return angle in radians for 2D vector + ret = number2js(js, atan2f(v[1], v[0])); + + free(v); +) + +JSC_CCALL(math_distance, + size_t alen, blen; + float *a = js2floats(js, argv[0], &alen); + float *b = js2floats(js, argv[1], &blen); + + if (!a || !b) { + free(a); + free(b); + return JS_UNDEFINED; + } + + size_t len = (alen < blen) ? alen : blen; + if (len == 0) { + free(a); + free(b); + return JS_UNDEFINED; + } + + float distSq = 0.0f; + for (size_t i = 0; i < len; i++) { + float diff = b[i] - a[i]; + distSq += diff * diff; + } + float dist = sqrtf(distSq); + + free(a); + free(b); + return number2js(js, dist); +) + +static const JSCFunctionListEntry js_math_funcs[] = { + MIST_FUNC_DEF(math, dot,2), + MIST_FUNC_DEF(math, project,2), + MIST_FUNC_DEF(math, rotate, 3), + MIST_FUNC_DEF(math, midpoint, 2), + MIST_FUNC_DEF(math, reflect, 2), + MIST_FUNC_DEF(math, distance, 2), + MIST_FUNC_DEF(math, direction, 2), + MIST_FUNC_DEF(math, angle, 1), + MIST_FUNC_DEF(math, norm, 1), + MIST_FUNC_DEF(math, angle_between, 2), + MIST_FUNC_DEF(math, lerp, 3), + MIST_FUNC_DEF(math, gcd, 2), + MIST_FUNC_DEF(math, lcm, 2), + MIST_FUNC_DEF(math, clamp, 3), + MIST_FUNC_DEF(math, angledist, 2), + MIST_FUNC_DEF(math, jitter, 2), + MIST_FUNC_DEF(math, mean, 1), + MIST_FUNC_DEF(math, sum, 1), + MIST_FUNC_DEF(math, sigma, 1), + MIST_FUNC_DEF(math, median, 1), + MIST_FUNC_DEF(math, length, 1), + MIST_FUNC_DEF(math, from_to, 5), + MIST_FUNC_DEF(math, rand, 0), + MIST_FUNC_DEF(math, randi, 0), + MIST_FUNC_DEF(math, srand,0), +}; + +JSValue js_math_use(JSContext *js) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,js_math_funcs,countof(js_math_funcs)); + return mod; +} diff --git a/source/qjs_math.h b/source/qjs_math.h new file mode 100644 index 00000000..46140bc7 --- /dev/null +++ b/source/qjs_math.h @@ -0,0 +1,8 @@ +#ifndef QJS_MATH_H +#define QJS_MATH_H + +#include "quickjs.h" + +JSValue js_math_use(JSContext *ctx); + +#endif /* QJS_MATH_H */ diff --git a/source/qjs_os.c b/source/qjs_os.c new file mode 100644 index 00000000..8029e229 --- /dev/null +++ b/source/qjs_os.c @@ -0,0 +1,489 @@ +#include "qjs_os.h" +#include "jsffi.h" +#include "qjs_macros.h" +#include "qjs_wota.h" +#include "prosperon.h" +#include "transform.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#ifdef __linux__ +#include +#endif +#endif + +// External variables +extern int trace; +extern prosperon_rt *io_actor; + +// External function declarations +extern JSClassID js_transform_id; +JSValue transform2js(JSContext *js, transform *t); +transform *js2transform(JSContext *js, JSValue v); + +// OS FUNCTIONS + +JSC_SCALL(os_openurl, + if (SDL_OpenURL(str) < 0) + ret = JS_ThrowInternalError(js, "%s", SDL_GetError()); +) + +JSC_SCALL(os_env, + char *env = getenv(str); + if (env) ret = JS_NewString(js,env); +) + +JSC_CCALL(os_exit, exit(js2number(js,argv[0]));) +JSC_CCALL(os_now, return number2js(js, (double)SDL_GetTicksNS()/1000000000.0)) + + +JSC_SCALL(os_system, ret = number2js(js,system(str)); ) + +JSC_SCALL(os_kill, + pid_t pid = js2number(js,argv[0]); + if (kill(pid, SIGTERM) < 0) + ret = JS_ThrowReferenceError(js,"could not kill process"); +) + +JSC_CCALL(os_sleep, + double secs = js2number(js,argv[0]); + int ms = secs*1000; + SDL_Delay(ms); +) + +JSC_CCALL(os_battery_pct, + int pct; + SDL_PowerState state = SDL_GetPowerInfo(&pct, NULL); + return number2js(js,pct); +) + +JSC_CCALL(os_battery_voltage, + return number2js(js,0); +) + +JSC_CCALL(os_battery_seconds, + int secs; + SDL_PowerState state = SDL_GetPowerInfo(NULL, &secs); + return number2js(js, secs); +) + +JSC_CCALL(os_power_state, + int pct, secs; + SDL_PowerState state = SDL_GetPowerInfo(&pct, &secs); + const char *statestr = "unknown"; + switch(state) { + case SDL_POWERSTATE_ON_BATTERY: statestr = "battery"; break; + case SDL_POWERSTATE_NO_BATTERY: statestr = "no battery"; break; + case SDL_POWERSTATE_CHARGING: statestr = "charging"; break; + case SDL_POWERSTATE_CHARGED: statestr = "charged"; break; + } + ret = JS_NewObject(js); + JS_SetPropertyStr(js,ret,"state",JS_NewString(js,statestr)); + JS_SetPropertyStr(js,ret,"percent",number2js(js,pct)); + JS_SetPropertyStr(js,ret,"seconds",number2js(js,secs)); +) + +JSC_CCALL(os_totalmem, return number2js(js, SDL_GetSystemRAM())) +JSC_CCALL(os_platform, return JS_NewString(js,SDL_GetPlatform())) + +JSC_CCALL(os_hostname, +#ifdef _WIN32 + TCHAR buffer[256] = TEXT(""); + DWORD size = sizeof(buffer) / sizeof(TCHAR); + GetComputerName(buffer, &size); + return JS_NewString(js, buffer); +#else + struct utsname buffer; + if (uname(&buffer) != 0) return JS_ThrowReferenceError(js,"Could not get hostname."); + return JS_NewString(js, buffer.nodename); +#endif +) + +JSC_CCALL(os_make_transform, + transform *t = make_transform(); + ret = transform2js(js,t); +// t->self = JS_DupValue(js,ret); + t->self = ret; +) + +JSC_CCALL(os_clean_transforms, + clean_all(js); +) + +JSC_CCALL(os_arch, +#if defined(__x86_64__) || defined(_M_X64) + return JS_NewString(js,"x64"); +#elif defined(__aarch64__) || defined(_M_ARM64) + return JS_NewString(js,"arm64"); +#elif defined(__arm__) || defined(_M_ARM) + return JS_NewString(js,"arm"); +#elif defined(__i386__) || defined(_M_IX86) + return JS_NewString(js,"ia32"); +#elif defined(__loongarch__) || defined(__loongarch32) || defined(__loongarch64) + return JS_NewString(js,"loong64"); +#elif defined(__mips__) || defined(__mips) || defined(_M_MIPS) + // You might want to distinguish mips vs mipsel + return JS_NewString(js,"mips"); +#elif defined(__ppc64__) || defined(__powerpc64__) || defined(_M_PPC) + // You might want to distinguish ppc vs ppc64, big-endian vs little-endian + return JS_NewString(js,"ppc64"); +#elif defined(__riscv) && __riscv_xlen == 64 + return JS_NewString(js,"riscv64"); +#elif defined(__s390x__) + return JS_NewString(js,"s390x"); +#else + return JS_NewString(js,"unknown"); +#endif +) + +JSC_CCALL(os_freemem, +#ifdef _WIN32 + MEMORYSTATUSEX statex; + statex.dwLength = sizeof(statex); + if (!GlobalMemoryStatusEx(&statex)) return JS_ThrowInternalError(js,"GlobalMemoryStatusEx failed"); + return JS_NewInt64(js,(int64_t)statex.ullAvailPhys); +#elif defined(__linux__) + struct sysinfo info; + if (sysinfo(&info) == 0) + return JS_NewInt64(js,(int64_t)info.freeram * info.mem_unit); + return JS_NewInt64(js,0); +#elif defined(__FreeBSD__) || defined(__OpenBSD__) + // A very rough fallback using the same sysconf approach + // (macOS or *BSD typically need specialized APIs to get free mem accurately) + // This is often only "unused" pages, ignoring caches, etc. + long pages = sysconf(_SC_AVPHYS_PAGES); + long page_size = sysconf(_SC_PAGE_SIZE); + if (pages < 0 || page_size < 0) return JS_NewInt64(js,0); + return JS_NewInt64(js,(int64_t)pages * (int64_t)page_size); +#else + // Fallback: unknown + return JS_NewInt64(js,0); +#endif +) + +JSC_CCALL(os_version, +#ifdef _WIN32 + typedef LONG (WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); + HMODULE h = GetModuleHandleA("ntdll.dll"); + if (h) { + RtlGetVersionPtr fx = (RtlGetVersionPtr)GetProcAddress(h, "RtlGetVersion"); + if (fx) { + RTL_OSVERSIONINFOW ver; + memset(&ver, 0, sizeof(ver)); + ver.dwOSVersionInfoSize = sizeof(ver); + if (!fx(&ver)) { + char buf[128]; + sprintf(buf, "%u.%u.%u", + (unsigned)ver.dwMajorVersion, + (unsigned)ver.dwMinorVersion, + (unsigned)ver.dwBuildNumber + ); + return JS_NewString(js, buf); + } + } + } + OSVERSIONINFOW wver; + memset(&wver, 0, sizeof(wver)); + wver.dwOSVersionInfoSize = sizeof(wver); + if (GetVersionExW(&wver)) { + char buf[128]; + sprintf(buf, "%u.%u.%u", + (unsigned)wver.dwMajorVersion, + (unsigned)wver.dwMinorVersion, + (unsigned)wver.dwBuildNumber + ); + return JS_NewString(js, buf); + } + return JS_NewString(js, "Windows_Unknown"); +#else + struct utsname info; + if (!uname(&info)) return JS_NewString(js, info.release); + return JS_NewString(js, ""); +#endif +) + +JSValue js_os_get_trace(JSContext *js, JSValue self) +{ + return JS_NewBool(js, trace); +} + +JSValue js_os_set_trace(JSContext *js, JSValue self, JSValue value) +{ + trace = JS_ToBool(js, value); + return JS_UNDEFINED; +} + +JSC_CCALL(os_on, + prosperon_rt *rt = JS_GetContextOpaque(js); + JS_FreeValue(js, rt->on_exception); + rt->on_exception = JS_DupValue(js,argv[1]); +) + +#define JSOBJ_ADD_FIELD(OBJ, STRUCT, FIELD, TYPE) \ +JS_SetPropertyStr(js, OBJ, #FIELD, TYPE##2js(js,STRUCT.FIELD));\ + +#define JSJMEMRET(FIELD) JSOBJ_ADD_FIELD(ret, jsmem, FIELD, number) + +JSC_CCALL(os_mallinfo, + ret = JS_UNDEFINED; + /*struct mallinfo jsmem = mallinfo(); + ret = JS_NewObject(js); + JSJMEMRET(arena); + JSJMEMRET(ordblks); + JSJMEMRET(smblks); + JSJMEMRET(hblks); + JSJMEMRET(hblkhd); + JSJMEMRET(usmblks); + JSJMEMRET(fsmblks); + JSJMEMRET(uordblks); + JSJMEMRET(fordblks); + JSJMEMRET(keepcost);*/ +) + +JSC_CCALL(os_rusage, + ret = JS_NewObject(js); + +#ifndef _WIN32 + struct rusage jsmem; + getrusage(RUSAGE_SELF, &jsmem); + JSJMEMRET(ru_maxrss); + JSJMEMRET(ru_ixrss); + JSJMEMRET(ru_idrss); + JSJMEMRET(ru_isrss); + JSJMEMRET(ru_minflt); + JSJMEMRET(ru_majflt); + JSJMEMRET(ru_nswap); + JSJMEMRET(ru_inblock); + JSJMEMRET(ru_oublock); + JSJMEMRET(ru_msgsnd); + JSJMEMRET(ru_msgrcv); + JSJMEMRET(ru_nsignals); + JSJMEMRET(ru_nvcsw); + JSJMEMRET(ru_nivcsw); +#endif + + return ret; +) + +JSC_CCALL(os_createprocess, + int ac = JS_ArrayLength(js,argv[0]); + const char *args[ac+1]; + for (int i = 0; i < ac; i++) { + JSValue astr = JS_GetPropertyUint32(js,argv[0],i); + args[i] = JS_ToCString(js,astr); + JS_FreeValue(js,astr); + } + + args[ac] = NULL; + + SDL_Process *actor = SDL_CreateProcess(args, 0); + + for (int i = 0; i < ac; i++) + JS_FreeCString(js,args[i]); + + if (!actor) + return JS_ThrowReferenceError(js, "Unable to create process: %s\n", SDL_GetError()); +) + +JSC_CCALL(os_createactor, + int margc = JS_ArrayLength(js, argv[0]); + + char **margv = malloc(margc*sizeof(char*)); + + for (int i = 0; i < margc; i++) { + JSValue val = JS_GetPropertyUint32(js, argv[0], i); + const char *cstr = JS_ToCString(js,val); + margv[i] = strdup(cstr); + JS_FreeCString(js,cstr); + JS_FreeValue(js,val); + } + + create_actor(margc, margv); +) + +JSC_CCALL(os_mailbox_push, + if (argc < 2) return JS_ThrowInternalError(js, "Need an actor and a message"); + if (!JS_IsObject(argv[1])) return JS_ThrowInternalError(js, "Object to push must be an object."); + + const char *id = JS_ToCString(js, argv[0]); + int exist = actor_exists(id); + JS_FreeCString(js,id); + if (!exist) + return JS_ThrowInternalError(js, "No mailbox found for given ID"); + + void *data = value2wota(js, argv[1], JS_UNDEFINED); + + const char *err = send_message(id, data); + if (err) { + free(data); + return JS_ThrowInternalError(js, "Could not send message: %s", err); + } +) + +JSC_CCALL(os_register_actor, + prosperon_rt *rt = JS_GetContextOpaque(js); + const char *id = JS_ToCString(js, argv[0]); + const char *err = register_actor(id, rt, JS_ToBool(js, argv[2])); + if (err) return JS_ThrowInternalError(js, "Could not register actor: %s", err); + rt->message_handle = JS_DupValue(js, argv[1]); + rt->context = js; + JS_FreeCString(js, id); +) + +JSC_CCALL(os_mailbox_exist, + const char *id = JS_ToCString(js, argv[0]); + int exist = actor_exists(id); + JS_FreeCString(js, id); + + return JS_NewBool(js, exist); +) + +JSC_CCALL(os_unneeded, + prosperon_rt *actor = JS_GetContextOpaque(js); + SDL_LockMutex(actor->msg_mutex); + JS_FreeValue(js, actor->unneeded); + + if (!JS_IsFunction(js, argv[0])) { + actor->unneeded = JS_UNDEFINED; + goto END; + } + + actor->unneeded = JS_DupValue(js, argv[0]); + JS_ToFloat64(js, &actor->unneeded_secs, argv[1]); + + END: + if (actor->ar) { + SDL_RemoveTimer(actor->ar); + actor->ar = 0; + } + set_actor_state(actor); + SDL_UnlockMutex(actor->msg_mutex); +) + +JSC_CCALL(os_destroy, + prosperon_rt *rt = JS_GetContextOpaque(js); + rt->need_stop = 1; +) + +JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv) { + if (!JS_IsFunction(js, argv[0])) + return JS_ThrowReferenceError(js, "Argument must be a function."); + + prosperon_rt *actor = JS_GetContextOpaque(js); + double seconds; + JS_ToFloat64(js, &seconds, argv[1]); + if (seconds <= 0) { + SDL_LockMutex(actor->msg_mutex); + JSValue cb = JS_DupValue(js, argv[0]); + arrput(actor->events, cb); + SDL_UnlockMutex(actor->msg_mutex); + return JS_NewInt32(js, -1); + } + Uint64 ns = seconds * SDL_NS_PER_SECOND; + + Uint32 id = SDL_AddTimerNS(ns, actor_timer_cb, actor); + + SDL_LockMutex(actor->msg_mutex); + JSValue cb = JS_DupValue(js, argv[0]); + hmput(actor->timers, id, cb); + if (actor->ar) { + SDL_RemoveTimer(actor->ar); + actor->ar = 0; + } + SDL_UnlockMutex(actor->msg_mutex); + + return JS_NewUint32(js, id); +} + +JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *argv) { + prosperon_rt *actor = JS_GetContextOpaque(js); + Uint32 timer_id; + JS_ToUint32(js, &timer_id, argv[0]); + if (timer_id == -1) return JS_UNDEFINED; + + SDL_RemoveTimer(timer_id); + + JSValue cb = JS_UNDEFINED; + + SDL_LockMutex(actor->msg_mutex); + int id = hmgeti(actor->timers, timer_id); + if (id != -1) { + cb = actor->timers[id].value; + hmdel(actor->timers, timer_id); + } + SDL_UnlockMutex(actor->msg_mutex); + + JS_FreeValue(js,cb); + + return JS_UNDEFINED; +} + +JSValue js_os_ioactor(JSContext *js, JSValue self, int argc, JSValue *argv) { + return JS_NewString(js, io_actor->id); +} + +static const JSCFunctionListEntry js_os_funcs[] = { + MIST_FUNC_DEF(os, make_transform, 0), + MIST_FUNC_DEF(os, clean_transforms, 0), + + MIST_FUNC_DEF(os, platform, 0), + MIST_FUNC_DEF(os, arch, 0), + MIST_FUNC_DEF(os, totalmem, 0), + MIST_FUNC_DEF(os, freemem, 0), + MIST_FUNC_DEF(os, hostname, 0), + MIST_FUNC_DEF(os, version, 0), + JS_CGETSET_DEF("trace", js_os_get_trace, js_os_set_trace), + + MIST_FUNC_DEF(os, kill, 1), + MIST_FUNC_DEF(os, exit, 1), + + MIST_FUNC_DEF(os, now, 0), + + MIST_FUNC_DEF(os, openurl, 1), + + MIST_FUNC_DEF(os, sleep, 1), + MIST_FUNC_DEF(os, battery_pct, 0), + MIST_FUNC_DEF(os, battery_voltage, 0), + MIST_FUNC_DEF(os, battery_seconds, 0), + MIST_FUNC_DEF(os, power_state, 0), + + MIST_FUNC_DEF(os, on, 2), + + MIST_FUNC_DEF(os, rusage, 0), + MIST_FUNC_DEF(os, mallinfo, 0), + + // dangerous ones that need disabled for shipping + MIST_FUNC_DEF(os, env, 1), + MIST_FUNC_DEF(os, system, 1), + MIST_FUNC_DEF(os, createprocess, 0), + MIST_FUNC_DEF(os, createactor, 1), + + MIST_FUNC_DEF(os, mailbox_push, 2), + MIST_FUNC_DEF(os, mailbox_exist, 1), + MIST_FUNC_DEF(actor, delay, 2), + MIST_FUNC_DEF(actor, removetimer, 1), + MIST_FUNC_DEF(os, register_actor, 2), + MIST_FUNC_DEF(os, unneeded, 2), + MIST_FUNC_DEF(os, destroy, 0), + MIST_FUNC_DEF(os, ioactor, 0), +}; + +JSValue js_os_use(JSContext *js) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,js_os_funcs,countof(js_os_funcs)); + return mod; +} diff --git a/source/qjs_os.h b/source/qjs_os.h new file mode 100644 index 00000000..3fe67bc1 --- /dev/null +++ b/source/qjs_os.h @@ -0,0 +1,8 @@ +#ifndef QJS_OS_H +#define QJS_OS_H + +#include "quickjs.h" + +JSValue js_os_use(JSContext *ctx); + +#endif /* QJS_OS_H */ diff --git a/source/qjs_renderer.c b/source/qjs_renderer.c index d63e6d06..637cdb69 100644 --- a/source/qjs_renderer.c +++ b/source/qjs_renderer.c @@ -56,7 +56,7 @@ JSC_CCALL(SDL_Renderer_rect, } if (JS_IsArray(js,argv[0])) { - int len = js_arrlen(js,argv[0]); + int len = JS_ArrayLength(js,argv[0]); rect rects[len]; for (int i = 0; i < len; i++) { JSValue val = JS_GetPropertyUint32(js,argv[0],i); @@ -81,8 +81,8 @@ JSC_CCALL(renderer_load_texture, SDL_Texture *tex = SDL_CreateTextureFromSurface(r,surf); if (!tex) return JS_ThrowReferenceError(js, "Could not create texture from surface: %s", SDL_GetError()); ret = SDL_Texture2js(js,tex); - JS_SetProperty(js,ret,width, number2js(js,tex->w)); - JS_SetProperty(js,ret,height, number2js(js,tex->h)); + JS_SetPropertyStr(js,ret,"width", number2js(js,tex->w)); + JS_SetPropertyStr(js,ret,"height", number2js(js,tex->h)); ) JSC_CCALL(SDL_Renderer_fillrect, @@ -93,7 +93,7 @@ JSC_CCALL(SDL_Renderer_fillrect, } if (JS_IsArray(js,argv[0])) { - int len = js_arrlen(js,argv[0]); + int len = JS_ArrayLength(js,argv[0]); rect rects[len]; for (int i = 0; i < len; i++) { JSValue val = JS_GetPropertyUint32(js,argv[0],i); @@ -193,7 +193,7 @@ JSC_CCALL(renderer_line, } if (JS_IsArray(js,argv[0])) { - int len = js_arrlen(js,argv[0]); + int len = JS_ArrayLength(js,argv[0]); HMM_Vec2 points[len]; assert(sizeof(HMM_Vec2) == sizeof(SDL_FPoint)); for (int i = 0; i < len; i++) { @@ -213,7 +213,7 @@ JSC_CCALL(renderer_point, } if (JS_IsArray(js,argv[0])) { - int len = js_arrlen(js,argv[0]); + int len = JS_ArrayLength(js,argv[0]); HMM_Vec2 points[len]; assert(sizeof(HMM_Vec2) ==sizeof(SDL_FPoint)); for (int i = 0; i < len; i++) { @@ -393,7 +393,7 @@ JSC_CCALL(renderer_target, // color: the color this sprite should be hued by JSC_CCALL(renderer_make_sprite_mesh, JSValue sprites = argv[0]; - size_t quads = js_arrlen(js,argv[0]); + size_t quads = JS_ArrayLength(js,argv[0]); size_t verts = quads*4; size_t count = quads*6; @@ -403,10 +403,10 @@ JSC_CCALL(renderer_make_sprite_mesh, for (int i = 0; i < quads; i++) { JSValue sub = JS_GetPropertyUint32(js,sprites,i); - JSValue jstransform = JS_GetProperty(js,sub,transform); + JSValue jstransform = JS_GetPropertyStr(js,sub,"transform"); - JSValue jssrc = JS_GetProperty(js,sub,src); - JSValue jscolor = JS_GetProperty(js,sub,color); + JSValue jssrc = JS_GetPropertyStr(js,sub,"src"); + JSValue jscolor = JS_GetPropertyStr(js,sub,"color"); HMM_Vec4 color; rect src; @@ -441,12 +441,12 @@ JSC_CCALL(renderer_make_sprite_mesh, } ret = JS_NewObject(js); - JS_SetProperty(js, ret, pos, make_gpu_buffer(js, posdata, sizeof(*posdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0)); - JS_SetProperty(js, ret, uv, make_gpu_buffer(js, uvdata, sizeof(*uvdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0)); - JS_SetProperty(js, ret, color, make_gpu_buffer(js, colordata, sizeof(*colordata) * verts, JS_TYPED_ARRAY_FLOAT32, 4, 0,0)); - JS_SetProperty(js, ret, indices, make_quad_indices_buffer(js, quads)); - JS_SetProperty(js, ret, vertices, number2js(js, verts)); - JS_SetProperty(js, ret, count, number2js(js, count)); + JS_SetPropertyStr(js, ret, "pos", make_gpu_buffer(js, posdata, sizeof(*posdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0)); + JS_SetPropertyStr(js, ret, "uv", make_gpu_buffer(js, uvdata, sizeof(*uvdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0)); + JS_SetPropertyStr(js, ret, "color", make_gpu_buffer(js, colordata, sizeof(*colordata) * verts, JS_TYPED_ARRAY_FLOAT32, 4, 0,0)); + JS_SetPropertyStr(js, ret, "indices", make_quad_indices_buffer(js, quads)); + JS_SetPropertyStr(js, ret, "vertices", number2js(js, verts)); + JS_SetPropertyStr(js, ret, "count", number2js(js, count)); ) static const JSCFunctionListEntry js_renderer_funcs[] = { diff --git a/source/qjs_sdl.c b/source/qjs_sdl.c new file mode 100644 index 00000000..5669ea59 --- /dev/null +++ b/source/qjs_sdl.c @@ -0,0 +1,476 @@ +#include "qjs_sdl.h" +#include "jsffi.h" +#include "qjs_macros.h" + +#include + +// SDL Free functions +void SDL_Camera_free(JSRuntime *rt, SDL_Camera *cam) +{ + SDL_CloseCamera(cam); +} + +void SDL_Cursor_free(JSRuntime *rt, SDL_Cursor *c) +{ + SDL_DestroyCursor(c); +} + +void SDL_AudioStream_free(JSRuntime *rt, SDL_AudioStream *st) { + SDL_DestroyAudioStream(st); +} + +// Class definitions for SDL types +QJSCLASS(SDL_Cursor,) +QJSCLASS(SDL_Camera,) +QJSCLASS(SDL_AudioStream,) + +// Internal keymod function for input module +static JSValue js_keymod(JSContext *js) +{ + SDL_Keymod modstate = SDL_GetModState(); + JSValue ret = JS_NewObject(js); + if (SDL_KMOD_CTRL & modstate) + JS_SetPropertyStr(js,ret,"ctrl", JS_NewBool(js,1)); + if (SDL_KMOD_SHIFT & modstate) + JS_SetPropertyStr(js,ret,"shift", JS_NewBool(js,1)); + if (SDL_KMOD_ALT & modstate) + JS_SetPropertyStr(js,ret,"alt", JS_NewBool(js,1)); + if (SDL_KMOD_GUI & modstate) + JS_SetPropertyStr(js,ret,"super", JS_NewBool(js,1)); + if (SDL_KMOD_NUM & modstate) + JS_SetPropertyStr(js,ret,"numlock", JS_NewBool(js,1)); + if (SDL_KMOD_CAPS & modstate) + JS_SetPropertyStr(js,ret,"caps", JS_NewBool(js,1)); + if (SDL_KMOD_SCROLL & modstate) + JS_SetPropertyStr(js,ret,"scrolllock", JS_NewBool(js,1)); + if (SDL_KMOD_MODE & modstate) + JS_SetPropertyStr(js,ret,"mode", JS_NewBool(js,1)); + + return ret; +} + +// INPUT FUNCTIONS +JSC_CCALL(input_mouse_lock, SDL_CaptureMouse(JS_ToBool(js,argv[0]))) + +JSC_CCALL(input_mouse_show, + if (JS_ToBool(js,argv[0])) + SDL_ShowCursor(); + else + SDL_HideCursor(); +) + +JSC_CCALL(input_cursor_set, + SDL_Cursor *c = js2SDL_Cursor(js,argv[0]); + if (!SDL_SetCursor(c)) + return JS_ThrowReferenceError(js, "could not set cursor: %s", SDL_GetError()); +) + +JSC_CCALL(input_keyname, + return JS_NewString(js, SDL_GetKeyName(js2number(js,argv[0]))); +) + +JSC_CCALL(input_keymod, + return js_keymod(js); +) + +JSC_CCALL(input_mousestate, + float x,y; + SDL_MouseButtonFlags flags = SDL_GetMouseState(&x,&y); + JSValue m = JS_NewObject(js); + JS_SetPropertyStr(js,m,"x", number2js(js,x)); + JS_SetPropertyStr(js,m,"y", number2js(js,y)); + + if (flags & SDL_BUTTON_LMASK) + JS_SetPropertyStr(js, m, "left", JS_NewBool(js, 1)); + if (flags & SDL_BUTTON_MMASK) + JS_SetPropertyStr(js, m, "middle", JS_NewBool(js, 1)); + if (flags & SDL_BUTTON_RMASK) + JS_SetPropertyStr(js, m, "right", JS_NewBool(js, 1)); + if (flags & SDL_BUTTON_X1MASK) + JS_SetPropertyStr(js, m, "x1", JS_NewBool(js, 1)); + if (flags & SDL_BUTTON_X2MASK) + JS_SetPropertyStr(js, m, "x2", JS_NewBool(js, 1)); + + return m; +) + +static const JSCFunctionListEntry js_input_funcs[] = { + MIST_FUNC_DEF(input, mouse_show, 1), + MIST_FUNC_DEF(input, mouse_lock, 1), + MIST_FUNC_DEF(input, cursor_set, 1), + MIST_FUNC_DEF(input, keyname, 1), + MIST_FUNC_DEF(input, keymod, 0), + MIST_FUNC_DEF(input, mousestate, 0), +}; + +JSValue js_input_use(JSContext *js) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,js_input_funcs,countof(js_input_funcs)); + + // Initialize SDL cursor class (no functions) + JSValue c_types = JS_GetPropertyStr(js, JS_GetGlobalObject(js), "c_types"); + QJSCLASSPREP_NO_FUNCS(SDL_Cursor) + JS_FreeValue(js, c_types); + + return mod; +} + +// CAMERA FUNCTIONS +JSC_CCALL(camera_list, + int num; + SDL_CameraID *ids = SDL_GetCameras(&num); + if (num == 0) return JS_UNDEFINED; + JSValue jsids = JS_NewArray(js); + for (int i = 0; i < num; i++) + JS_SetPropertyUint32(js,jsids, i, number2js(js,ids[i])); + + return jsids; +) + +JSC_CCALL(camera_open, + int id = js2number(js,argv[0]); + SDL_Camera *cam = SDL_OpenCamera(id, NULL); + if (!cam) ret = JS_ThrowReferenceError(js, "Could not open camera %d: %s\n", id, SDL_GetError()); + else + ret = SDL_Camera2js(js,cam); +) + +JSC_CCALL(camera_name, + const char *name = SDL_GetCameraName(js2number(js,argv[0])); + if (!name) return JS_ThrowReferenceError(js, "Could not get camera name from id %d.", (int)js2number(js,argv[0])); + + return JS_NewString(js, name); +) + +JSC_CCALL(camera_position, + SDL_CameraPosition pos = SDL_GetCameraPosition(js2number(js,argv[0])); + switch(pos) { + case SDL_CAMERA_POSITION_UNKNOWN: return JS_NewString(js,"unknown"); + case SDL_CAMERA_POSITION_FRONT_FACING: return JS_NewString(js,"front"); + case SDL_CAMERA_POSITION_BACK_FACING: return JS_NewString(js,"back"); + } +) + +static const JSCFunctionListEntry js_camera_funcs[] = { + MIST_FUNC_DEF(camera, list, 0), + MIST_FUNC_DEF(camera, open, 1), + MIST_FUNC_DEF(camera, name, 1), + MIST_FUNC_DEF(camera, position, 1), +}; + + +static const JSCFunctionListEntry js_SDL_Camera_funcs[] = +{ +// MIST_FUNC_DEF(camera, frame, 0), +// MIST_FUNC_DEF(camera, release_frame, 1), +}; + +JSValue js_camera_use(JSContext *js) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,js_camera_funcs,countof(js_camera_funcs)); + + // Initialize SDL camera class with functions + JSValue c_types = JS_GetPropertyStr(js, JS_GetGlobalObject(js), "c_types"); + QJSCLASSPREP_FUNCS(SDL_Camera) + JS_FreeValue(js, c_types); + + return mod; +} + +JSC_CCALL(camera_frame, +/* + SDL_ClearError(); + SDL_Camera *cam = js2SDL_Camera(js,self); + if (!cam) return JS_ThrowReferenceError(js,"Self was not a camera: %s", SDL_GetError()); + SDL_Surface *surf = SDL_AcquireCameraFrame(cam, NULL); + if (!surf) { + const char *msg = SDL_GetError(); + if (msg[0] != 0) + return JS_ThrowReferenceError(js,"Could not get camera frame: %s", SDL_GetError()); + else return JS_UNDEFINED; + } + return SDL_Surface2js(js,surf); + SDL_Surface *newsurf = SDL_CreateSurface(surf->w, surf->h, surf->format); + SDL_ReleaseCameraFrame(cam,surf); + + int didit = SDL_BlitSurface(surf, NULL, newsurf, NULL); + if (!didit) { + SDL_DestroySurface(newsurf); + return JS_ThrowReferenceError(js, "Could not blit: %s", SDL_GetError()); + } + + return SDL_Surface2js(js,newsurf); +*/ +) + +JSC_CCALL(camera_release_frame, +/* SDL_Camera *cam = js2SDL_Camera(js,self); + SDL_Surface *surf = js2SDL_Surface(js,argv[0]); + SDL_ReleaseCameraFrame(cam,surf); +*/ +) + + +// SDL AUDIO FUNCTIONS + +// Audio format lookup table and conversion functions +static const struct { const char *s; SDL_AudioFormat f; } fmt_lut[] = { + { "u8", SDL_AUDIO_U8 }, /* Unsigned 8-bit */ + { "s8", SDL_AUDIO_S8 }, /* Signed 8-bit */ + { "s16", SDL_AUDIO_S16 }, /* Signed 16-bit, host endian */ + { "s32", SDL_AUDIO_S32 }, /* Signed 32-bit, host endian */ + { "f32", SDL_AUDIO_F32 } /* Float 32-bit, host endian */ +}; + +static int format_str_to_enum(const char *f, SDL_AudioFormat *out) +{ + struct { const char *s; SDL_AudioFormat f; } map[] = { + {"u8", SDL_AUDIO_U8 }, {"s16", SDL_AUDIO_S16}, + {"s32", SDL_AUDIO_S32}, {"f32", SDL_AUDIO_F32} + }; + for (size_t i=0;iformat))); + + JS_SetPropertyStr(js, o, "channels", + JS_NewInt32(js, spec->channels)); + + JS_SetPropertyStr(js, o, "samplerate", + JS_NewInt32(js, spec->freq)); + + return o; +} + +static SDL_AudioSpec js2audiospec(JSContext *js, JSValue obj) +{ + SDL_AudioSpec spec; + + JSValue v; + + v = JS_GetPropertyStr(js, obj, "format"); + if (!JS_IsUndefined(v)) { + const char *s = JS_ToCString(js, v); + format_str_to_enum(s, &spec.format); + JS_FreeCString(js, s); + } + JS_FreeValue(js, v); + + v = JS_GetPropertyStr(js, obj, "channels"); + if (!JS_IsUndefined(v)) JS_ToInt32(js, &spec.channels, v); + JS_FreeValue(js, v); + + v = JS_GetPropertyStr(js, obj, "samplerate"); + if (!JS_IsUndefined(v)) JS_ToInt32(js, &spec.freq, v); + JS_FreeValue(js, v); + + return spec; +} + +JSC_CCALL(sdl_audio_drivers, + int num = SDL_GetNumAudioDrivers(); + JSValue arr = JS_NewArray(js); + for (int i = 0; i < num; i++) + JS_SetPropertyUint32(js, arr, i, JS_NewString(js, SDL_GetAudioDriver(i))); + return arr; +) + +JSC_CCALL(sdl_audio_devices, + int n; + SDL_AudioDeviceID *ids = SDL_GetAudioPlaybackDevices(&n); + + JSValue arr = JS_NewArray(js); + for (int i = 0; i < n; i++) + JS_SetPropertyUint32(js,arr,i,JS_NewString(js, SDL_GetAudioDeviceName(ids[i]))); + + return arr; +) + +JSC_CCALL(sdl_audio_open_stream, + const char *type = JS_IsString(argv[0]) ? JS_ToCString(js, argv[0]) : NULL; + SDL_AudioDeviceID devid = !strcmp(type, "capture") ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING : SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK; + + if (type) + JS_FreeCString(js, type); + + SDL_AudioStream *st; + + if (JS_IsUndefined(argv[1])) + st = SDL_OpenAudioDeviceStream(devid, NULL, NULL, NULL); + else { + SDL_AudioSpec want = js2audiospec(js, argv[1]); + st = SDL_OpenAudioDeviceStream(devid, &want, NULL, NULL); + } + + if (!st) + return JS_ThrowInternalError(js, "open failed: %s", SDL_GetError()); + + return SDL_AudioStream2js(js, st); +) + +static const JSCFunctionListEntry js_sdl_audio_funcs[] = { + MIST_FUNC_DEF(sdl_audio, drivers, 0), + MIST_FUNC_DEF(sdl_audio, devices, 0), + MIST_FUNC_DEF(sdl_audio, open_stream, 2), +}; + +JSC_CCALL(sdl_audiostream_get_format, + SDL_AudioStream *as = js2SDL_AudioStream(js, self); + SDL_AudioSpec src; + SDL_AudioSpec dst; + SDL_GetAudioStreamFormat(as, &src, &dst); + JSValue obj = JS_NewObject(js); + JS_SetPropertyStr(js, obj, "src", audiospec2js(js, &src)); + JS_SetPropertyStr(js, obj, "dst", audiospec2js(js, &dst)); + return obj; +) + +JSC_CCALL(sdl_audiostream_set_format, + SDL_AudioStream *as=js2SDL_AudioStream(js,self); + const SDL_AudioSpec *src_ptr=NULL,*dst_ptr=NULL; + SDL_AudioSpec src={0},dst={0}; + + if(argc>0&&!JS_IsUndefined(argv[0])){ + src=js2audiospec(js,argv[0]); + src_ptr=&src; + } + if(argc>1&&!JS_IsUndefined(argv[1])){ + dst=js2audiospec(js,argv[1]); + dst_ptr=&dst; + } + + if(!SDL_SetAudioStreamFormat(as,src_ptr,dst_ptr)) + return JS_ThrowInternalError(js,"%s",SDL_GetError()); + + return JS_UNDEFINED; +) + +JSC_CCALL(sdl_audiostream_resume, + SDL_AudioStream *as = js2SDL_AudioStream(js,self); + if (!SDL_ResumeAudioStreamDevice(as)) + return JS_ThrowInternalError(js,"%s",SDL_GetError()); + + return JS_UNDEFINED; +) + +JSC_CCALL(sdl_audiostream_clear, + SDL_AudioStream *as=js2SDL_AudioStream(js,self); + if (!SDL_ClearAudioStream(as)) + return JS_ThrowInternalError(js,"%s",SDL_GetError()); + return JS_UNDEFINED; +) + +JSC_CCALL(sdl_audiostream_flush, + SDL_AudioStream *as=js2SDL_AudioStream(js,self); + if(!SDL_FlushAudioStream(as)) + return JS_ThrowInternalError(js,"%s",SDL_GetError()); + return JS_UNDEFINED; +) + +JSC_CCALL(sdl_audiostream_available, + SDL_AudioStream *as=js2SDL_AudioStream(js,self); + Sint64 n = SDL_GetAudioStreamAvailable(as); + if(n<0) return JS_ThrowInternalError(js,"%s",SDL_GetError()); + return JS_NewInt64(js,n); +) + +JSC_CCALL(sdl_audiostream_queued, + SDL_AudioStream *as=js2SDL_AudioStream(js,self); + Sint64 n = SDL_GetAudioStreamQueued(as); + if(n<0) return JS_ThrowInternalError(js,"%s",SDL_GetError()); + return JS_NewInt64(js,n); +) + +/* ---------- data IO ---------------------------------------------------- */ +JSC_CCALL(sdl_audiostream_put, + SDL_AudioStream *as=js2SDL_AudioStream(js,self); + size_t len; + void *buf = JS_GetArrayBuffer(js, &len, argv[0]); + if (!buf) + return JS_ThrowInternalError(js, "Requires array buffer."); + + if (!SDL_PutAudioStreamData(as,buf,len)) + return JS_ThrowInternalError(js, "%s", SDL_GetError()); + return JS_UNDEFINED; +) + +JSC_CCALL(sdl_audiostream_get, + SDL_AudioStream *as=js2SDL_AudioStream(js,self); + int want; + JS_ToInt32(js,&want,argv[0]); + void *data = malloc(want); + int got = SDL_GetAudioStreamData(as, data, want); + + if (got<0) { + free(data); + return JS_ThrowInternalError(js,"%s",SDL_GetError()); + } + + JSValue ab = JS_NewArrayBufferCopy(js, data, got); + free(data); + + return ab; +) + +JSC_CCALL(sdl_audiostream_get_gain, + SDL_AudioStream *as=js2SDL_AudioStream(js,self); + return JS_NewFloat64(js,SDL_GetAudioStreamGain(as)); +) + +JSC_CCALL(sdl_audiostream_set_gain, + SDL_AudioStream *as=js2SDL_AudioStream(js,self); + double g; JS_ToFloat64(js,&g,argv[0]); + SDL_SetAudioStreamGain(as,(float)g); + return JS_UNDEFINED; +) + +JSC_CCALL(sdl_audiostream_get_freq_ratio, + SDL_AudioStream *as=js2SDL_AudioStream(js,self); + return JS_NewFloat64(js,SDL_GetAudioStreamFrequencyRatio(as)); +) + +JSC_CCALL(sdl_audiostream_set_freq_ratio, + SDL_AudioStream *as=js2SDL_AudioStream(js,self); + double r; JS_ToFloat64(js,&r,argv[0]); + SDL_SetAudioStreamFrequencyRatio(as,(float)r); + return JS_UNDEFINED; +) + +/* ---------- JS export list -------------------------------------------- */ +static const JSCFunctionListEntry js_SDL_AudioStream_funcs[] = { + MIST_FUNC_DEF(sdl_audiostream, get_format, 0), + MIST_FUNC_DEF(sdl_audiostream, set_format, 2), + MIST_FUNC_DEF(sdl_audiostream, resume, 0), + MIST_FUNC_DEF(sdl_audiostream, clear, 0), + MIST_FUNC_DEF(sdl_audiostream, flush, 0), + MIST_FUNC_DEF(sdl_audiostream, available, 0), + MIST_FUNC_DEF(sdl_audiostream, queued, 0), + MIST_FUNC_DEF(sdl_audiostream, put, 1), + MIST_FUNC_DEF(sdl_audiostream, get, 1), + MIST_FUNC_DEF(sdl_audiostream, set_gain, 1), + MIST_FUNC_DEF(sdl_audiostream, get_gain, 0), + MIST_FUNC_DEF(sdl_audiostream, set_freq_ratio, 1), + MIST_FUNC_DEF(sdl_audiostream, get_freq_ratio, 0), +}; + +JSValue js_sdl_audio_use(JSContext *js) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,js_sdl_audio_funcs,countof(js_sdl_audio_funcs)); + return mod; +} \ No newline at end of file diff --git a/source/qjs_sdl.h b/source/qjs_sdl.h new file mode 100644 index 00000000..5a87f2e5 --- /dev/null +++ b/source/qjs_sdl.h @@ -0,0 +1,10 @@ +#ifndef QJS_SDL_H +#define QJS_SDL_H + +#include "quickjs.h" + +JSValue js_input_use(JSContext *ctx); +JSValue js_camera_use(JSContext *ctx); +JSValue js_sdl_audio_use(JSContext *ctx); + +#endif /* QJS_SDL_H */ diff --git a/source/qjs_sdl_gpu.c b/source/qjs_sdl_gpu.c new file mode 100644 index 00000000..aac6fcff --- /dev/null +++ b/source/qjs_sdl_gpu.c @@ -0,0 +1,2481 @@ +#include "qjs_sdl_gpu.h" +#include "jsffi.h" +#include "qjs_macros.h" + +#include +#include "render.h" +#include "prosperon.h" +#include "sprite.h" + +#include "stb_dxt.h" + + +typedef struct { + JSValue val; + void *ptr; + size_t size; + int need_new; +} BufferCheckResult; + +static BufferCheckResult get_or_extend_buffer( + JSContext *js, + JSValue old_mesh, + const char *prop, + size_t needed_size, + int type, + int elements_per_item, + int copy, + int index +) { + BufferCheckResult res = { JS_UNDEFINED, NULL, 0, 0 }; + if (!JS_IsUndefined(old_mesh)) { + JSValue old_buf = JS_GetProperty(js, old_mesh, prop); + if (!JS_IsUndefined(old_buf)) { + size_t old_size; + void *data = get_gpu_buffer(js, old_buf, NULL, &old_size); + if (data && old_size >= needed_size) { + // Old buffer is large enough + res.val = old_buf; // keep it + res.ptr = data; + res.size = old_size; + return res; + } + JS_FreeValue(js, old_buf); + } + } + // If we reach here, we need a new buffer + res.need_new = 1; + return res; +} + +// Global GPU device and window +static SDL_GPUDevice *global_gpu; + +// GPU Free functions +void SDL_GPUDevice_free(JSRuntime *rt, SDL_GPUDevice *d) +{ + SDL_DestroyGPUDevice(d); +} + +void SDL_GPUCommandBuffer_free(JSRuntime *rt, SDL_GPUCommandBuffer *c) +{ +} + +void SDL_GPUComputePass_free(JSRuntime *rt, SDL_GPUComputePass *c) { } +void SDL_GPUCopyPass_free(JSRuntime *rt, SDL_GPUCopyPass *c) { } +void SDL_GPURenderPass_free(JSRuntime *rt, SDL_GPURenderPass *c) { } + + +// GPU Class definitions +#define GPURELEASECLASS(NAME) \ +void SDL_GPU##NAME##_free(JSRuntime *rt, SDL_GPU##NAME *c) { \ + SDL_ReleaseGPU##NAME(global_gpu, c); } \ +QJSCLASS(SDL_GPU##NAME,) \ + +QJSCLASS(SDL_GPUDevice,) + +GPURELEASECLASS(Buffer) +GPURELEASECLASS(ComputePipeline) +GPURELEASECLASS(GraphicsPipeline) +GPURELEASECLASS(Sampler) +GPURELEASECLASS(Shader) +GPURELEASECLASS(Texture) +GPURELEASECLASS(TransferBuffer) +GPURELEASECLASS(Fence) + +QJSCLASS(SDL_GPUCommandBuffer,) +QJSCLASS(SDL_GPUComputePass,) +QJSCLASS(SDL_GPUCopyPass,) +QJSCLASS(SDL_GPURenderPass,) + +SDL_GPUGraphicsPipelineTargetInfo js2SDL_GPUGraphicsPipelineTargetInfo(JSContext *js, JSValue v) +{ + SDL_GPUGraphicsPipelineTargetInfo info = {0}; + return info; +} + +SDL_GPUSampleCount js2SDL_GPUSampleCount(JSContext *js, JSValue v) +{ + int n = js2number(js,v); + switch(n) { + case 1: return SDL_GPU_SAMPLECOUNT_1; + case 2: return SDL_GPU_SAMPLECOUNT_2; + case 4: return SDL_GPU_SAMPLECOUNT_4; + case 8: return SDL_GPU_SAMPLECOUNT_8; + } + return -1; +} + +#define JS2ENUM(NAME, RETS, VALS) \ +int js2##NAME(JSContext *js, JSValue v) { \ + if (JS_IsUndefined(v)) return 0; \ + const char *str = JS_ToCString(js, v); \ + int *rets = (RETS); \ + const char **vals = (VALS); \ + /* Compute how many entries are in the arrays */ \ + int n = (int)(sizeof((RETS)) / sizeof((RETS)[0])); \ + for(int i = 0; i < n; i++) \ + if(!strcmp(vals[i], str)) { \ + JS_FreeCString(js, str); \ + return rets[i]; \ + } \ + JS_FreeCString(js, str); \ + return 0; \ +} + +static int rets_SDL_GPUSwapchainComposition[] = { + SDL_GPU_SWAPCHAINCOMPOSITION_SDR, + SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR, + SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR, +// SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084 +}; + +static const char *vals_SDL_GPUSwapchainComposition[] = { + "sdr", + "linear", + "hdr", +// "hdr10" +}; + +JS2ENUM(SDL_GPUSwapchainComposition, rets_SDL_GPUSwapchainComposition, vals_SDL_GPUSwapchainComposition) + +static int rets_SDL_FlipMode[] = { + SDL_FLIP_NONE, + SDL_FLIP_HORIZONTAL, + SDL_FLIP_VERTICAL +}; + +static const char *vals_SDL_FlipMode[] = { + "none", + "horizontal", + "vertical" +}; + +SDL_FColor js2SDL_FColor(JSContext *js, JSValue v) +{ + colorf color = js2color(js,v); + return (SDL_FColor){color.r,color.g,color.b,color.a}; +} + +JS2ENUM(SDL_FlipMode, rets_SDL_FlipMode, vals_SDL_FlipMode) + +/* ------------------------------------------------------- + 1) SDL_GPUBlendFactor + ------------------------------------------------------- */ + +static int rets_SDL_GPUBlendFactor[] = { + SDL_GPU_BLENDFACTOR_INVALID, + SDL_GPU_BLENDFACTOR_ZERO, + SDL_GPU_BLENDFACTOR_ONE, + SDL_GPU_BLENDFACTOR_SRC_COLOR, + SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_COLOR, + SDL_GPU_BLENDFACTOR_DST_COLOR, + SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_COLOR, + SDL_GPU_BLENDFACTOR_SRC_ALPHA, + SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + SDL_GPU_BLENDFACTOR_DST_ALPHA, + SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_ALPHA, + SDL_GPU_BLENDFACTOR_CONSTANT_COLOR, + SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR, + SDL_GPU_BLENDFACTOR_SRC_ALPHA_SATURATE +}; +static const char *vals_SDL_GPUBlendFactor[] = { + "invalid", + "zero", + "one", + "src_color", + "one_minus_src_color", + "dst_color", + "one_minus_dst_color", + "src_alpha", + "one_minus_src_alpha", + "dst_alpha", + "one_minus_dst_alpha", + "constant_color", + "one_minus_constant_color", + "src_alpha_saturate" +}; + +JS2ENUM(SDL_GPUBlendFactor, rets_SDL_GPUBlendFactor, vals_SDL_GPUBlendFactor) + +/* ------------------------------------------------------- + 2) SDL_GPUBlendOp + ------------------------------------------------------- */ +static int rets_SDL_GPUBlendOp[] = { + SDL_GPU_BLENDOP_INVALID, + SDL_GPU_BLENDOP_ADD, + SDL_GPU_BLENDOP_SUBTRACT, + SDL_GPU_BLENDOP_REVERSE_SUBTRACT, + SDL_GPU_BLENDOP_MIN, + SDL_GPU_BLENDOP_MAX +}; +static const char *vals_SDL_GPUBlendOp[] = { + "invalid", + "add", + "subtract", + "reverse_subtract", + "min", + "max" +}; + +JS2ENUM(SDL_GPUBlendOp, rets_SDL_GPUBlendOp, vals_SDL_GPUBlendOp) + +/* ------------------------------------------------------- + 3) SDL_GPUCompareOp + ------------------------------------------------------- */ +static int rets_SDL_GPUCompareOp[] = { + SDL_GPU_COMPAREOP_INVALID, + SDL_GPU_COMPAREOP_NEVER, + SDL_GPU_COMPAREOP_LESS, + SDL_GPU_COMPAREOP_EQUAL, + SDL_GPU_COMPAREOP_LESS_OR_EQUAL, + SDL_GPU_COMPAREOP_GREATER, + SDL_GPU_COMPAREOP_NOT_EQUAL, + SDL_GPU_COMPAREOP_GREATER_OR_EQUAL, + SDL_GPU_COMPAREOP_ALWAYS +}; +static const char *vals_SDL_GPUCompareOp[] = { + "invalid", + "never", + "less", + "equal", + "less_or_equal", + "greater", + "not_equal", + "greater_or_equal", + "always" +}; + +JS2ENUM(SDL_GPUCompareOp, rets_SDL_GPUCompareOp, vals_SDL_GPUCompareOp) + +/* ------------------------------------------------------- + 4) SDL_GPUCullMode + ------------------------------------------------------- */ +static int rets_SDL_GPUCullMode[] = { + SDL_GPU_CULLMODE_NONE, + SDL_GPU_CULLMODE_FRONT, + SDL_GPU_CULLMODE_BACK +}; +static const char *vals_SDL_GPUCullMode[] = { + "none", + "front", + "back" +}; + +JS2ENUM(SDL_GPUCullMode, rets_SDL_GPUCullMode, vals_SDL_GPUCullMode) + +/* ------------------------------------------------------- + 5) SDL_GPUFillMode + ------------------------------------------------------- */ +static int rets_SDL_GPUFillMode[] = { + SDL_GPU_FILLMODE_FILL, + SDL_GPU_FILLMODE_LINE +}; +static const char *vals_SDL_GPUFillMode[] = { + "fill", + "line" +}; + +JS2ENUM(SDL_GPUFillMode, rets_SDL_GPUFillMode, vals_SDL_GPUFillMode) + +/* ------------------------------------------------------- + 6) SDL_GPUFilter + ------------------------------------------------------- */ +static int rets_SDL_GPUFilter[] = { + SDL_GPU_FILTER_NEAREST, + SDL_GPU_FILTER_LINEAR +}; +static const char *vals_SDL_GPUFilter[] = { + "nearest", + "linear" +}; + +JS2ENUM(SDL_GPUFilter, rets_SDL_GPUFilter, vals_SDL_GPUFilter) + +/* ------------------------------------------------------- + 7) SDL_GPUFrontFace + ------------------------------------------------------- */ +static int rets_SDL_GPUFrontFace[] = { + SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE, + SDL_GPU_FRONTFACE_CLOCKWISE +}; +static const char *vals_SDL_GPUFrontFace[] = { + "counter_clockwise", + "clockwise" +}; + +JS2ENUM(SDL_GPUFrontFace, rets_SDL_GPUFrontFace, vals_SDL_GPUFrontFace) + +/* ------------------------------------------------------- + 8) SDL_GPULoadOp + ------------------------------------------------------- */ +static int rets_SDL_GPULoadOp[] = { + SDL_GPU_LOADOP_LOAD, + SDL_GPU_LOADOP_CLEAR, + SDL_GPU_LOADOP_DONT_CARE +}; +static const char *vals_SDL_GPULoadOp[] = { + "load", + "clear", + "dont_care" +}; + +JS2ENUM(SDL_GPULoadOp, rets_SDL_GPULoadOp, vals_SDL_GPULoadOp) + +/* ------------------------------------------------------- + 9) SDL_GPUPresentMode + ------------------------------------------------------- */ +static int rets_SDL_GPUPresentMode[] = { + SDL_GPU_PRESENTMODE_VSYNC, + SDL_GPU_PRESENTMODE_IMMEDIATE, + SDL_GPU_PRESENTMODE_MAILBOX +}; +static const char *vals_SDL_GPUPresentMode[] = { + "vsync", + "immediate", + "mailbox" +}; + +JS2ENUM(SDL_GPUPresentMode, rets_SDL_GPUPresentMode, vals_SDL_GPUPresentMode) + +/* ------------------------------------------------------- + 10) SDL_GPUPrimitiveType + ------------------------------------------------------- */ +static int rets_SDL_GPUPrimitiveType[] = { + SDL_GPU_PRIMITIVETYPE_TRIANGLELIST, + SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP, + SDL_GPU_PRIMITIVETYPE_LINELIST, + SDL_GPU_PRIMITIVETYPE_LINESTRIP, + SDL_GPU_PRIMITIVETYPE_POINTLIST +}; +static const char *vals_SDL_GPUPrimitiveType[] = { + "triangle", + "trianglestrip", + "line", + "linestrip", + "point" +}; + +JS2ENUM(SDL_GPUPrimitiveType, rets_SDL_GPUPrimitiveType, vals_SDL_GPUPrimitiveType) + +/* ------------------------------------------------------- + 11) SDL_GPUSamplerAddressMode + ------------------------------------------------------- */ +static int rets_SDL_GPUSamplerAddressMode[] = { + SDL_GPU_SAMPLERADDRESSMODE_REPEAT, + SDL_GPU_SAMPLERADDRESSMODE_MIRRORED_REPEAT, + SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE +}; +static const char *vals_SDL_GPUSamplerAddressMode[] = { + "repeat", + "mirrored_repeat", + "clamp" +}; + +JS2ENUM(SDL_GPUSamplerAddressMode, rets_SDL_GPUSamplerAddressMode, vals_SDL_GPUSamplerAddressMode) + +/* ------------------------------------------------------- + 12) SDL_GPUSamplerMipmapMode + ------------------------------------------------------- */ +static int rets_SDL_GPUSamplerMipmapMode[] = { + SDL_GPU_SAMPLERMIPMAPMODE_NEAREST, + SDL_GPU_SAMPLERMIPMAPMODE_LINEAR +}; +static const char *vals_SDL_GPUSamplerMipmapMode[] = { + "nearest", + "linear" +}; + +JS2ENUM(SDL_GPUSamplerMipmapMode, rets_SDL_GPUSamplerMipmapMode, vals_SDL_GPUSamplerMipmapMode) + +/* ------------------------------------------------------- + 13) SDL_GPUStencilOp + ------------------------------------------------------- */ +static int rets_SDL_GPUStencilOp[] = { + SDL_GPU_STENCILOP_INVALID, + SDL_GPU_STENCILOP_KEEP, + SDL_GPU_STENCILOP_ZERO, + SDL_GPU_STENCILOP_REPLACE, + SDL_GPU_STENCILOP_INCREMENT_AND_CLAMP, + SDL_GPU_STENCILOP_DECREMENT_AND_CLAMP, + SDL_GPU_STENCILOP_INVERT, + SDL_GPU_STENCILOP_INCREMENT_AND_WRAP, + SDL_GPU_STENCILOP_DECREMENT_AND_WRAP +}; +static const char *vals_SDL_GPUStencilOp[] = { + "invalid", + "keep", + "zero", + "replace", + "increment_and_clamp", + "decrement_and_clamp", + "invert", + "increment_and_wrap", + "decrement_and_wrap" +}; + +JS2ENUM(SDL_GPUStencilOp, rets_SDL_GPUStencilOp, vals_SDL_GPUStencilOp) + +static int rets_SDL_GPUTextureType[] = { + SDL_GPU_TEXTURETYPE_2D, /**< The texture is a 2-dimensional image. */ + SDL_GPU_TEXTURETYPE_2D_ARRAY, /**< The texture is a 2-dimensional array image. */ + SDL_GPU_TEXTURETYPE_3D, /**< The texture is a 3-dimensional image. */ + SDL_GPU_TEXTURETYPE_CUBE, /**< The texture is a cube image. */ + SDL_GPU_TEXTURETYPE_CUBE_ARRAY /**< The texture is a cube array image. */ +}; + +static const char *vals_SDL_GPUTextureType[] = { + "2d", + "2d array", + "3d", + "cube", + "cube array" +}; + +JS2ENUM(SDL_GPUTextureType, rets_SDL_GPUTextureType, vals_SDL_GPUTextureType) + +/* ------------------------------------------------------- + 14) SDL_GPUStoreOp + ------------------------------------------------------- */ +static int rets_SDL_GPUStoreOp[] = { + SDL_GPU_STOREOP_STORE, + SDL_GPU_STOREOP_DONT_CARE, + SDL_GPU_STOREOP_RESOLVE, + SDL_GPU_STOREOP_RESOLVE_AND_STORE +}; + +static const char *vals_SDL_GPUStoreOp[] = { + "store", + "dont_care", + "resolve", + "resolve_and_store" +}; + +JS2ENUM(SDL_GPUStoreOp, rets_SDL_GPUStoreOp, vals_SDL_GPUStoreOp) + +/* ------------------------------------------------------- + X) SDL_GPUTextureType + ------------------------------------------------------- */ +static int rets_SDL_GPUTextureFormat[] = { + SDL_GPU_TEXTUREFORMAT_INVALID, + + /* Unsigned Normalized Float Color Formats */ + SDL_GPU_TEXTUREFORMAT_A8_UNORM, + SDL_GPU_TEXTUREFORMAT_R8_UNORM, + SDL_GPU_TEXTUREFORMAT_R8G8_UNORM, + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, + SDL_GPU_TEXTUREFORMAT_R16_UNORM, + SDL_GPU_TEXTUREFORMAT_R16G16_UNORM, + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM, + SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM, + SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM, + SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM, + SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM, + SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, + /* Compressed Unsigned Normalized Float Color Formats */ + SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM, + SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM, + SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM, + SDL_GPU_TEXTUREFORMAT_BC4_R_UNORM, + SDL_GPU_TEXTUREFORMAT_BC5_RG_UNORM, + SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM, + /* Compressed Signed Float Color Formats */ + SDL_GPU_TEXTUREFORMAT_BC6H_RGB_FLOAT, + /* Compressed Unsigned Float Color Formats */ + SDL_GPU_TEXTUREFORMAT_BC6H_RGB_UFLOAT, + /* Signed Normalized Float Color Formats */ + SDL_GPU_TEXTUREFORMAT_R8_SNORM, + SDL_GPU_TEXTUREFORMAT_R8G8_SNORM, + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM, + SDL_GPU_TEXTUREFORMAT_R16_SNORM, + SDL_GPU_TEXTUREFORMAT_R16G16_SNORM, + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM, + /* Signed Float Color Formats */ + SDL_GPU_TEXTUREFORMAT_R16_FLOAT, + SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT, + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, + SDL_GPU_TEXTUREFORMAT_R32_FLOAT, + SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT, + SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT, + /* Unsigned Float Color Formats */ + SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT, + /* Unsigned Integer Color Formats */ + SDL_GPU_TEXTUREFORMAT_R8_UINT, + SDL_GPU_TEXTUREFORMAT_R8G8_UINT, + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT, + SDL_GPU_TEXTUREFORMAT_R16_UINT, + SDL_GPU_TEXTUREFORMAT_R16G16_UINT, + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT, + SDL_GPU_TEXTUREFORMAT_R32_UINT, + SDL_GPU_TEXTUREFORMAT_R32G32_UINT, + SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT, + /* Signed Integer Color Formats */ + SDL_GPU_TEXTUREFORMAT_R8_INT, + SDL_GPU_TEXTUREFORMAT_R8G8_INT, + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_INT, + SDL_GPU_TEXTUREFORMAT_R16_INT, + SDL_GPU_TEXTUREFORMAT_R16G16_INT, + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT, + SDL_GPU_TEXTUREFORMAT_R32_INT, + SDL_GPU_TEXTUREFORMAT_R32G32_INT, + SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT, + /* SRGB Unsigned Normalized Color Formats */ + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB, + /* Compressed SRGB Unsigned Normalized Color Formats */ + SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM_SRGB, + /* Depth Formats */ + SDL_GPU_TEXTUREFORMAT_D16_UNORM, + SDL_GPU_TEXTUREFORMAT_D24_UNORM, + SDL_GPU_TEXTUREFORMAT_D32_FLOAT, + SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT, + SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT, + /* Compressed ASTC Normalized Float Color Formats*/ + SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM, + SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM, + SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM, + SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM, + SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM, + SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM, + SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM, + SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM, + SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM, + SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM, + SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM, + SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM, + SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM, + SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM, + /* Compressed SRGB ASTC Normalized Float Color Formats*/ + SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM_SRGB, + SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM_SRGB, + /* Compressed ASTC Signed Float Color Formats*/ + SDL_GPU_TEXTUREFORMAT_ASTC_4x4_FLOAT, + SDL_GPU_TEXTUREFORMAT_ASTC_5x4_FLOAT, + SDL_GPU_TEXTUREFORMAT_ASTC_5x5_FLOAT, + SDL_GPU_TEXTUREFORMAT_ASTC_6x5_FLOAT, + SDL_GPU_TEXTUREFORMAT_ASTC_6x6_FLOAT, + SDL_GPU_TEXTUREFORMAT_ASTC_8x5_FLOAT, + SDL_GPU_TEXTUREFORMAT_ASTC_8x6_FLOAT, + SDL_GPU_TEXTUREFORMAT_ASTC_8x8_FLOAT, + SDL_GPU_TEXTUREFORMAT_ASTC_10x5_FLOAT, + SDL_GPU_TEXTUREFORMAT_ASTC_10x6_FLOAT, + SDL_GPU_TEXTUREFORMAT_ASTC_10x8_FLOAT, + SDL_GPU_TEXTUREFORMAT_ASTC_10x10_FLOAT, + SDL_GPU_TEXTUREFORMAT_ASTC_12x10_FLOAT, + SDL_GPU_TEXTUREFORMAT_ASTC_12x12_FLOAT +}; + +static const char *vals_SDL_GPUTextureFormat[] = { + "invalid", + "a8", + "r8", + "rg8", + "rgba8", + "r16", + "rg16", + "rgba16", + "r10g10b10a2", + "b5g6r5", + "b5g5r5a1", + "b4g4r4a4", + "b8g8r8a8", + + "bc1", + "bc2", + "bc3", + "bc4", + "bc5", + "bc7", + "bc6h float", + "bc6h ufloat", + + "r8 snorm", + "rg8 snorm", + "rgba8 snorm", + "r16 snorm", + "rg16 snorm", + "rgba16 snorm", + + "r16 float", + "rg16 float", + "rgba16 float", + "r32 float", + "rg32 float", + "rgba32 float", + + "r11g11b10", + + "r8 uint", + "rg8 uint", + "rgba8 uint", + "r16 uint", + "rg16 uint", + "rgba16 uint", + "r32 uint", + "rg32 uint", + "rgba32 uint", + + "r8 int", + "rg8 int", + "rgba8 int", + "r16 int", + "rg16 int", + "rgba16 int", + "r32 int", + "rg32 int", + "rgba32 int", + + "rgba8 srgb", + "b8g8r8a8 srgb", + "bc1 srgb", + "bc2 srgb", + "bc3 srgb", + "bc7 srgb", + + "d16", + "d24", + "d32 float", + "d24 s8", + "d32 float s8", + + "astc 4x4", + "astc 5x4", + "astc 5x5", + "astc 6x5", + "astc 6x6", + "astc 8x5", + "astc 8x6", + "astc 8x8", + "astc 10x5", + "astc 10x6", + "astc 10x8", + "astc 10x10", + "astc 12x10", + "astc 12x12", + + "astc 4x4 srgb", + "astc 5x4 srgb", + "astc 5x5 srgb", + "astc 6x5 srgb", + "astc 6x6 srgb", + "astc 8x5 srgb", + "astc 8x6 srgb", + "astc 8x8 srgb", + "astc 10x5 srgb", + "astc 10x6 srgb", + "astc 10x8 srgb", + "astc 10x10 srgb", + "astc 12x10 srgb", + "astc 12x12 srgb", + + "astc 4x4 float", + "astc 5x4 float", + "astc 5x5 float", + "astc 6x5 float", + "astc 6x6 float", + "astc 8x5 float", + "astc 8x6 float", + "astc 8x8 float", + "astc 10x5 float", + "astc 10x6 float", + "astc 10x8 float", + "astc 10x10 float", + "astc 12x10 float", + "astc 12x12 float" +}; + +SDL_GPUColorTargetInfo js2SDL_GPUColorTargetInfo(JSContext *js, JSValue v) +{ + SDL_GPUColorTargetInfo info = {0}; + JS_GETPROP(js, info.texture, v, texture, SDL_GPUTexture) + JS_GETPROP(js,info.mip_level,v,mip_level,number) + JS_GETPROP(js,info.layer_or_depth_plane, v, layer, number) + JS_GETPROP(js,info.load_op,v,load,SDL_GPULoadOp) + JS_GETPROP(js,info.store_op,v,store,SDL_GPUStoreOp) + JS_GETPROP(js,info.resolve_mip_level,v,resolve_mip_level,number) + JS_GETPROP(js,info.resolve_layer,v,resolve_layer,number) + JS_GETPROP(js,info.clear_color,v,clear,SDL_FColor) + return info; +} + +SDL_GPUTextureSamplerBinding js2SDL_GPUTextureSamplerBinding(JSContext *js, JSValue v) +{ + SDL_GPUTextureSamplerBinding b; + JS_GETPROP(js, b.texture, v, texture, SDL_GPUTexture) + JS_GETPROP(js, b.sampler, v, sampler, SDL_GPUSampler) + return b; +} + +JS2ENUM(SDL_GPUTextureFormat, rets_SDL_GPUTextureFormat, vals_SDL_GPUTextureFormat) + +SDL_GPUColorTargetBlendState js2SDL_GPUColorTargetBlendState(JSContext *js, JSValue v) +{ + SDL_GPUColorTargetBlendState state = {0}; + JS_GETPROP(js,state.src_color_blendfactor,v,src_rgb,SDL_GPUBlendFactor); + JS_GETPROP(js,state.dst_color_blendfactor,v,dst_rgb,SDL_GPUBlendFactor); + JS_GETPROP(js,state.src_alpha_blendfactor,v,src_alpha,SDL_GPUBlendFactor); + JS_GETPROP(js,state.dst_alpha_blendfactor,v,src_alpha,SDL_GPUBlendFactor); + JS_GETPROP(js,state.color_blend_op,v,op_rgb,SDL_GPUBlendOp) + JS_GETPROP(js,state.alpha_blend_op,v,op_alpha,SDL_GPUBlendOp) + JS_GETPROP(js,state.enable_blend,v,enabled,bool) + return state; +} + +SDL_GPUShaderFormat js2SDL_GPUShaderFormat(JSContext *js, JSValue v) +{ + const char *format_str = JS_ToCString(js, v); + SDL_GPUShaderFormat format = SDL_GPU_SHADERFORMAT_SPIRV; // default to SPIR-V + if (format_str) { + if (!strcmp(format_str, "private")) format = SDL_GPU_SHADERFORMAT_PRIVATE; + else if (!strcmp(format_str, "spv")) format = SDL_GPU_SHADERFORMAT_SPIRV; + else if (!strcmp(format_str, "dxbc")) format = SDL_GPU_SHADERFORMAT_DXBC; + else if (!strcmp(format_str, "dxil")) format = SDL_GPU_SHADERFORMAT_DXIL; + else if (!strcmp(format_str, "msl")) format = SDL_GPU_SHADERFORMAT_MSL; + else if (!strcmp(format_str, "metallib")) format = SDL_GPU_SHADERFORMAT_METALLIB; + JS_FreeCString(js, format_str); + } + return format; +} + +SDL_GPUColorTargetDescription js2SDL_GPUColorTargetDescription(JSContext *js, JSValue v) +{ + SDL_GPUColorTargetDescription dsc = {0}; + JS_GETPROP(js,dsc.format,v,format,SDL_GPUTextureFormat) + JS_GETPROP(js,dsc.blend_state,v,blend,SDL_GPUColorTargetBlendState) + return dsc; +} + +SDL_GPUStencilOpState js2SDL_GPUStencilOpState(JSContext *js, JSValue v) +{ + SDL_GPUStencilOpState state; + memset(&state, 0, sizeof(state)); + + JSValue compare_val = JS_GetPropertyStr(js, v, "compare"); + if(!JS_IsUndefined(compare_val)) state.compare_op = js2SDL_GPUCompareOp(js, compare_val); + JS_FreeValue(js, compare_val); + + JSValue fail_val = JS_GetPropertyStr(js, v, "fail"); + if(!JS_IsUndefined(fail_val)) state.fail_op = js2SDL_GPUStencilOp(js, fail_val); + JS_FreeValue(js, fail_val); + + JSValue depth_fail_val = JS_GetPropertyStr(js, v, "depth_fail"); + if(!JS_IsUndefined(depth_fail_val)) state.depth_fail_op = js2SDL_GPUStencilOp(js, depth_fail_val); + JS_FreeValue(js, depth_fail_val); + + JSValue pass_val = JS_GetPropertyStr(js, v, "pass"); + if(!JS_IsUndefined(pass_val)) state.pass_op = js2SDL_GPUStencilOp(js, pass_val); + JS_FreeValue(js, pass_val); + + return state; +} + + +// Unpacks a typed array javascript object. If it has a gpu property, returns it, too. Otherwise, if requested, makes one. +void *gpu_buffer_unpack(JSContext *js, SDL_GPUDevice *device, JSValue buffer, size_t *size, void **send_data, SDL_GPUBuffer **send_gpu) +{ + size_t o, len, bytes, msize; + JSValue buf = JS_GetTypedArrayBuffer(js, buffer, &o, &len, &bytes); + void *data = JS_GetArrayBuffer(js, &msize, buf); + JS_FreeValue(js,buf); + if (size) *size = msize; + if (send_gpu) { + JSValue gpu = JS_GetPropertyStr(js,buffer,"gpu"); + *send_gpu = js2SDL_GPUBuffer(js,gpu); + if (!*send_gpu) { + JSValue idx = JS_GetPropertyStr(js,buffer, "index"); + Uint32 usage = JS_ToBool(js,idx) ? SDL_GPU_BUFFERUSAGE_INDEX : SDL_GPU_BUFFERUSAGE_VERTEX; + JS_FreeValue(js,idx); + *send_gpu = SDL_CreateGPUBuffer(device, &(SDL_GPUBufferCreateInfo) { .usage=usage,.size=msize}); + if (!*send_gpu) printf("COULDN'T MAKE GPU BUFFER: %s\n", SDL_GetError()); + JS_SetPropertyStr(js, buffer, "gpu", SDL_GPUBuffer2js(js,*send_gpu)); + } + JS_FreeValue(js,gpu); + } + + if (send_data) + *send_data = data; + return data; +} + + +// GPU buffer management functions +void free_gpu_buffer(JSRuntime *rt, void *opaque, void *ptr) +{ + TracyCFree(ptr); +} + +// GPU API +JSC_CCALL(gpu_claim_window, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + SDL_Window *win = js2SDL_Window(js, argv[0]); + SDL_ClaimWindowForGPUDevice(gpu,win); +) + +JSC_CCALL(gpu_set_swapchain, + if (!SDL_SetGPUSwapchainParameters(global_gpu,global_window, js2SDL_GPUSwapchainComposition(js,argv[0]), js2SDL_GPUPresentMode(js,argv[1]))) + return JS_ThrowReferenceError(js, "Could not set: %s\n", SDL_GetError()); +) + +JSC_CCALL(cmd_acquire_swapchain, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + Uint32 w,h; + SDL_GPUTexture *texture; + SDL_AcquireGPUSwapchainTexture(cmds,global_window, &texture, &w, &h); + if (!texture) return JS_UNDEFINED; + JSValue swap = JS_UNDEFINED; + + JSValue *js_swapchains = ((prosperon_rt*)JS_GetContextOpaque(js))->js_swapchains; + + for (int i = 0; i < arrlen(js_swapchains); i++) { + if (js2SDL_GPUTexture(js,js_swapchains[i]) == texture) { + swap = js_swapchains[i]; + break; + } + } + + if (JS_IsUndefined(swap)) { + swap = SDL_GPUTexture2js(js,texture); + arrput(js_swapchains,swap); + } + + JS_SetPropertyStr(js,swap,"width", number2js(js,w)); + JS_SetPropertyStr(js,swap,"height", number2js(js,h)); + return JS_DupValue(js,swap); +) + +JSC_CCALL(cmd_swapchain_pass, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + SDL_GPUColorTargetInfo info = {0}; + Uint32 w, h; + SDL_AcquireGPUSwapchainTexture(cmds, global_window, &info.texture, &w,&h); + info.load_op = SDL_GPU_LOADOP_CLEAR; + info.store_op = SDL_GPU_STOREOP_STORE; + colorf c = js2color(js,argv[0]); + info.clear_color = (SDL_FColor){c.r,c.g,c.b,c.a}; + SDL_GPURenderPass *pass = SDL_BeginGPURenderPass( + cmds, + &info, + 1, + NULL + ); + if (!pass) return JS_ThrowReferenceError(js, "Unable to create swapchain pass: %s", SDL_GetError()); + JSValue jspass = SDL_GPURenderPass2js(js,pass); + JS_SetPropertyStr(js,jspass,"size", vec22js(js,(HMM_Vec2){w,h})); + return jspass; +) + +JSC_CCALL(gpu_load_texture, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self); + SDL_Surface *surf = js2SDL_Surface(js, argv[0]); + if (!surf) return JS_ThrowReferenceError(js, "Surface was not a surface."); + + int compression_level = js2number(js,argv[1]); + if (compression_level < 0) compression_level = 0; + if (compression_level > 2) compression_level = 2; + + int dofree = 0; + + SDL_PixelFormat sfmt = surf->format; + const SDL_PixelFormatDetails *pdetails = SDL_GetPixelFormatDetails(sfmt); + +if (!pdetails) { + // If we can't get pixel format details, fall back to converting to RGBA8888 + surf = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA8888); + dofree = 1; + sfmt = SDL_PIXELFORMAT_RGBA8888; + pdetails = SDL_GetPixelFormatDetails(sfmt); + if (!pdetails) { + // Should never happen with RGBA8888, but just in case + return JS_ThrowReferenceError(js, "Unable to get pixel format details."); + } +} + +// Check if format has alpha +bool has_alpha = (pdetails->Amask != 0); + +// Choose a GPU format that closely matches the surface's format +SDL_GPUTextureFormat chosen_format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; + +switch (sfmt) { + case SDL_PIXELFORMAT_RGBA32: + case SDL_PIXELFORMAT_RGBX8888: + case SDL_PIXELFORMAT_XRGB8888: + case SDL_PIXELFORMAT_XBGR8888: + chosen_format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; + break; + + case SDL_PIXELFORMAT_RGBA4444: + case SDL_PIXELFORMAT_ABGR4444: + case SDL_PIXELFORMAT_ARGB4444: + case SDL_PIXELFORMAT_BGRA4444: + // 4-4-4-4 format + chosen_format = SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM; + break; + + case SDL_PIXELFORMAT_RGB565: + case SDL_PIXELFORMAT_BGR565: + // 5-6-5 format + chosen_format = SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM; + break; + + case SDL_PIXELFORMAT_RGBA5551: + case SDL_PIXELFORMAT_ARGB1555: + case SDL_PIXELFORMAT_BGRA5551: + case SDL_PIXELFORMAT_ABGR1555: + // 5-5-5-1 format + chosen_format = SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM; + break; + + default: + surf = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA32); + dofree = 1; + sfmt = SDL_PIXELFORMAT_RGBA32; + pdetails = SDL_GetPixelFormatDetails(sfmt); + chosen_format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; + has_alpha = true; + break; +} + +// If compression_level > 0, we override format with BC1/BC3 +bool compress = (compression_level > 0); +int stb_mode = STB_DXT_NORMAL; +SDL_GPUTextureFormat compressed_format = chosen_format; + +if (compress) { + if (has_alpha) + compressed_format = SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM; // DXT5 + else + compressed_format = SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM; // DXT1 + + if (compression_level > 1) stb_mode = STB_DXT_HIGHQUAL; +} + +SDL_GPUTexture *tex = SDL_CreateGPUTexture(gpu, &(SDL_GPUTextureCreateInfo) { + .type = SDL_GPU_TEXTURETYPE_2D, + .format = compress ? compressed_format : chosen_format, + .width = surf->w, + .height = surf->h, + .layer_count_or_depth = 1, + .num_levels = 1, + .usage = SDL_GPU_TEXTUREUSAGE_SAMPLER +}); + +const void *pixel_data = surf->pixels; +size_t pixel_data_size = surf->pitch * surf->h; +unsigned char *upload_data = NULL; +size_t upload_size = pixel_data_size; + +if (compress) { + // Compress with stb_dxt + int block_width = (surf->w + 3) / 4; + int block_height = (surf->h + 3) / 4; + int block_size = (compressed_format == SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM) ? 8 : 16; + int blocks_count = block_width * block_height; + + unsigned char *compressed_data = (unsigned char*)malloc(blocks_count * block_size); + if (!compressed_data) { + if (dofree) SDL_DestroySurface(surf); + return JS_ThrowOutOfMemory(js); + } + + const unsigned char *src_pixels = (const unsigned char *)pixel_data; + for (int by = 0; by < block_height; ++by) { + for (int bx = 0; bx < block_width; ++bx) { + unsigned char block_rgba[4*4*4]; + memset(block_rgba, 0, sizeof(block_rgba)); + + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 4; ++x) { + int sx = bx*4 + x; + int sy = by*4 + y; + if (sx < surf->w && sy < surf->h) { + const unsigned char *pixel = src_pixels + sy * surf->pitch + sx * pdetails->bytes_per_pixel; + Uint32 pixelValue = 0; + // Copy pixel data into a Uint32 for SDL_GetRGBA-like extraction + // We'll use masks/shifts from pdetails + memcpy(&pixelValue, pixel, pdetails->bytes_per_pixel); + + // Extract RGBA + unsigned char r = (pixelValue & pdetails->Rmask) >> pdetails->Rshift; + unsigned char g = (pixelValue & pdetails->Gmask) >> pdetails->Gshift; + unsigned char b = (pixelValue & pdetails->Bmask) >> pdetails->Bshift; + unsigned char a = pdetails->Amask ? ((pixelValue & pdetails->Amask) >> pdetails->Ashift) : 255; + + // If bits are not fully 8-bit, scale them: + // pdetails->Rbits (etc.) give how many bits each channel uses + int Rmax = (1 << pdetails->Rbits) - 1; + int Gmax = (1 << pdetails->Gbits) - 1; + int Bmax = (1 << pdetails->Bbits) - 1; + int Amax = pdetails->Abits ? ((1 << pdetails->Abits) - 1) : 255; + + if (pdetails->Rbits < 8) r = (r * 255) / Rmax; + if (pdetails->Gbits < 8) g = (g * 255) / Gmax; + if (pdetails->Bbits < 8) b = (b * 255) / Bmax; + if (pdetails->Amask && pdetails->Abits < 8) a = (a * 255) / Amax; + + // Set block pixel + block_rgba[(y*4+x)*4+0] = r; + block_rgba[(y*4+x)*4+1] = g; + block_rgba[(y*4+x)*4+2] = b; + block_rgba[(y*4+x)*4+3] = a; + } + } + } + + unsigned char *dest_block = compressed_data + (by * block_width + bx)*block_size; + int alpha = (compressed_format == SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM) ? 1 : 0; + stb_compress_dxt_block(dest_block, block_rgba, alpha, stb_mode); + } + } + + upload_data = compressed_data; + upload_size = blocks_count * block_size; + } else { + // No compression, upload directly + upload_data = (unsigned char*)pixel_data; + upload_size = pixel_data_size; + } + + SDL_GPUTransferBuffer *tex_buffer = SDL_CreateGPUTransferBuffer( + gpu, + &(SDL_GPUTransferBufferCreateInfo) { + .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, + .size = upload_size + }); + void *tex_ptr = SDL_MapGPUTransferBuffer(gpu, tex_buffer, false); + memcpy(tex_ptr, upload_data, upload_size); + SDL_UnmapGPUTransferBuffer(gpu, tex_buffer); + + SDL_GPUCommandBuffer *uploadcmd = SDL_AcquireGPUCommandBuffer(gpu); + SDL_GPUCopyPass *copypass = SDL_BeginGPUCopyPass(uploadcmd); + SDL_UploadToGPUTexture( + copypass, + &(SDL_GPUTextureTransferInfo) { + .transfer_buffer = tex_buffer, + .offset = 0 + }, + &(SDL_GPUTextureRegion) { + .texture = tex, + .w = surf->w, + .h = surf->h, + .d = 1 + }, + false +); + + SDL_EndGPUCopyPass(copypass); + SDL_SubmitGPUCommandBuffer(uploadcmd); + SDL_ReleaseGPUTransferBuffer(gpu, tex_buffer); + + if (compress) { + free(upload_data); + } + + ret = SDL_GPUTexture2js(js, tex); + JS_SetPropertyStr(js, ret, "width", number2js(js, surf->w)); + JS_SetPropertyStr(js, ret, "height", number2js(js, surf->h)); + JS_SetPropertyStr(js,ret,"dim", vec22js(js,(HMM_Vec2){surf->w,surf->h})); + + if (dofree) SDL_DestroySurface(surf); + +) + +static JSValue js_gpu_make_pipeline(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + if (argc < 1) + return JS_ThrowTypeError(js, "gpu_pipeline requires a pipeline object"); + + JSValue pipe = argv[0]; + if (!JS_IsObject(pipe)) + return JS_ThrowTypeError(js, "gpu_pipeline argument must be an object"); + + SDL_GPUGraphicsPipelineCreateInfo info = {0}; + + // --------------------------------------------------- + // 2. Retrieve vertex buffer descriptions array + // --------------------------------------------------- + JSValue vbd_val = JS_GetPropertyStr(js, pipe, "vertex_buffer_descriptions"); + Uint32 vbd_len = JS_ArrayLength(js,vbd_val); + SDL_GPUVertexBufferDescription vbd[vbd_len]; + for (Uint32 i = 0; i < vbd_len; i++) { + JSValue elem = JS_GetPropertyUint32(js, vbd_val, i); + if (JS_IsObject(elem)) { + JSValue slot_val = JS_GetPropertyStr(js, elem, "slot"); + JSValue pitch_val = JS_GetPropertyStr(js, elem, "pitch"); + JSValue rate_val = JS_GetPropertyStr(js, elem, "input_rate"); + JSValue step_val = JS_GetPropertyStr(js, elem, "instance_step_rate"); + + // Slot + Uint32 slot = 0; + JS_ToUint32(js, &slot, slot_val); + JS_FreeValue(js, slot_val); + + // Pitch + Uint32 pitch = 0; + JS_ToUint32(js, &pitch, pitch_val); + JS_FreeValue(js, pitch_val); + + // Input Rate + const char *rate_str = JS_ToCString(js, rate_val); + SDL_GPUVertexInputRate input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX; + if (rate_str) { + if (!strcmp(rate_str, "vertex")) input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX; + else if (!strcmp(rate_str, "instance")) input_rate = SDL_GPU_VERTEXINPUTRATE_INSTANCE; + JS_FreeCString(js, rate_str); + } + JS_FreeValue(js, rate_val); + + // Instance Step Rate + Uint32 step_rate = 0; + JS_ToUint32(js, &step_rate, step_val); + JS_FreeValue(js, step_val); + + vbd[i].slot = slot; + vbd[i].pitch = pitch; + vbd[i].input_rate = input_rate; + vbd[i].instance_step_rate = step_rate; + } + JS_FreeValue(js, elem); + } + JS_FreeValue(js, vbd_val); + + // --------------------------------------------------- + // 3. Retrieve vertex attributes array + // --------------------------------------------------- + JSValue vat_val = JS_GetPropertyStr(js, pipe, "vertex_attributes"); + Uint32 vat_len = JS_ArrayLength(js,vat_val); + + SDL_GPUVertexAttribute vat[vat_len]; + for (Uint32 i = 0; i < vat_len; i++) { + JSValue elem = JS_GetPropertyUint32(js, vat_val, i); + if (JS_IsObject(elem)) { + JSValue loc_val = JS_GetPropertyStr(js, elem, "location"); + JSValue slot_val = JS_GetPropertyStr(js, elem, "buffer_slot"); + JSValue fmt_val = JS_GetPropertyStr(js, elem, "format"); + JSValue off_val = JS_GetPropertyStr(js, elem, "offset"); + + // location + Uint32 location = 0; + JS_ToUint32(js, &location, loc_val); + JS_FreeValue(js, loc_val); + + // buffer_slot + Uint32 buffer_slot = 0; + JS_ToUint32(js, &buffer_slot, slot_val); + JS_FreeValue(js, slot_val); + + // format + const char *fmt_str = JS_ToCString(js, fmt_val); + SDL_GPUVertexElementFormat format = -1; // TODO: Check for error (if this is still -1) + if (fmt_str) { + // Map from string to SDL_GPUVertexElementFormat + if (!strcmp(fmt_str, "float2")) format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2; + else if (!strcmp(fmt_str, "float3")) format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; + else if (!strcmp(fmt_str, "float4")) format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4; + else if (!strcmp(fmt_str, "color")) + format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4; + JS_FreeCString(js, fmt_str); + } + JS_FreeValue(js, fmt_val); + + // offset + Uint32 offset = 0; + JS_ToUint32(js, &offset, off_val); + JS_FreeValue(js, off_val); + + vat[i].location = location; + vat[i].buffer_slot = buffer_slot; + vat[i].format = format; + vat[i].offset = offset; + } + JS_FreeValue(js, elem); + } + JS_FreeValue(js, vat_val); + + info.vertex_input_state = (SDL_GPUVertexInputState){ + .vertex_buffer_descriptions = vbd, + .num_vertex_buffers = vbd_len, + .vertex_attributes = vat, + .num_vertex_attributes = vat_len + }; + + // Vertex Shader + JSValue vertex_val = JS_GetPropertyStr(js, pipe, "vertex"); + JSValue fragment_val = JS_GetPropertyStr(js,pipe,"fragment"); + JS_GETPROP(js,info.vertex_shader, vertex_val, gpu, SDL_GPUShader) + JS_GETPROP(js, info.fragment_shader, fragment_val, gpu, SDL_GPUShader) + + JS_FreeValue(js, vertex_val); + JS_FreeValue(js, fragment_val); + + // Primitive Type + JS_GETPROP(js, info.primitive_type, pipe, primitive, SDL_GPUPrimitiveType) + + // Rasterizer State + JS_GETPROP(js, info.rasterizer_state.fill_mode, pipe, fill, SDL_GPUFillMode) + JS_GETPROP(js, info.rasterizer_state.cull_mode, pipe, cull, SDL_GPUCullMode) + JS_GETPROP(js, info.rasterizer_state.front_face, pipe, face, SDL_GPUFrontFace) + + // Depth Stencil State + JSValue depth_val = JS_GetPropertyStr(js, pipe, "depth"); + if (JS_IsObject(depth_val)) { + JS_GETPROP(js,info.depth_stencil_state.compare_op, depth_val, compare, SDL_GPUCompareOp) + JS_GETPROP(js,info.depth_stencil_state.enable_depth_test, depth_val, test, bool) + JS_GETPROP(js,info.depth_stencil_state.enable_depth_write, depth_val, write, bool) + JS_GETPROP(js,info.rasterizer_state.depth_bias_constant_factor, depth_val, bias, number) + JS_GETPROP(js,info.rasterizer_state.depth_bias_slope_factor, depth_val,bias_slope_scale, number) + JS_GETPROP(js,info.rasterizer_state.depth_bias_clamp, depth_val,bias_clamp_val, number) + } + JS_FreeValue(js, depth_val); + + // Stencil State + JSValue stencil_val = JS_GetPropertyStr(js, pipe, "stencil"); + if (JS_IsObject(stencil_val)) { + JS_GETPROP(js,info.depth_stencil_state.enable_stencil_test, stencil_val, enabled, bool) + if (info.depth_stencil_state.enable_stencil_test) { + JS_GETPROP(js, info.depth_stencil_state.front_stencil_state, stencil_val, front, SDL_GPUStencilOpState) + JS_GETPROP(js, info.depth_stencil_state.back_stencil_state, stencil_val, back, SDL_GPUStencilOpState) + + JSValue compare_mask_val = JS_GetPropertyStr(js, stencil_val, "compare_mask"); + uint32_t tmp; + JS_ToUint32(js, &tmp, compare_mask_val); + info.depth_stencil_state.compare_mask = tmp; + JS_FreeValue(js, compare_mask_val); + + // Write Mask + JSValue write_mask_val = JS_GetPropertyStr(js, stencil_val, "write_mask"); + JS_ToUint32(js, &tmp, write_mask_val); + info.depth_stencil_state.write_mask = tmp; + } + } + JS_FreeValue(js, stencil_val); + + JSValue js_tar = JS_GetPropertyStr(js,pipe,"target"); + + SDL_GPUGraphicsPipelineTargetInfo target_info = {0}; + JSValue color_tars = JS_GetPropertyStr(js,js_tar,"color_targets"); + target_info.num_color_targets = JS_ArrayLength(js,color_tars); + SDL_GPUColorTargetDescription dsc[target_info.num_color_targets]; + target_info.color_target_descriptions = dsc; + + for (int i = 0; i < target_info.num_color_targets; i++) { + JSValue c = JS_GetPropertyUint32(js,color_tars,i); + dsc[i] = js2SDL_GPUColorTargetDescription(js,c); + JS_FreeValue(js,c); + } + JS_FreeValue(js,color_tars); + + JS_GETPROP(js,target_info.depth_stencil_format,js_tar,depth,SDL_GPUTextureFormat); + if (target_info.depth_stencil_format) target_info.has_depth_stencil_target = 1; + + info.target_info = target_info; + + JS_FreeValue(js, js_tar); + + // Create the pipeline + SDL_GPUGraphicsPipeline *pipeline = SDL_CreateGPUGraphicsPipeline(gpu, &info); + if (!pipeline) return JS_ThrowInternalError(js, "Failed to create GPU pipeline"); + + return SDL_GPUGraphicsPipeline2js(js, pipeline); +} + +// Now the function to create the sampler +static JSValue js_gpu_make_sampler(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + SDL_GPUSamplerCreateInfo info = {0}; + JSValue sampler = argv[0]; + + JS_GETPROP(js,info.min_filter,sampler,min_filter,SDL_GPUFilter) + JS_GETPROP(js,info.mag_filter,sampler,mag_filter,SDL_GPUFilter) + JS_GETPROP(js,info.mipmap_mode, sampler, mipmap, SDL_GPUSamplerMipmapMode) + JS_GETPROP(js,info.address_mode_u, sampler, u, SDL_GPUSamplerAddressMode) + JS_GETPROP(js,info.address_mode_v, sampler, v, SDL_GPUSamplerAddressMode) + JS_GETPROP(js,info.address_mode_w, sampler, w, SDL_GPUSamplerAddressMode) + JS_GETPROP(js,info.mip_lod_bias, sampler, mip_bias, number) + JS_GETPROP(js,info.max_anisotropy, sampler, max_anisotropy, number) + JS_GETPROP(js,info.compare_op,sampler,compare_op,SDL_GPUCompareOp) + JS_GETPROP(js,info.min_lod,sampler,min_lod,number) + JS_GETPROP(js,info.max_lod,sampler,max_lod,number) + JS_GETPROP(js, info.enable_anisotropy, sampler, anistropy, bool) + JS_GETPROP(js, info.enable_compare, sampler, compare, bool) + + // Create the sampler + SDL_GPUSampler *sdl_sampler = SDL_CreateGPUSampler(gpu, &info); + if (!sdl_sampler) return JS_ThrowInternalError(js, "Failed to create GPU sampler"); + + return SDL_GPUSampler2js(js, sdl_sampler); // You need to implement SDL_GPUSampler2js similar to pipeline +} + + +JSC_CCALL(gpu_texture, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + SDL_GPUTextureCreateInfo info = {0}; + JSValue fmt = argv[0]; + JS_GETPROP(js, info.width, fmt, width, number) + JS_GETPROP(js, info.height, fmt, height, number) + JS_GETPROP(js, info.layer_count_or_depth, fmt, layers, number) + JS_GETPROP(js, info.num_levels, fmt, mip_levels, number) + JS_GETPROP(js, info.sample_count, fmt, samples, number) + JS_GETPROP(js, info.type, fmt, type, SDL_GPUTextureType) + JS_GETPROP(js, info.format, fmt, format, SDL_GPUTextureFormat) + if (JS_GETBOOL(js,fmt,"sampler")) + info.usage |= SDL_GPU_TEXTUREUSAGE_SAMPLER; + if (JS_GETBOOL(js,fmt,"color_target")) + info.usage |= SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; + if (JS_GETBOOL(js,fmt,"depth_target")) + info.usage |= SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; + if (JS_GETBOOL(js,fmt,"read")) + info.usage |= (SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ); + if (JS_GETBOOL(js,fmt,"write")) + info.usage |= SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE; + if (JS_GETBOOL(js,fmt,"readwrite")) + info.usage |= SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE; + + SDL_GPUTexture *tex = SDL_CreateGPUTexture(gpu,&info); + if (!tex) return JS_ThrowReferenceError(js, "Unable to create texture: %s", SDL_GetError()); + JSValue jstex = SDL_GPUTexture2js(js,tex); + JS_SetPropertyStr(js,jstex,"width", number2js(js,info.width)); + JS_SetPropertyStr(js,jstex,"height", number2js(js,info.height)); + JS_SetPropertyStr(js,jstex,"dim", vec22js(js,(HMM_Vec2){info.width, info.height})); + return jstex; +) + +static HMM_Vec3 base_quad[4] = { + {0.0,0.0,1.0}, + {1.0,0.0,1.0}, + {0.0,1.0,1.0}, + {1.0,1.0,1.0} +}; + + +typedef struct { + text_vert vert[4]; +} quad; + +// Comparator inline for potential compiler inlining +static inline int sprite_compare(const sprite *a, const sprite *b) { + if (a->layer != b->layer) return a->layer - b->layer; + + if (a->pos.Y != b->pos.Y) + return (b->pos.Y - a->pos.Y); + + if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) + return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; + + return 0; +} + +// Merge two sorted sub-ranges [left..mid] and [mid+1..right] +static void merge_sprites(sprite *arr, sprite *temp, size_t left, size_t mid, size_t right) { + size_t i = left, j = mid + 1, k = left; + + while (i <= mid && j <= right) + if (sprite_compare(&arr[i], &arr[j]) <= 0) temp[k++] = arr[i++]; + else temp[k++] = arr[j++]; + + while (i <= mid) temp[k++] = arr[i++]; + while (j <= right) temp[k++] = arr[j++]; + + for (size_t p = left; p <= right; p++) + arr[p] = temp[p]; +} + +// Recursive mergesort +static void merge_sort_recursive(sprite *arr, sprite *temp, size_t left, size_t right) { + if (left >= right) return; + size_t mid = (left + right) / 2; + merge_sort_recursive(arr, temp, left, mid); + merge_sort_recursive(arr, temp, mid + 1, right); + merge_sprites(arr, temp, left, mid, right); +} + +// Public function to sort the array of sprites with mergesort +void better_sort_sprites(sprite *arr, size_t length) { + if (length < 2) return; + sprite *temp = malloc(length * sizeof(sprite)); + if (!temp) return; // fallback or handle error + merge_sort_recursive(arr, temp, 0, length - 1); + free(temp); +} + +int sort_sprite_backtofront(const sprite *a, const sprite *b) +{ + if (a->layer != b->layer) return a->layer - b->layer; + if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) + return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; + return 0; +} + +int sort_sprite_fronttoback(const sprite *a, const sprite *b) +{ + if (a->layer != b->layer) return b->layer - a->layer; + if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) + return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; + return 0; +} + +int sort_sprite_texture(const sprite *a, const sprite *b) +{ + if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) + return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; + return 0; +} + +int sort_sprite(const sprite *a, const sprite *b) +{ + if (a->layer != b->layer) return a->layer - b->layer; + + if (a->pos.Y != b->pos.Y) + return (b->pos.Y - a->pos.Y); + + if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) + return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; + return 0; +} + +JSC_CCALL(gpu_sort_sprite, + JSValue a = argv[0]; + JSValue b = argv[1]; + + int alayer, blayer; + JS_GETATOM(js,alayer,a,layer,number) + JS_GETATOM(js,blayer,b,layer,number) + if (alayer != blayer) return number2js(js,alayer - blayer); + + rect ar, br; + JS_GETATOM(js,ar,a,rect,rect) + JS_GETATOM(js,br,b,rect,rect) + if (ar.y != br.y) return number2js(js,br.y-ar.y); + + JSValue aimg,bimg; + aimg = JS_GetPropertyStr(js,a,"image"); + bimg = JS_GetPropertyStr(js,b,"image"); + JS_FreeValue(js,aimg); + JS_FreeValue(js,bimg); + if (!JS_SameValue(js,aimg,bimg)) return number2js(js,JS_VALUE_GET_PTR(aimg) < JS_VALUE_GET_PTR(bimg) ? -1 : 1); + return number2js(js,0); +) + + + +JSC_CCALL(gpu_make_quad, + size_t quads = 1; + size_t verts = quads*4; + size_t count = quads*6; + + // Prepare arrays on CPU + HMM_Vec2 *posdata = malloc(sizeof(*posdata)*verts); + HMM_Vec2 *uvdata = malloc(sizeof(*uvdata)*verts); + HMM_Vec4 *colordata = malloc(sizeof(*colordata)*verts); + HMM_Vec4 usecolor = (HMM_Vec4){1,1,1,1}; + posdata[0] = (HMM_Vec2){0,1}; + posdata[1] = (HMM_Vec2){1,1}; + posdata[2] = (HMM_Vec2){0,0}; + posdata[3] = (HMM_Vec2){1,0}; + + uvdata[0] = (HMM_Vec2){0,0}; + uvdata[1] = (HMM_Vec2){1,0}; + uvdata[2] = (HMM_Vec2){0,1}; + uvdata[3] = (HMM_Vec2){1,1}; + + colordata[0] = usecolor; + colordata[1] = usecolor; + colordata[2] = usecolor; + colordata[3] = usecolor; + + ret = JS_NewObject(js); + JSValue pos = make_gpu_buffer(js, posdata, sizeof(*posdata)*verts, JS_TYPED_ARRAY_FLOAT32, 2, 1,0); + JSValue uv = make_gpu_buffer(js, uvdata, sizeof(*uvdata)*verts, JS_TYPED_ARRAY_FLOAT32, 2, 1,0); + JSValue color = make_gpu_buffer(js, colordata, sizeof(*colordata)*verts, JS_TYPED_ARRAY_FLOAT32, 0, 1,0); + + JS_SetPropertyStr(js, ret, "pos", pos); + JS_SetPropertyStr(js, ret, "uv", uv); + JS_SetPropertyStr(js, ret, "color", color); + JSValue indices = make_quad_indices_buffer(js, quads); + JS_SetPropertyStr(js, ret, "indices", indices); + + JS_SetPropertyStr(js, ret, "vertices", number2js(js, verts)); + JS_SetPropertyStr(js, ret, "count", number2js(js, count)); + JS_SetPropertyStr(js,ret,"num_indices", number2js(js,count)); + + // Free temporary CPU arrays + free(posdata); + free(uvdata); + free(colordata); + + return ret; +) + + +JSC_CCALL(gpu_driver, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + ret = JS_NewString(js, SDL_GetGPUDeviceDriver(gpu)); +) + +static JSValue js_gpu_make_shader(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { + if (argc < 1 || !JS_IsObject(argv[0])) + return JS_ThrowTypeError(js, "make_shader expects an object with code, stage, num_samplers, num_textures, num_storage_buffers, num_uniform_buffers"); + + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self); + + JSValue obj = argv[0]; + + SDL_GPUShaderCreateInfo info = {0}; + + // code + JSValue code_val = JS_GetPropertyStr(js, obj, "code"); + size_t code_size; + void *code_data = JS_GetArrayBuffer(js, &code_size, code_val); + JS_FreeValue(js, code_val); + if (!code_data) + return JS_ThrowTypeError(js, "shader.code must be an ArrayBuffer"); + + // stage + JSValue stage_val = JS_GetPropertyStr(js, obj, "stage"); + const char *stage_str = JS_ToCString(js, stage_val); + if (stage_str) { + if (!strcmp(stage_str, "vertex")) info.stage = SDL_GPU_SHADERSTAGE_VERTEX; + else if (!strcmp(stage_str, "fragment")) info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT; + JS_FreeCString(js, stage_str); + } // TODO: Make this its own function + JS_FreeValue(js, stage_val); + + // num_samplers + JS_GETPROP(js, info.num_samplers, obj, num_samplers, number) + JS_GETPROP(js, info.num_storage_textures, obj, num_textures, number) + JS_GETPROP(js, info.num_storage_buffers, obj, num_storage_buffers, number) + JS_GETPROP(js, info.num_uniform_buffers, obj, num_uniform_buffers, number) + JS_GETPROP(js, info.format, obj, format, SDL_GPUShaderFormat) + + JSValue entry_val = JS_GetPropertyStr(js,obj,"entrypoint"); + info.entrypoint = JS_ToCString(js,entry_val); + JS_FreeValue(js,entry_val); + + info.code_size = code_size; + info.code = code_data; + info.props = 0; // No extension properties by default + + JS_FreeCString(js,info.entrypoint); + + SDL_GPUShader *shader = SDL_CreateGPUShader(gpu, &info); + if (!shader) + return JS_ThrowReferenceError(js, "Unable to create shader: %s", SDL_GetError()); + + return SDL_GPUShader2js(js, shader); +} + +JSC_CCALL(gpu_acquire_cmd_buffer, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self); + SDL_GPUCommandBuffer *cb = SDL_AcquireGPUCommandBuffer(gpu); + if (!cb) return JS_ThrowReferenceError(js,"Unable to acquire command buffer: %s", SDL_GetError()); + // Wrap cb in a JS object + return SDL_GPUCommandBuffer2js(js, cb); +) + +/* takes argv + 0: a command buffer to write to + 1: a buffer or array of buffers to upload + 2: an optional transfer buffer to use; if undefined a temporary one is used +*/ +JSC_CCALL(gpu_upload, + JSValue js_cmd = argv[0]; + JSValue js_buffers = argv[1]; + JSValue js_transfer = argv[2]; + + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self); + if (!gpu) + return JS_ThrowTypeError(js, "Invalid GPU device"); + + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, js_cmd); + if (!cmds) + return JS_ThrowTypeError(js, "Invalid command buffer"); + + if (!JS_IsArray(js, js_buffers)) + return JS_ThrowTypeError(js, "buffers must be an array"); + + size_t len = JS_ArrayLength(js, js_buffers); + if (len == 0) + return JS_DupValue(js, js_transfer); // No data to upload, just return existing transfer buffer + + struct { + SDL_GPUBuffer *gpu_buffer; + void *data; + size_t size; + } *items = malloc(sizeof(*items) * len); + + if (!items) + return JS_ThrowOutOfMemory(js); + + size_t total_size_needed = 0; + + for (size_t i = 0; i < len; i++) { + JSValue js_buf = JS_GetPropertyUint32(js, js_buffers, i); + + if (JS_IsUndefined(js_buf)) + continue; + + gpu_buffer_unpack(js, gpu, js_buf, &items[i].size, &items[i].data, &items[i].gpu_buffer); + total_size_needed += items[i].size; + JS_FreeValue(js, js_buf); + } + + SDL_GPUTransferBuffer *transfer = js2SDL_GPUTransferBuffer(js,js_transfer); + if (transfer) { + // ensure it's large enough + size_t transfer_size = js_getnum_str(js,js_transfer, "size"); + if (transfer_size < total_size_needed) { + total_size_needed *= 1.5; + transfer = SDL_CreateGPUTransferBuffer( gpu, &(SDL_GPUTransferBufferCreateInfo){ + .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, + .size = total_size_needed + } + ); + ret = SDL_GPUTransferBuffer2js(js,transfer); + JS_SetPropertyStr(js,ret,"size", number2js(js,total_size_needed)); + } else + ret = JS_DupValue(js,js_transfer); // supplied transfer buffer is fine so we use it + } else { + transfer = SDL_CreateGPUTransferBuffer( gpu, &(SDL_GPUTransferBufferCreateInfo){ + .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, + .size = total_size_needed + } + ); + ret = SDL_GPUTransferBuffer2js(js,transfer); + JS_SetPropertyStr(js,ret,"size", number2js(js,total_size_needed)); + } + + SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(cmds); + if (!copy_pass) { + free(items); + return JS_ThrowReferenceError(js, "Failed to begin copy pass"); + } + + void *mapped_data = SDL_MapGPUTransferBuffer(gpu, transfer, false); + if (!mapped_data) { + SDL_EndGPUCopyPass(copy_pass); + free(items); + return JS_ThrowReferenceError(js, "Failed to map transfer buffer: %s", SDL_GetError()); + } + + // Copy all data into the mapped transfer buffer + size_t current_offset = 0; + for (size_t i = 0; i < len; i++) { + memcpy((char*)mapped_data + current_offset, items[i].data, items[i].size); + current_offset += items[i].size; + } + SDL_UnmapGPUTransferBuffer(gpu, transfer); + + // Issue uploads for each item + current_offset = 0; + for (size_t i = 0; i < len; i++) { + SDL_UploadToGPUBuffer( + copy_pass, + &(SDL_GPUTransferBufferLocation){ + .transfer_buffer = transfer, + .offset = current_offset + }, + &(SDL_GPUBufferRegion){ + .buffer = items[i].gpu_buffer, + .offset = 0, + .size = items[i].size + }, + false + ); + current_offset += items[i].size; + } + + SDL_EndGPUCopyPass(copy_pass); + free(items); +) + +JSC_CCALL(gpu_wait_for_fences, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + int n = JS_ArrayLength(js,argv[0]); + SDL_GPUFence *fences[n]; + for (int i = 0; i < n; i++) { + JSValue a = JS_GetPropertyUint32(js,argv[0],i); + fences[i] = js2SDL_GPUFence(js,a); + JS_FreeValue(js,a); + } + + int wait_all = JS_ToBool(js,argv[1]); + return JS_NewBool(js,SDL_WaitForGPUFences(gpu,wait_all,fences,n)); +) + +JSC_CCALL(gpu_query_fence, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + SDL_GPUFence *fence = js2SDL_GPUFence(js,argv[0]); + return JS_NewBool(js,SDL_QueryGPUFence(gpu,fence)); +) + +JSC_CCALL(gpu_shader_format, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + SDL_GPUShaderFormat fmt = SDL_GetGPUShaderFormats(gpu); + if (!fmt) return JS_ThrowReferenceError(js, "Shader format available invalid."); + + JSValue arr = JS_NewArray(js); + int i = 0; + if (fmt & SDL_GPU_SHADERFORMAT_PRIVATE) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, ".private")); + if (fmt & SDL_GPU_SHADERFORMAT_SPIRV) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "spv")); + if (fmt & SDL_GPU_SHADERFORMAT_DXBC) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "dxbc")); + if (fmt & SDL_GPU_SHADERFORMAT_DXIL) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "dxil")); + if (fmt & SDL_GPU_SHADERFORMAT_MSL) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "msl")); + if (fmt & SDL_GPU_SHADERFORMAT_METALLIB) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "metallib")); + return arr; +) + +JSC_CCALL(gpu_compute_pipeline, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + SDL_GPUComputePipelineCreateInfo info = {0}; + JSValue pipe = argv[0]; + JS_GETPROP(js, info.num_samplers, pipe, num_samplers, number) + JS_GETPROP(js,info.num_readonly_storage_textures,pipe,num_readonly_storage_textures,number) + JS_GETPROP(js,info.num_readonly_storage_buffers,pipe,num_readonly_storage_buffers,number) + JS_GETPROP(js,info.num_readwrite_storage_textures,pipe,num_readwrite_storage_textures,number) + JS_GETPROP(js,info.num_readwrite_storage_buffers,pipe,num_readwrite_storage_buffers,number) + JS_GETPROP(js,info.threadcount_x,pipe,threadcount_x,number) + JS_GETPROP(js,info.threadcount_y,pipe,threadcount_y,number) + JS_GETPROP(js,info.threadcount_z,pipe,threadcount_z,number) + JS_GETPROP(js,info.entrypoint,pipe,entrypoint,cstring) + + + JSValue shader = JS_GetPropertyStr(js,pipe,"shader"); + info.code = JS_GetArrayBuffer(js,&info.code_size, shader); + JS_FreeValue(js,shader); + + SDL_GPUComputePipeline *pipeline = SDL_CreateGPUComputePipeline(gpu, &info); + JS_FreeCString(js,info.entrypoint); + if (!pipeline) return JS_ThrowReferenceError(js,"Could not create compute pipeline: %s", SDL_GetError()); + return SDL_GPUComputePipeline2js(js,pipeline); +) + +static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = { + MIST_FUNC_DEF(gpu, claim_window, 1), + MIST_FUNC_DEF(gpu, make_pipeline, 1), // loads pipeline state into an object + MIST_FUNC_DEF(gpu,compute_pipeline,1), + MIST_FUNC_DEF(gpu, set_swapchain, 2), + MIST_FUNC_DEF(gpu,sort_sprite,2), + MIST_FUNC_DEF(gpu, make_sampler,1), + MIST_FUNC_DEF(gpu, load_texture, 2), + MIST_FUNC_DEF(gpu, texture, 1), + MIST_FUNC_DEF(gpu, make_quad, 0), + MIST_FUNC_DEF(gpu, driver, 0), + MIST_FUNC_DEF(gpu, make_shader, 1), + MIST_FUNC_DEF(gpu, acquire_cmd_buffer, 0), + MIST_FUNC_DEF(gpu, upload, 3), + MIST_FUNC_DEF(gpu, wait_for_fences, 2), + MIST_FUNC_DEF(gpu, query_fence, 1), + MIST_FUNC_DEF(gpu, shader_format, 0), +}; + +JSC_CCALL(renderpass_bind_pipeline, + SDL_GPURenderPass *r = js2SDL_GPURenderPass(js,self); + SDL_GPUGraphicsPipeline *pipe = js2SDL_GPUGraphicsPipeline(js,argv[0]); + SDL_BindGPUGraphicsPipeline(r,pipe); +) + +JSC_CCALL(renderpass_draw_indexed, + SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); + SDL_DrawGPUIndexedPrimitives(pass, js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]), js2number(js,argv[4])); +) + +JSC_CCALL(renderpass_draw, + SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); + SDL_DrawGPUPrimitives(pass, js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3])); +) + +JSC_CCALL(renderpass_bind_buffers, + SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); + int first = js2number(js,argv[0]); + JSValue buffers = argv[1]; + int len = JS_ArrayLength(js,buffers); + SDL_GPUBufferBinding bindings[len]; + for (int i = 0; i < len; i++) { + JSValue buffer = JS_GetPropertyUint32(js,buffers,i); + bindings[i].offset = 0; + gpu_buffer_unpack(js,global_gpu, buffer, NULL, NULL,&bindings[i].buffer); + JS_FreeValue(js,buffer); + } + + SDL_BindGPUVertexBuffers( + pass, + first, + bindings, + len); +) + +JSC_CCALL(renderpass_bind_index_buffer, + SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); + SDL_GPUBufferBinding bind; + bind.offset = 0; + gpu_buffer_unpack(js,global_gpu,argv[0], NULL, NULL, &bind.buffer); + int elen; + JSValue b = argv[0]; + JS_GETPROP(js, elen, b, elen, number) + + SDL_BindGPUIndexBuffer(pass,&bind,elen == 2 ? SDL_GPU_INDEXELEMENTSIZE_16BIT : SDL_GPU_INDEXELEMENTSIZE_32BIT); +) + +JSC_CCALL(renderpass_end, + SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); + SDL_EndGPURenderPass(pass); +) + +JSC_CCALL(renderpass_bind_samplers, + SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); + int first_slot = js2number(js,argv[1]); + JSValue arr = argv[2]; + int num = JS_ArrayLength(js,arr); + SDL_GPUTextureSamplerBinding binds[num]; + for (int i = 0; i < num; i++) { + JSValue val = JS_GetPropertyUint32(js,arr,i); + JSValue tex = JS_GetPropertyStr(js,val,"texture"); + JSValue smp = JS_GetPropertyStr(js,val,"sampler"); + binds[i].texture = js2SDL_GPUTexture(js,tex); + binds[i].sampler = js2SDL_GPUSampler(js,smp); + JS_FreeValue(js,tex); + JS_FreeValue(js,smp); + JS_FreeValue(js,val); + } + int vertex = JS_ToBool(js,argv[0]); + if (vertex) + SDL_BindGPUVertexSamplers(pass, first_slot, binds, num); + else + SDL_BindGPUFragmentSamplers(pass, first_slot, binds, num); +) + +JSC_CCALL(renderpass_bind_storage_buffers, + +) + +JSC_CCALL(renderpass_bind_storage_textures, + +) + +JSC_CCALL(renderpass_viewport, + SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); + rect r = js2rect(js,argv[0]); + SDL_GPUViewport vp ={0}; + vp.x = r.x; + vp.y = r.y; + vp.w = r.w; + vp.h = r.h; + vp.min_depth = 0; + vp.max_depth = 1; + SDL_SetGPUViewport(pass,&vp); +) + +JSC_CCALL(renderpass_scissor, + SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); + rect r = js2rect(js,argv[0]); + SDL_Rect rr; + rr.x = r.x; + rr.y = r.y; + rr.w = r.w; + rr.h = r.h; + SDL_SetGPUScissor(pass,&rr); +) + +static const JSCFunctionListEntry js_SDL_GPURenderPass_funcs[] = { + MIST_FUNC_DEF(renderpass, bind_pipeline, 1), + MIST_FUNC_DEF(renderpass, viewport, 1), + MIST_FUNC_DEF(renderpass, scissor, 1), + MIST_FUNC_DEF(renderpass, draw, 4), + MIST_FUNC_DEF(renderpass, draw_indexed, 5), + MIST_FUNC_DEF(renderpass, end, 0), + MIST_FUNC_DEF(renderpass, bind_index_buffer, 1), + MIST_FUNC_DEF(renderpass, bind_buffers, 2), + MIST_FUNC_DEF(renderpass, bind_samplers, 3), + MIST_FUNC_DEF(renderpass, bind_storage_buffers, 2), + MIST_FUNC_DEF(renderpass, bind_storage_textures, 2), +}; + +JSC_CCALL(cmd_render_pass, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + + if (!JS_IsObject(argv[0])) return JS_ThrowTypeError(js, "render_pass: Expected a render pass descriptor object"); + JSValue passObj = argv[0]; + + JSValue colorTargetsVal = JS_GetPropertyStr(js, passObj, "color_targets"); + if (!JS_IsArray(js, colorTargetsVal)) + return JS_ThrowTypeError(js, "render_pass: colorTargets must be an array"); + + uint32_t colorCount = JS_ArrayLength(js, colorTargetsVal); + SDL_GPUColorTargetInfo colortars[colorCount]; + SDL_GPUDepthStencilTargetInfo depthtar; + int has_depth = 0; + + // Fill colorInfos from JS array + for (uint32_t i = 0; i < colorCount; i++) { + JSValue ctargetVal = JS_GetPropertyUint32(js, colorTargetsVal, i); + colortars[i] = js2SDL_GPUColorTargetInfo(js,ctargetVal); + JS_FreeValue(js, ctargetVal); + } + + // Optional depth_stencil + JSValue depthval = JS_GetPropertyStr(js, passObj, "depth_stencil"); + if (!JS_IsUndefined(depthval)) { + has_depth = 1; + JS_GETPROP(js, depthtar.texture, depthval, texture, SDL_GPUTexture) + JS_GETPROP(js, depthtar.load_op, depthval, load, SDL_GPULoadOp) + JS_GETPROP(js, depthtar.store_op, depthval, store, SDL_GPUStoreOp) + JS_GETPROP(js, depthtar.stencil_load_op, depthval, stencil_load, SDL_GPULoadOp) + JS_GETPROP(js, depthtar.stencil_store_op, depthval, stencil_store, SDL_GPUStoreOp) + JS_GETPROP(js,depthtar.clear_depth, depthval, clear, number) + JS_GETPROP(js,depthtar.clear_stencil,depthval,clear_stencil,number) + } + JS_FreeValue(js, depthval); + + SDL_GPURenderPass *pass = SDL_BeginGPURenderPass( + cmds, + colortars, + colorCount, + has_depth ? &depthtar : NULL + ); + + JS_FreeValue(js, colorTargetsVal); + + if (!pass) return JS_ThrowInternalError(js, "render_pass: Failed to begin render pass"); + + return SDL_GPURenderPass2js(js, pass); +) + +JSC_CCALL(cmd_bind_vertex_buffer, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + int slot; + JS_ToInt32(js, &slot, argv[0]); + SDL_GPUBuffer *buffer = js2SDL_GPUBuffer(js, argv[1]); + size_t offset = 0; + if (argc > 2) JS_ToIndex(js, &offset, argv[2]); + SDL_BindGPUVertexBuffers(cmds, slot, &(SDL_GPUBufferBinding){.buffer = buffer, .offset = offset}, 1); +) + +JSC_CCALL(cmd_bind_index_buffer, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + SDL_GPUBuffer *buffer = js2SDL_GPUBuffer(js, argv[0]); + size_t offset = 0; + if (argc > 1) JS_ToIndex(js, &offset, argv[1]); + SDL_BindGPUIndexBuffer(cmds, &(SDL_GPUBufferBinding){.buffer = buffer, .offset = offset}, SDL_GPU_INDEXELEMENTSIZE_16BIT); +) + +JSC_CCALL(cmd_bind_fragment_sampler, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + int slot; + JS_ToInt32(js, &slot, argv[0]); + SDL_GPUTexture *tex = js2SDL_GPUTexture(js, argv[1]); + SDL_GPUSampler *sampler = js2SDL_GPUSampler(js, argv[2]); + SDL_BindGPUFragmentSamplers(cmds, slot, &(SDL_GPUTextureSamplerBinding){.texture = tex, .sampler = sampler}, 1); +) + +JSC_CCALL(cmd_push_vertex_uniform_data, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + int slot; + JS_ToInt32(js, &slot, argv[0]); + size_t buf_size; + void *data = JS_GetArrayBuffer(js, &buf_size, argv[1]); + SDL_PushGPUVertexUniformData(cmds, slot, data, buf_size); +) + +JSC_CCALL(cmd_push_fragment_uniform_data, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + int slot; + JS_ToInt32(js, &slot, argv[0]); + size_t buf_size; + void *data = JS_GetArrayBuffer(js, &buf_size, argv[1]); + SDL_PushGPUFragmentUniformData(cmds, slot, data, buf_size); +) + +JSC_CCALL(cmd_push_compute_uniform_data, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + int slot; + JS_ToInt32(js, &slot, argv[0]); + size_t buf_size; + void *data = JS_GetArrayBuffer(js, &buf_size, argv[1]); + SDL_PushGPUComputeUniformData(cmds, slot, data, buf_size); +) + +JSC_CCALL(cmd_submit, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js,self); + SDL_GPUFence *fence = SDL_SubmitGPUCommandBufferAndAcquireFence(cmds); + return SDL_GPUFence2js(js,fence); +) + +JSC_CCALL(cmd_hud, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js,self); + HMM_Vec2 size = js2vec2(js,argv[0]); + HMM_Mat4 proj = HMM_Orthographic_RH_NO(0,size.x,0,size.y,-1,1); + shader_globals data = {0}; + data.world_to_projection = proj; + data.projection_to_world = HMM_InvGeneralM4(proj); + data.viewport_min_z = -1, + data.viewport_max_z = 1; + data.render_size = size; + data.view_to_projection = proj; + data.viewport_size = (HMM_Vec2){1,1}; + data.viewport_offset = (HMM_Vec2){0,0}; + data.time = SDL_GetTicksNS() / 1000000000.0f; + SDL_PushGPUVertexUniformData(cmds, js2number(js,argv[1]), &data, sizeof(data)); +) + +JSC_CCALL(cmd_camera, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + shader_globals data = camera_globals(js, argv[0]); + SDL_PushGPUVertexUniformData(cmds, js2number(js,argv[1]), &data, sizeof(data)); +) + +JSC_SCALL(cmd_push_debug_group, + SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self); + SDL_PushGPUDebugGroup(cmd,str); +) + +JSC_CCALL(cmd_pop_debug_group, + SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self); + SDL_PopGPUDebugGroup(cmd); +) + +JSC_SCALL(cmd_debug_label, + SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self); + SDL_InsertGPUDebugLabel(cmd, str); +) + +SDL_GPUBlitRegion js2SDL_GPUBlitRegion(JSContext *js, JSValue v) +{ + SDL_GPUBlitRegion info = {0}; + if (JS_GetClassID(v) == js_SDL_GPUTexture_id) { + // texture path + JSValue tex = v; + info.texture = js2SDL_GPUTexture(js,tex); + info.mip_level = 0, + info.layer_or_depth_plane = 0; + info.x = 0; + info.y = 0; + JS_GETPROP(js,info.w,tex,width,number) + JS_GETPROP(js,info.h,tex,height,number) + return info; + } + JSValue tex = JS_GetPropertyStr(js, v, "texture"); + info.texture = js2SDL_GPUTexture(js,tex); + JS_GETPROP(js,info.mip_level,v,mip_level, number) + JS_GETPROP(js,info.mip_level,v,layer,number) + JS_GETPROP(js,info.x,v,x,number) + JS_GETPROP(js,info.y,v,y,number) + JS_GETPROP(js,info.w,v,width,number) + JS_GETPROP(js,info.h,v,height,number) + + if (!info.w) JS_GETPROP(js,info.w,tex,width,number) + if (!info.h) JS_GETPROP(js,info.h,tex,height,number) + + JS_FreeValue(js,tex); + return info; +} + +JSC_CCALL(cmd_blit, + SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self); + SDL_GPUBlitInfo info = {0}; + JSValue v = argv[0]; + JS_GETPROP(js,info.source,v,src,SDL_GPUBlitRegion) + JS_GETPROP(js,info.destination,v,dst,SDL_GPUBlitRegion) + JS_GETPROP(js,info.load_op,v,load,SDL_GPULoadOp) + JS_GETPROP(js,info.flip_mode,v,flip,SDL_FlipMode) + JS_GETPROP(js,info.filter,v,filter,SDL_GPUFilter) + JS_GETPROP(js,info.clear_color,v,color,SDL_FColor) + SDL_BlitGPUTexture(cmd,&info); +) + +JSC_CCALL(cmd_cancel, + SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self); + SDL_CancelGPUCommandBuffer(cmd); +) + +JSC_CCALL(cmd_compute_pass, + SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self); + JSValue textures = argv[0]; + JSValue buffers = argv[1]; + + int t_n = JS_ArrayLength(js,textures); + SDL_GPUStorageTextureReadWriteBinding t_bind[t_n]; + for (int i = 0; i < t_n; i++) { + JSValue T = JS_GetPropertyUint32(js,textures,i); + JS_GETPROP(js, t_bind[i].texture, T, texture, SDL_GPUTexture) + JS_GETPROP(js,t_bind[i].mip_level,T,mip, number) + JS_GETPROP(js,t_bind[i].layer,T,layer, number) + JS_FreeValue(js,T); + } + + int b_n = JS_ArrayLength(js,buffers); + SDL_GPUStorageBufferReadWriteBinding b_bind[b_n]; + for (int i = 0; i < b_n; i++) { + JSValue T = JS_GetPropertyUint32(js,buffers,i); + JS_GETPROP(js,b_bind[i].buffer, T,buffer,SDL_GPUBuffer) + } + + SDL_GPUComputePass *pass = SDL_BeginGPUComputePass(cmd,t_bind,t_n,b_bind,b_n); + if (!pass) return JS_ThrowReferenceError(js, "Unable to begin compute pass: %s", SDL_GetError()); + return SDL_GPUComputePass2js(js,pass); +) + +static const JSCFunctionListEntry js_SDL_GPUCommandBuffer_funcs[] = { + MIST_FUNC_DEF(cmd, render_pass, 1), + MIST_FUNC_DEF(cmd, compute_pass, 2), + MIST_FUNC_DEF(cmd, swapchain_pass, 1), + MIST_FUNC_DEF(cmd, acquire_swapchain,0), + MIST_FUNC_DEF(cmd, bind_vertex_buffer, 2), + MIST_FUNC_DEF(cmd, bind_index_buffer, 1), + MIST_FUNC_DEF(cmd, bind_fragment_sampler, 3), + MIST_FUNC_DEF(cmd, push_vertex_uniform_data, 2), + MIST_FUNC_DEF(cmd, push_fragment_uniform_data, 2), + MIST_FUNC_DEF(cmd, push_compute_uniform_data, 2), + MIST_FUNC_DEF(cmd, submit, 0), + MIST_FUNC_DEF(cmd, cancel, 0), + MIST_FUNC_DEF(cmd, camera, 2), + MIST_FUNC_DEF(cmd, hud, 2), + MIST_FUNC_DEF(cmd, push_debug_group, 1), + MIST_FUNC_DEF(cmd, pop_debug_group, 0), + MIST_FUNC_DEF(cmd, debug_label, 1), + MIST_FUNC_DEF(cmd, blit, 1), +}; + +JSC_SCALL(buffer_name, + SDL_GPUBuffer *buffer = js2SDL_GPUBuffer(js,self); + SDL_GPUDevice *gpu; + JS_GETPROP(js, gpu, self, gpu, SDL_GPUDevice) + SDL_SetGPUBufferName(gpu,buffer,str); +) + +static const JSCFunctionListEntry js_SDL_GPUBuffer_funcs[] = { + MIST_FUNC_DEF(buffer, name, 1), +}; + +JSC_CCALL(compute_dispatch, + SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self); + SDL_DispatchGPUCompute(pass,js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2])); +) + +JSC_CCALL(compute_end, + SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self); + SDL_EndGPUComputePass(pass); +) + +JSC_CCALL(compute_pipeline, + SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self); + SDL_GPUComputePipeline *pipeline = js2SDL_GPUComputePipeline(js,argv[0]); + SDL_BindGPUComputePipeline(pass,pipeline); +) + +JSC_CCALL(compute_samplers, + SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self); + JSValue samplers = argv[0]; + int n = JS_ArrayLength(js,samplers); + SDL_GPUTextureSamplerBinding b[n]; + for (int i = 0; i < n; i++) { + JSValue s = JS_GetPropertyUint32(js,samplers,i); + b[i] = js2SDL_GPUTextureSamplerBinding(js,s); + JS_FreeValue(js,s); + } + SDL_BindGPUComputeSamplers(pass,js2number(js,argv[1]),b,n); +) + +JSC_CCALL(compute_storage_textures, + SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self); + JSValue textures = argv[0]; + int n = JS_ArrayLength(js,textures); + SDL_GPUTexture *b[n]; + for (int i = 0; i < n; i++) { + JSValue s = JS_GetPropertyUint32(js,textures,i); + b[i] = js2SDL_GPUTexture(js,s); + JS_FreeValue(js,s); + } + SDL_BindGPUComputeStorageTextures(pass,js2number(js,argv[1]),b,n); +) + +JSC_CCALL(compute_storage_buffers, + SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self); + JSValue buffers = argv[0]; + int n = JS_ArrayLength(js,buffers); + SDL_GPUBuffer *b[n]; + for (int i = 0; i < n; i++) { + JSValue s = JS_GetPropertyUint32(js,buffers,i); + b[i] = js2SDL_GPUBuffer(js,s); + JS_FreeValue(js,s); + } + SDL_BindGPUComputeStorageTextures(pass,js2number(js,argv[1]),b,n); +) + +static const JSCFunctionListEntry js_SDL_GPUComputePass_funcs[] = { + MIST_FUNC_DEF(compute, dispatch, 3), + MIST_FUNC_DEF(compute, end, 0), + MIST_FUNC_DEF(compute, pipeline, 1), + MIST_FUNC_DEF(compute, samplers, 2), + MIST_FUNC_DEF(compute, storage_buffers, 2), + MIST_FUNC_DEF(compute, storage_textures, 2), +}; + +JSValue make_gpu_buffer(JSContext *js, void *data, size_t size, int type, int elements, int copy, int index) +{ + JSValue tstack[2]; + if (copy) { + tstack[0] = JS_NewArrayBufferCopy(js,data,size);//, make_gpu_buffer, NULL, 1); + } else { + tstack[0] = JS_NewArrayBuffer(js,data,size,free_gpu_buffer, NULL, 0); + } + tstack[1] = JS_NewObject(js); + JS_SetPropertyStr(js,tstack[1],"type",number2js(js,type)); + JS_SetPropertyStr(js,tstack[1],"elements",number2js(js,elements)); + JS_SetPropertyStr(js,tstack[1],"buffer",tstack[0]); + JS_SetPropertyStr(js,tstack[1],"index", number2js(js,index)); + return tstack[1]; +} + +void *get_gpu_buffer(JSContext *js, JSValue argv, size_t *stride, size_t *size) +{ + if (JS_IsArray(js,argv)) { + if (stride) *stride = sizeof(float); + int arrlen = JS_ArrayLength(js,argv); + float *arr = malloc(sizeof(float)*arrlen); + for (int i = 0; i < arrlen; i++) { + JSValue v = JS_GetPropertyUint32(js,argv,i); + arr[i] = js2number(js,v); + JS_FreeValue(js,v); + } + if (size) *size = sizeof(float)*arrlen; + return arr; + } + + JSValue buffer = JS_GetPropertyStr(js,argv,"buffer"); + void *data = JS_GetArrayBuffer(js,NULL,buffer); + JS_FreeValue(js,buffer); + + JSValue typeval = JS_GetPropertyStr(js,argv,"type"); + int type = js2number(js,typeval); + JS_FreeValue(js,typeval); + + return data; +} + +// Conversion functions for SDL GPU types +SDL_GPUGraphicsPipelineTargetInfo js2SDL_GPUGraphicsPipelineTargetInfo(JSContext *js, JSValue v) +{ + SDL_GPUGraphicsPipelineTargetInfo info = {0}; + return info; +} + +SDL_GPUSampleCount js2SDL_GPUSampleCount(JSContext *js, JSValue v) +{ + int n = js2number(js,v); + switch(n) { + case 1: return SDL_GPU_SAMPLECOUNT_1; + case 2: return SDL_GPU_SAMPLECOUNT_2; + case 4: return SDL_GPU_SAMPLECOUNT_4; + case 8: return SDL_GPU_SAMPLECOUNT_8; + } + return -1; +} + +// Enum conversion macro and definitions +#define JS2ENUM(NAME, RETS, VALS) \ +int js2##NAME(JSContext *js, JSValue v) { \ + if (JS_IsUndefined(v)) return 0; \ + const char *str = JS_ToCString(js, v); \ + int *rets = (RETS); \ + const char **vals = (VALS); \ + /* Compute how many entries are in the arrays */ \ + int n = (int)(sizeof((RETS)) / sizeof((RETS)[0])); \ + for(int i = 0; i < n; i++) \ + if(!strcmp(vals[i], str)) { \ + JS_FreeCString(js, str); \ + return rets[i]; \ + } \ + JS_FreeCString(js, str); \ + return 0; \ +} + +// SDL GPU Swapchain Composition +static int rets_SDL_GPUSwapchainComposition[] = { + SDL_GPU_SWAPCHAINCOMPOSITION_SDR, + SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR, + SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR, +// SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084 +}; + +static const char *vals_SDL_GPUSwapchainComposition[] = { + "sdr", + "linear", + "hdr", +// "hdr10" +}; + +JS2ENUM(SDL_GPUSwapchainComposition, rets_SDL_GPUSwapchainComposition, vals_SDL_GPUSwapchainComposition) + +// SDL GPU Blend Factor +static int rets_SDL_GPUBlendFactor[] = { + SDL_GPU_BLENDFACTOR_INVALID, + SDL_GPU_BLENDFACTOR_ZERO, + SDL_GPU_BLENDFACTOR_ONE, + SDL_GPU_BLENDFACTOR_SRC_COLOR, + SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_COLOR, + SDL_GPU_BLENDFACTOR_DST_COLOR, + SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_COLOR, + SDL_GPU_BLENDFACTOR_SRC_ALPHA, + SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + SDL_GPU_BLENDFACTOR_DST_ALPHA, + SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_ALPHA, + SDL_GPU_BLENDFACTOR_CONSTANT_COLOR, + SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR, + SDL_GPU_BLENDFACTOR_SRC_ALPHA_SATURATE +}; + +static const char *vals_SDL_GPUBlendFactor[] = { + "invalid", + "zero", + "one", + "src_color", + "one_minus_src_color", + "dst_color", + "one_minus_dst_color", + "src_alpha", + "one_minus_src_alpha", + "dst_alpha", + "one_minus_dst_alpha", + "constant_color", + "one_minus_constant_color", + "src_alpha_saturate" +}; + +JS2ENUM(SDL_GPUBlendFactor, rets_SDL_GPUBlendFactor, vals_SDL_GPUBlendFactor) + +// GPU FUNCTIONS + +JSC_CCALL(SDL_Window_make_gpu, + if (global_gpu) + return JS_ThrowReferenceError(js, "A gpu has already been created somewhere."); + const char *name = JS_ToCString(js,argv[1]); + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING, name); + JS_FreeCString(js,name); + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, JS_ToBool(js,argv[0])); + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, 1); + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN, 1); + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN, 1); + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, 1); + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN, 1); + + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN, 1); + + SDL_GPUDevice *gpu = SDL_CreateGPUDeviceWithProperties(props); + SDL_DestroyProperties(props); + + if (!gpu) return JS_ThrowReferenceError(js, "Could not create GPU device! %s", SDL_GetError()); + + global_gpu = gpu; + return SDL_GPUDevice2js(js,gpu); +) + +JSC_CCALL(gpu_claim_window, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + SDL_Window *w = js2SDL_Window(js,argv[0]); + if (!SDL_ClaimWindowForGPUDevice(gpu, w)) + return JS_ThrowReferenceError(js, "%s", SDL_GetError()); +) + +JSC_CCALL(gpu_set_swapchain, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + SDL_Window *w = js2SDL_Window(js,argv[0]); + SDL_GPUSwapchainComposition comp = js2SDL_GPUSwapchainComposition(js,argv[1]); + if (!SDL_SetGPUSwapchainParameters(gpu, w, comp, SDL_GPU_PRESENTMODE_VSYNC)) + return JS_ThrowReferenceError(js, "%s", SDL_GetError()); +) + +JSC_CCALL(gpu_driver, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + return JS_NewString(js, SDL_GetGPUDeviceDriver(gpu)); +) + +JSC_CCALL(gpu_shader_format, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + SDL_GPUShaderFormat format = SDL_GetGPUShaderFormats(gpu); + switch(format) { + case SDL_GPU_SHADERFORMAT_SPIRV: return JS_NewString(js, "spirv"); + case SDL_GPU_SHADERFORMAT_DXBC: return JS_NewString(js, "dxbc"); + case SDL_GPU_SHADERFORMAT_DXIL: return JS_NewString(js, "dxil"); + case SDL_GPU_SHADERFORMAT_MSL: return JS_NewString(js, "msl"); + case SDL_GPU_SHADERFORMAT_METALLIB: return JS_NewString(js, "metallib"); + } + return JS_NewString(js, "unknown"); +) + +JSC_CCALL(gpu_acquire_cmd_buffer, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + SDL_GPUCommandBuffer *cmd = SDL_AcquireGPUCommandBuffer(gpu); + if (!cmd) return JS_ThrowReferenceError(js, "%s", SDL_GetError()); + return SDL_GPUCommandBuffer2js(js,cmd); +) + +JSC_CCALL(gpu_wait_for_fences, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + int fencecount = js2number(js,argv[1]); + SDL_GPUFence **fences = malloc(sizeof(SDL_GPUFence*)*fencecount); + for (int i = 0; i < fencecount; i++) { + JSValue fenceval = JS_GetPropertyUint32(js,argv[0],i); + fences[i] = js2SDL_GPUFence(js,fenceval); + JS_FreeValue(js,fenceval); + } + int ok = SDL_WaitForGPUFences(gpu,1,fences,fencecount); + free(fences); + if (!ok) return JS_ThrowReferenceError(js, "%s", SDL_GetError()); +) + +JSC_CCALL(gpu_query_fence, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + SDL_GPUFence *fence = js2SDL_GPUFence(js,argv[0]); + return JS_NewBool(js, SDL_QueryGPUFence(gpu,fence)); +) + +// Function array for SDL_GPUDevice +static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = { + MIST_FUNC_DEF(gpu, claim_window, 1), + MIST_FUNC_DEF(gpu, set_swapchain, 2), + MIST_FUNC_DEF(gpu, driver, 0), + MIST_FUNC_DEF(gpu, acquire_cmd_buffer, 0), + MIST_FUNC_DEF(gpu, wait_for_fences, 2), + MIST_FUNC_DEF(gpu, query_fence, 1), + MIST_FUNC_DEF(gpu, shader_format, 0), +}; + +// Function array for SDL_Window GPU functions +static const JSCFunctionListEntry js_SDL_Window_gpu_funcs[] = { + MIST_FUNC_DEF(SDL_Window, make_gpu, 2), +}; + +JSC_SCALL(texture_name, + SDL_GPUTexture *texture = js2SDL_GPUTexture(js,self); + SDL_GPUDevice *gpu; + JS_GETPROP(js,gpu,self,gpu,SDL_GPUDevice) + SDL_SetGPUTextureName(gpu,texture,str); +) + +static const JSCFunctionListEntry js_SDL_GPUTexture_funcs[] = { + MIST_FUNC_DEF(texture, name, 1), +}; + +// Function to register module - GPU classes are registered in main FFI loading +JSValue js_sdl_gpu_use(JSContext *js) +{ + // GPU classes are registered via the main FFI loading system + // This module doesn't export its own functions, it just provides class definitions + return JS_UNDEFINED; +} diff --git a/source/qjs_sdl_gpu.h b/source/qjs_sdl_gpu.h new file mode 100644 index 00000000..2bba77c8 --- /dev/null +++ b/source/qjs_sdl_gpu.h @@ -0,0 +1,38 @@ +#ifndef QJS_SDL_GPU_H +#define QJS_SDL_GPU_H + +#include "jsffi.h" +#include "sprite.h" + +// Forward declarations for sprite sorting functions +int sort_sprite(const sprite *a, const sprite *b); +int sort_sprite_backtofront(const sprite *a, const sprite *b); +int sort_sprite_fronttoback(const sprite *a, const sprite *b); +int sort_sprite_texture(const sprite *a, const sprite *b); + +// Buffer check result structure +typedef struct { + JSValue val; + void *ptr; + size_t size; + int need_new; +} BufferCheckResult; + +// Function for buffer management +BufferCheckResult get_or_extend_buffer( + JSContext *js, + JSValue old_mesh, + const char *prop, + size_t needed_size, + int type, + int elements_per_item, + int copy, + int index +); + +// Base quad for sprite rendering +extern HMM_Vec3 base_quad[4]; + +JSValue js_sdl_gpu_use(JSContext *js); + +#endif diff --git a/source/qjs_sprite.c b/source/qjs_sprite.c new file mode 100644 index 00000000..443d87b2 --- /dev/null +++ b/source/qjs_sprite.c @@ -0,0 +1,64 @@ +#include "qjs_sprite.h" +#include "jsffi.h" +#include "qjs_macros.h" + +#include "sprite.h" +#include "HandmadeMath.h" + +// Sprite class definitions +static JSClassID js_sprite_id; +static void js_sprite_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { + sprite *sp = JS_GetOpaque(val, js_sprite_id); + if (!sp) return; + JS_MarkValue(rt, sp->image, mark_func); +} + +// Class definition for sprite with mark function for GC +QJSCLASSMARK(sprite,) + +// SPRITE GETTER/SETTER FUNCTIONS +JSC_GETSET(sprite, pos, vec2) +JSC_GETSET(sprite, center, vec2) +JSC_GETSET(sprite, layer, number) +JSC_GETSET(sprite, color, color) +JSC_GETSET(sprite, skew, vec2) +JSC_GETSET(sprite, scale, vec2) +JSC_GETSET(sprite, rotation, number) + +// SPRITE ACTION FUNCTIONS + +JSC_CCALL(sprite_move, + sprite *sp = js2sprite(js,self); + HMM_Vec2 mv = js2vec2(js,argv[0]); + sp->pos.x += mv.x; + sp->pos.y += mv.y; +) + +JSC_CCALL(sprite_set_affine, + sprite *sp = js2sprite(js,self); + sprite_apply(sp); +) + +JSC_CCALL(sprite_set_image, + sprite *sp = js2sprite(js,self); + if (!JS_IsUndefined(sp->image)) + JS_FreeValue(js,sp->image); + sp->image = JS_DupValue(js, argv[0]); +) + +static const JSCFunctionListEntry js_sprite_funcs[] = { + MIST_FUNC_DEF(sprite, set_affine, 0), + MIST_FUNC_DEF(sprite, set_image, 1), + MIST_FUNC_DEF(sprite, move, 1), + JS_CGETSET_DEF("pos", js_sprite_get_pos, js_sprite_set_pos), + JS_CGETSET_DEF("scale", js_sprite_get_scale, js_sprite_set_scale), + JS_CGETSET_DEF("skew", js_sprite_get_skew, js_sprite_set_skew), + JS_CGETSET_DEF("layer", js_sprite_get_layer, js_sprite_set_layer), + JS_CGETSET_DEF("color", js_sprite_get_color, js_sprite_set_color), + JS_CGETSET_DEF("center", js_sprite_get_center, js_sprite_set_center), + JS_CGETSET_DEF("rotation", js_sprite_get_rotation, js_sprite_set_rotation), +}; + +// Note: Like transform, sprite doesn't use MISTUSE because sprite is a C type created via os.make_sprite() +// The sprite functions are registered as methods on the sprite class prototype +// This would be handled in the main FFI loading where QJSCLASSPREP_FUNCS(sprite) is called \ No newline at end of file diff --git a/source/qjs_sprite.h b/source/qjs_sprite.h new file mode 100644 index 00000000..1bda2372 --- /dev/null +++ b/source/qjs_sprite.h @@ -0,0 +1,12 @@ +#ifndef QJS_SPRITE_H +#define QJS_SPRITE_H + +#include "quickjs.h" + +#include "sprite.h" + +JSValue js_sprite_use(JSContext *ctx); + +sprite *js2sprite(JSContext *js, JSValue v); + +#endif /* QJS_SPRITE_H */ diff --git a/source/qjs_transform.c b/source/qjs_transform.c new file mode 100644 index 00000000..6b58a7f1 --- /dev/null +++ b/source/qjs_transform.c @@ -0,0 +1,212 @@ +#include "qjs_transform.h" +#include "jsffi.h" +#include "qjs_macros.h" + +#include "transform.h" +#include "HandmadeMath.h" +#include "prosperon.h" + +// Transform class definitions +JSClassID js_transform_id; +static void js_transform_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { + transform *t = JS_GetOpaque(val, js_transform_id); + if (!t) return; + // Mark the JSValue references stored in your struct + JS_MarkValue(rt, t->change_hook, mark_func); + JS_MarkValue(rt, t->jsparent, mark_func); + // Mark the array elements + for (int i = 0; i < arrlen(t->jschildren); i++) + JS_MarkValue(rt, t->jschildren[i], mark_func); +} + +QJSCLASSMARK_EXTERN(transform,) + +// TRANSFORM GETTER/SETTER FUNCTIONS +JSC_GETSET_APPLY(transform, pos, vec3) +JSC_GETSET_APPLY(transform, scale, vec3) +JSC_GETSET_APPLY(transform, rotation, quat) + +static JSValue js_transform_get_change_hook(JSContext *js, JSValueConst self) +{ + transform *t = js2transform(js,self); + return JS_DupValue(js,t->change_hook); +} + +static JSValue js_transform_set_change_hook(JSContext *js, JSValueConst self, JSValue v) +{ + transform *t = js2transform(js,self); + if (!JS_IsUndefined(v) && !JS_IsFunction(js,v)) return JS_ThrowReferenceError(js, "Hook must be a function."); + JS_FreeValue(js,t->change_hook); + t->change_hook = JS_DupValue(js,v); + return JS_UNDEFINED; +} + +static JSValue js_transform_get_parent(JSContext *js, JSValueConst self) +{ + transform *t = js2transform(js,self); + if (t->parent) return JS_DupValue(js,t->jsparent); + return JS_UNDEFINED; +} + +static JSValue js_transform_set_parent(JSContext *js, JSValueConst self, JSValue v) +{ + transform *p = js2transform(js,v); + if (!JS_IsUndefined(v) && !p) + return JS_ThrowReferenceError(js,"Parent must be another transform."); + + transform *t = js2transform(js,self); + + if (t == p) + return JS_ThrowReferenceError(js, "A transform cannot be its own parent."); + + if (t->parent) { + transform *cur_parent = t->parent; + JS_FreeValue(js,t->jsparent); + t->jsparent = JS_UNDEFINED; + + for (int i = 0; i < arrlen(cur_parent->children); i++) { + if (cur_parent->children[i] == t) { + arrdelswap(cur_parent->children,i); + break; + } + } + + for (int i = 0; i < arrlen(cur_parent->jschildren); i++) { + if (JS_SameValue(js,cur_parent->jschildren[i],self)) { + JS_FreeValue(js,cur_parent->jschildren[i]); + arrdelswap(cur_parent->jschildren,i); + break; + } + } + } + + t->parent = p; + t->jsparent = JS_DupValue(js,v); + + if (p) { + arrput(p->children, t); + JSValue child = JS_DupValue(js,self); + arrput(p->jschildren,child); + } + + transform_apply(t); + + return JS_UNDEFINED; +} + +// TRANSFORM ACTION FUNCTIONS + +JSC_CCALL(transform_move, + transform *t = js2transform(js,self); + transform_move(t, js2vec3(js,argv[0])); +) + +JSC_CCALL(transform_lookat, + HMM_Vec3 point = js2vec3(js,argv[0]); + transform *go = js2transform(js,self); + HMM_Mat4 m = HMM_LookAt_RH(go->pos, point, vUP); + go->rotation = HMM_M4ToQ_RH(m); + transform_apply(go); +) + +JSC_CCALL(transform_rotate, + HMM_Vec3 axis = js2vec3(js,argv[0]); + transform *t = js2transform(js,self); + HMM_Quat rot = HMM_QFromAxisAngle_RH(axis, js2angle(js,argv[1])); + t->rotation = HMM_MulQ(t->rotation,rot); + transform_apply(t); +) + +JSC_CCALL(transform_angle, + HMM_Vec3 axis = js2vec3(js,argv[0]); + transform *t = js2transform(js,self); + if (axis.x) return angle2js(js,HMM_Q_Roll(t->rotation)); + if (axis.y) return angle2js(js,HMM_Q_Pitch(t->rotation)); + if (axis.z) return angle2js(js,HMM_Q_Yaw(t->rotation)); + return angle2js(js,0); +) + +JSC_CCALL(transform_direction, + transform *t = js2transform(js,self); + return vec32js(js, HMM_QVRot(js2vec3(js,argv[0]), t->rotation)); +) + +JSC_CCALL(transform_phys2d, + transform *t = js2transform(js,self); + HMM_Vec2 v = js2vec2(js,argv[0]); + float av = js2number(js,argv[1]); + float dt = js2number(js,argv[2]); + transform_move(t, (HMM_Vec3){v.x*dt,v.y*dt,0}); + HMM_Quat rot = HMM_QFromAxisAngle_RH((HMM_Vec3){0,0,1}, av*dt); + t->rotation = HMM_MulQ(t->rotation, rot); + transform_apply(t); +) + +JSC_CCALL(transform_unit, + transform *t = js2transform(js,self); + t->pos = v3zero; + t->rotation = QUAT1; + t->scale = v3one; + transform_apply(t); +) + +JSC_CCALL(transform_trs, + transform *t = js2transform(js,self); + t->pos = JS_IsUndefined(argv[0]) ? v3zero : js2vec3(js,argv[0]); + t->rotation = JS_IsUndefined(argv[1]) ? QUAT1 : js2quat(js,argv[1]); + t->scale = JS_IsUndefined(argv[2]) ? v3one : js2vec3(js,argv[2]); + transform_apply(t); +) + +JSC_CCALL(transform_rect, + transform *t = js2transform(js,self); + rect r = js2rect(js,argv[0]); + t->pos = (HMM_Vec3){r.x,r.y,0}; + t->scale = (HMM_Vec3){r.w,r.h,1}; + t->rotation = QUAT1; + transform_apply(t); +) + +JSC_CCALL(transform_array, + transform *t = js2transform(js,self); + HMM_Mat4 m= transform2mat(t); + ret = JS_NewArray(js); + for (int i = 0; i < 16; i++) + JS_SetPropertyUint32(js,ret,i, number2js(js,m.em[i])); +) + +JSC_CCALL(transform_torect, + transform *t = js2transform(js,self); + return rect2js(js,transform2rect(t)); +) + +JSC_CCALL(transform_children, + transform *t = js2transform(js,self); + ret = JS_NewArray(js); + for (int i = 0; i < arrlen(t->jschildren); i++) + JS_SetPropertyUint32(js,ret,i,JS_DupValue(js,t->jschildren[i])); +) + +static const JSCFunctionListEntry js_transform_funcs[] = { + CGETSET_ADD(transform, pos), + CGETSET_ADD(transform, scale), + CGETSET_ADD(transform, rotation), + CGETSET_ADD(transform, parent), + CGETSET_ADD(transform, change_hook), + MIST_FUNC_DEF(transform, trs, 3), + MIST_FUNC_DEF(transform, phys2d, 3), + MIST_FUNC_DEF(transform, move, 1), + MIST_FUNC_DEF(transform, rotate, 2), + MIST_FUNC_DEF(transform, angle, 1), + MIST_FUNC_DEF(transform, lookat, 1), + MIST_FUNC_DEF(transform, direction, 1), + MIST_FUNC_DEF(transform, unit, 0), + MIST_FUNC_DEF(transform, rect, 1), + MIST_FUNC_DEF(transform, array, 0), + MIST_FUNC_DEF(transform, torect, 0), + MIST_FUNC_DEF(transform, children, 0), +}; + +// Note: Transform module doesn't use MISTUSE because transform is a C type that's created via os.make_transform() +// The transform functions are registered as methods on the transform class prototype +// This would be handled in the main FFI loading where QJSCLASSPREP_FUNCS(transform) is called diff --git a/source/qjs_transform.h b/source/qjs_transform.h new file mode 100644 index 00000000..606cc433 --- /dev/null +++ b/source/qjs_transform.h @@ -0,0 +1,8 @@ +#ifndef QJS_TRANSFORM_H +#define QJS_TRANSFORM_H + +#include "quickjs.h" + +JSValue js_transform_use(JSContext *ctx); + +#endif /* QJS_TRANSFORM_H */