Files
cell-sdl3/gpu.c
2025-12-11 10:39:24 -06:00

1725 lines
65 KiB
C

#include "sdl.h"
#include "quickjs.h"
#include <SDL3/SDL_gpu.h>
#include "cell.h"
// Macro for GPU wrapper classes that need cleanup
#define QJSCLASSGPUWRAPPER(WRAPPERTYPE, SDLTYPE) \
typedef struct { \
SDL_GPUDevice *device; \
JSValue js_device; \
SDL_##SDLTYPE *type; \
} WRAPPERTYPE; \
JSClassID js_SDL_##SDLTYPE##_id; \
static void js_SDL_##SDLTYPE##_finalizer(JSRuntime *rt, JSValue val) { \
WRAPPERTYPE *wrapper = JS_GetOpaque(val, js_SDL_##SDLTYPE##_id); \
JS_FreeValueRT(rt, wrapper->js_device); \
if (wrapper && wrapper->device && wrapper->type) \
SDL_Release##SDLTYPE(wrapper->device, wrapper->type); \
free(wrapper); \
} \
static JSClassDef js_SDL_##SDLTYPE##_class = { \
.class_name = #SDLTYPE, \
.finalizer = js_SDL_##SDLTYPE##_finalizer, \
}; \
SDL_##SDLTYPE *js2SDL_##SDLTYPE(JSContext *js, JSValue val) { \
if (JS_GetClassID(val) != js_SDL_##SDLTYPE##_id) return NULL; \
WRAPPERTYPE *wrapper = JS_GetOpaque(val, js_SDL_##SDLTYPE##_id); \
return wrapper ? wrapper->type : NULL; \
} \
JSValue SDL_##SDLTYPE##2js(JSContext *js, JSValue device, SDL_##SDLTYPE *member) { \
WRAPPERTYPE *wrapper = malloc(sizeof(WRAPPERTYPE)); \
wrapper->js_device = JS_DupValue(js,device); \
wrapper->device = js2SDL_GPUDevice(js, device); \
wrapper->type = member; \
JSValue j = JS_NewObjectClass(js, js_SDL_##SDLTYPE##_id); \
JS_SetOpaque(j, wrapper); \
return j; \
}
// Simple string conversion helper
const char *js2cstring(JSContext *js, JSValue v)
{
return JS_ToCString(js, v);
}
// GPU Free functions
void SDL_GPUDevice_free(JSRuntime *rt, SDL_GPUDevice *d)
{
SDL_DestroyGPUDevice(d);
}
void SDL_GPUCommandBuffer_free(JSRuntime *rt, void *w)
{
}
void gpu_command_buffer_wrapper_free(JSRuntime *rt, void *w)
{
}
// Moved to jsffi.c - extern declaration
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
QJSCLASS(SDL_GPUDevice,)
QJSCLASSGPUWRAPPER(gpu_buffer_wrapper, GPUBuffer)
QJSCLASSGPUWRAPPER(gpu_compute_pipeline_wrapper, GPUComputePipeline)
QJSCLASSGPUWRAPPER(gpu_graphics_pipeline_wrapper, GPUGraphicsPipeline)
QJSCLASSGPUWRAPPER(gpu_sampler_wrapper, GPUSampler)
QJSCLASSGPUWRAPPER(gpu_shader_wrapper, GPUShader)
QJSCLASSGPUWRAPPER(gpu_texture_wrapper, GPUTexture)
QJSCLASSGPUWRAPPER(gpu_transfer_buffer_wrapper, GPUTransferBuffer)
QJSCLASSGPUWRAPPER(gpu_fence_wrapper, GPUFence)
QJSCLASS(SDL_GPUCommandBuffer,)
QJSCLASS(SDL_GPUComputePass,)
QJSCLASS(SDL_GPUCopyPass,)
QJSCLASS(SDL_GPURenderPass,)
// GPU type conversion functions
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 SDL_GPU_SAMPLECOUNT_1;
}
// X-macro enum definition system
#define ENUM_MAPPING_TABLE(ENUM) \
static const struct { int value; const char *name; } ENUM##_mapping[]
#define JS2ENUM(NAME) \
int js2##NAME(JSContext *js, JSValue v) { \
if (JS_IsNull(v)) return 0; \
const char *str = JS_ToCString(js, v); \
if (!str) return 0; \
for(int i = 0; i < sizeof(NAME##_mapping)/sizeof(NAME##_mapping[0]); i++) \
if(!strcmp(NAME##_mapping[i].name, str)) { \
JS_FreeCString(js, str); \
return NAME##_mapping[i].value; \
} \
JS_FreeCString(js, str); \
return 0; \
} \
JSValue NAME##2js(JSContext *js, int enumval) { \
for(int i = 0; i < sizeof(NAME##_mapping)/sizeof(NAME##_mapping[0]); i++) \
if(NAME##_mapping[i].value == enumval) \
return JS_NewString(js, NAME##_mapping[i].name); \
return JS_NULL; \
}
// Enum conversion tables using x-macro technique
ENUM_MAPPING_TABLE(SDL_GPUSwapchainComposition) = {
{SDL_GPU_SWAPCHAINCOMPOSITION_SDR, "sdr"},
{SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR, "linear"},
{SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR, "hdr"},
{SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084, "hdr10"}
};
JS2ENUM(SDL_GPUSwapchainComposition)
ENUM_MAPPING_TABLE(SDL_FlipMode) = {
{SDL_FLIP_NONE, "none"},
{SDL_FLIP_HORIZONTAL, "horizontal"},
{SDL_FLIP_VERTICAL, "vertical"}
};
JS2ENUM(SDL_FlipMode)
SDL_FColor js2SDL_FColor(JSContext *js, JSValue v)
{
colorf color = js2color(js,v);
return (SDL_FColor){color.x, color.y, color.z, color.w};
}
ENUM_MAPPING_TABLE(SDL_GPUBlendFactor) = {
{SDL_GPU_BLENDFACTOR_INVALID, "invalid"},
{SDL_GPU_BLENDFACTOR_ZERO, "zero"},
{SDL_GPU_BLENDFACTOR_ONE, "one"},
{SDL_GPU_BLENDFACTOR_SRC_COLOR, "src_color"},
{SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_COLOR, "one_minus_src_color"},
{SDL_GPU_BLENDFACTOR_DST_COLOR, "dst_color"},
{SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_COLOR, "one_minus_dst_color"},
{SDL_GPU_BLENDFACTOR_SRC_ALPHA, "src_alpha"},
{SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, "one_minus_src_alpha"},
{SDL_GPU_BLENDFACTOR_DST_ALPHA, "dst_alpha"},
{SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_ALPHA, "one_minus_dst_alpha"},
{SDL_GPU_BLENDFACTOR_CONSTANT_COLOR, "constant_color"},
{SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR, "one_minus_constant_color"},
{SDL_GPU_BLENDFACTOR_SRC_ALPHA_SATURATE, "src_alpha_saturate"}
};
JS2ENUM(SDL_GPUBlendFactor)
ENUM_MAPPING_TABLE(SDL_GPUBlendOp) = {
{SDL_GPU_BLENDOP_INVALID, "invalid"},
{SDL_GPU_BLENDOP_ADD, "add"},
{SDL_GPU_BLENDOP_SUBTRACT, "subtract"},
{SDL_GPU_BLENDOP_REVERSE_SUBTRACT, "reverse_subtract"},
{SDL_GPU_BLENDOP_MIN, "min"},
{SDL_GPU_BLENDOP_MAX, "max"}
};
JS2ENUM(SDL_GPUBlendOp)
ENUM_MAPPING_TABLE(SDL_GPUCompareOp) = {
{SDL_GPU_COMPAREOP_INVALID, "invalid"},
{SDL_GPU_COMPAREOP_NEVER, "never"},
{SDL_GPU_COMPAREOP_LESS, "less"},
{SDL_GPU_COMPAREOP_EQUAL, "equal"},
{SDL_GPU_COMPAREOP_LESS_OR_EQUAL, "less_or_equal"},
{SDL_GPU_COMPAREOP_GREATER, "greater"},
{SDL_GPU_COMPAREOP_NOT_EQUAL, "not_equal"},
{SDL_GPU_COMPAREOP_GREATER_OR_EQUAL, "greater_or_equal"},
{SDL_GPU_COMPAREOP_ALWAYS, "always"}
};
JS2ENUM(SDL_GPUCompareOp)
ENUM_MAPPING_TABLE(SDL_GPUCullMode) = {
{SDL_GPU_CULLMODE_NONE, "none"},
{SDL_GPU_CULLMODE_FRONT, "front"},
{SDL_GPU_CULLMODE_BACK, "back"}
};
JS2ENUM(SDL_GPUCullMode)
ENUM_MAPPING_TABLE(SDL_GPUFillMode) = {
{SDL_GPU_FILLMODE_FILL, "fill"},
{SDL_GPU_FILLMODE_LINE, "line"}
};
JS2ENUM(SDL_GPUFillMode)
ENUM_MAPPING_TABLE(SDL_GPUFilter) = {
{SDL_GPU_FILTER_NEAREST, "nearest"},
{SDL_GPU_FILTER_LINEAR, "linear"}
};
JS2ENUM(SDL_GPUFilter)
ENUM_MAPPING_TABLE(SDL_GPUFrontFace) = {
{SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE, "counter_clockwise"},
{SDL_GPU_FRONTFACE_CLOCKWISE, "clockwise"}
};
JS2ENUM(SDL_GPUFrontFace)
ENUM_MAPPING_TABLE(SDL_GPULoadOp) = {
{SDL_GPU_LOADOP_LOAD, "load"},
{SDL_GPU_LOADOP_CLEAR, "clear"},
{SDL_GPU_LOADOP_DONT_CARE, "dont_care"}
};
JS2ENUM(SDL_GPULoadOp)
ENUM_MAPPING_TABLE(SDL_GPUPresentMode) = {
{SDL_GPU_PRESENTMODE_VSYNC, "vsync"},
{SDL_GPU_PRESENTMODE_IMMEDIATE, "immediate"},
{SDL_GPU_PRESENTMODE_MAILBOX, "mailbox"}
};
JS2ENUM(SDL_GPUPresentMode)
ENUM_MAPPING_TABLE(SDL_GPUPrimitiveType) = {
{SDL_GPU_PRIMITIVETYPE_TRIANGLELIST, "triangle"},
{SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP, "trianglestrip"},
{SDL_GPU_PRIMITIVETYPE_LINELIST, "line"},
{SDL_GPU_PRIMITIVETYPE_LINESTRIP, "linestrip"},
{SDL_GPU_PRIMITIVETYPE_POINTLIST, "point"}
};
JS2ENUM(SDL_GPUPrimitiveType)
ENUM_MAPPING_TABLE(SDL_GPUSamplerAddressMode) = {
{SDL_GPU_SAMPLERADDRESSMODE_REPEAT, "repeat"},
{SDL_GPU_SAMPLERADDRESSMODE_MIRRORED_REPEAT, "mirrored_repeat"},
{SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, "clamp"}
};
JS2ENUM(SDL_GPUSamplerAddressMode)
ENUM_MAPPING_TABLE(SDL_GPUSamplerMipmapMode) = {
{SDL_GPU_SAMPLERMIPMAPMODE_NEAREST, "nearest"},
{SDL_GPU_SAMPLERMIPMAPMODE_LINEAR, "linear"}
};
JS2ENUM(SDL_GPUSamplerMipmapMode)
ENUM_MAPPING_TABLE(SDL_GPUStencilOp) = {
{SDL_GPU_STENCILOP_INVALID, "invalid"},
{SDL_GPU_STENCILOP_KEEP, "keep"},
{SDL_GPU_STENCILOP_ZERO, "zero"},
{SDL_GPU_STENCILOP_REPLACE, "replace"},
{SDL_GPU_STENCILOP_INCREMENT_AND_CLAMP, "increment_and_clamp"},
{SDL_GPU_STENCILOP_DECREMENT_AND_CLAMP, "decrement_and_clamp"},
{SDL_GPU_STENCILOP_INVERT, "invert"},
{SDL_GPU_STENCILOP_INCREMENT_AND_WRAP, "increment_and_wrap"},
{SDL_GPU_STENCILOP_DECREMENT_AND_WRAP, "decrement_and_wrap"}
};
JS2ENUM(SDL_GPUStencilOp)
ENUM_MAPPING_TABLE(SDL_GPUTextureType) = {
{SDL_GPU_TEXTURETYPE_2D, "2d"},
{SDL_GPU_TEXTURETYPE_2D_ARRAY, "2d array"},
{SDL_GPU_TEXTURETYPE_3D, "3d"},
{SDL_GPU_TEXTURETYPE_CUBE, "cube"},
{SDL_GPU_TEXTURETYPE_CUBE_ARRAY, "cube array"}
};
JS2ENUM(SDL_GPUTextureType)
ENUM_MAPPING_TABLE(SDL_GPUStoreOp) = {
{SDL_GPU_STOREOP_STORE, "store"},
{SDL_GPU_STOREOP_DONT_CARE, "dont_care"},
{SDL_GPU_STOREOP_RESOLVE, "resolve"},
{SDL_GPU_STOREOP_RESOLVE_AND_STORE, "resolve_and_store"}
};
JS2ENUM(SDL_GPUStoreOp)
ENUM_MAPPING_TABLE(SDL_GPUTextureFormat) = {
{SDL_GPU_TEXTUREFORMAT_INVALID, "invalid"},
{SDL_GPU_TEXTUREFORMAT_A8_UNORM, "a8"},
{SDL_GPU_TEXTUREFORMAT_R8_UNORM, "r8"},
{SDL_GPU_TEXTUREFORMAT_R8G8_UNORM, "rg8"},
{SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, "rgba8"},
{SDL_GPU_TEXTUREFORMAT_R16_UNORM, "r16"},
{SDL_GPU_TEXTUREFORMAT_R16G16_UNORM, "rg16"},
{SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM, "rgba16"},
{SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM, "r10g10b10a2"},
{SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM, "b5g6r5"},
{SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM, "b5g5r5a1"},
{SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM, "b4g4r4a4"},
{SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, "bgra8"},
{SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM, "bc1"},
{SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM, "bc2"},
{SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM, "bc3"},
{SDL_GPU_TEXTUREFORMAT_BC4_R_UNORM, "bc4"},
{SDL_GPU_TEXTUREFORMAT_BC5_RG_UNORM, "bc5"},
{SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM, "bc7"},
{SDL_GPU_TEXTUREFORMAT_BC6H_RGB_FLOAT, "bc6h float"},
{SDL_GPU_TEXTUREFORMAT_BC6H_RGB_UFLOAT, "bc6h ufloat"},
{SDL_GPU_TEXTUREFORMAT_R8_SNORM, "r8 snorm"},
{SDL_GPU_TEXTUREFORMAT_R8G8_SNORM, "rg8 snorm"},
{SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM, "rgba8 snorm"},
{SDL_GPU_TEXTUREFORMAT_R16_SNORM, "r16 snorm"},
{SDL_GPU_TEXTUREFORMAT_R16G16_SNORM, "rg16 snorm"},
{SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM, "rgba16 snorm"},
{SDL_GPU_TEXTUREFORMAT_R16_FLOAT, "r16 float"},
{SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT, "rg16 float"},
{SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, "rgba16 float"},
{SDL_GPU_TEXTUREFORMAT_R32_FLOAT, "r32 float"},
{SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT, "rg32 float"},
{SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT, "rgba32 float"},
{SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT, "r11g11b10"},
{SDL_GPU_TEXTUREFORMAT_R8_UINT, "r8 uint"},
{SDL_GPU_TEXTUREFORMAT_R8G8_UINT, "rg8 uint"},
{SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT, "rgba8 uint"},
{SDL_GPU_TEXTUREFORMAT_R16_UINT, "r16 uint"},
{SDL_GPU_TEXTUREFORMAT_R16G16_UINT, "rg16 uint"},
{SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT, "rgba16 uint"},
{SDL_GPU_TEXTUREFORMAT_R32_UINT, "r32 uint"},
{SDL_GPU_TEXTUREFORMAT_R32G32_UINT, "rg32 uint"},
{SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT, "rgba32 uint"},
{SDL_GPU_TEXTUREFORMAT_R8_INT, "r8 int"},
{SDL_GPU_TEXTUREFORMAT_R8G8_INT, "rg8 int"},
{SDL_GPU_TEXTUREFORMAT_R8G8B8A8_INT, "rgba8 int"},
{SDL_GPU_TEXTUREFORMAT_R16_INT, "r16 int"},
{SDL_GPU_TEXTUREFORMAT_R16G16_INT, "rg16 int"},
{SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT, "rgba16 int"},
{SDL_GPU_TEXTUREFORMAT_R32_INT, "r32 int"},
{SDL_GPU_TEXTUREFORMAT_R32G32_INT, "rg32 int"},
{SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT, "rgba32 int"},
{SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB, "rgba8 srgb"},
{SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB, "b8g8r8a8 srgb"},
{SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM_SRGB, "bc1 srgb"},
{SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM_SRGB, "bc2 srgb"},
{SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM_SRGB, "bc3 srgb"},
{SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM_SRGB, "bc7 srgb"},
{SDL_GPU_TEXTUREFORMAT_D16_UNORM, "d16"},
{SDL_GPU_TEXTUREFORMAT_D24_UNORM, "d24"},
{SDL_GPU_TEXTUREFORMAT_D32_FLOAT, "d32 float"},
{SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT, "d24 s8"},
{SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT, "d32 float s8"},
{SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM, "astc 4x4"},
{SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM, "astc 5x4"},
{SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM, "astc 5x5"},
{SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM, "astc 6x5"},
{SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM, "astc 6x6"},
{SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM, "astc 8x5"},
{SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM, "astc 8x6"},
{SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM, "astc 8x8"},
{SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM, "astc 10x5"},
{SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM, "astc 10x6"},
{SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM, "astc 10x8"},
{SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM, "astc 10x10"},
{SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM, "astc 12x10"},
{SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM, "astc 12x12"},
{SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM_SRGB, "astc 4x4 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM_SRGB, "astc 5x4 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM_SRGB, "astc 5x5 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM_SRGB, "astc 6x5 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM_SRGB, "astc 6x6 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM_SRGB, "astc 8x5 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM_SRGB, "astc 8x6 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM_SRGB, "astc 8x8 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM_SRGB, "astc 10x5 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM_SRGB, "astc 10x6 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM_SRGB, "astc 10x8 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM_SRGB, "astc 10x10 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM_SRGB, "astc 12x10 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM_SRGB, "astc 12x12 srgb"},
{SDL_GPU_TEXTUREFORMAT_ASTC_4x4_FLOAT, "astc 4x4 float"},
{SDL_GPU_TEXTUREFORMAT_ASTC_5x4_FLOAT, "astc 5x4 float"},
{SDL_GPU_TEXTUREFORMAT_ASTC_5x5_FLOAT, "astc 5x5 float"},
{SDL_GPU_TEXTUREFORMAT_ASTC_6x5_FLOAT, "astc 6x5 float"},
{SDL_GPU_TEXTUREFORMAT_ASTC_6x6_FLOAT, "astc 6x6 float"},
{SDL_GPU_TEXTUREFORMAT_ASTC_8x5_FLOAT, "astc 8x5 float"},
{SDL_GPU_TEXTUREFORMAT_ASTC_8x6_FLOAT, "astc 8x6 float"},
{SDL_GPU_TEXTUREFORMAT_ASTC_8x8_FLOAT, "astc 8x8 float"},
{SDL_GPU_TEXTUREFORMAT_ASTC_10x5_FLOAT, "astc 10x5 float"},
{SDL_GPU_TEXTUREFORMAT_ASTC_10x6_FLOAT, "astc 10x6 float"},
{SDL_GPU_TEXTUREFORMAT_ASTC_10x8_FLOAT, "astc 10x8 float"},
{SDL_GPU_TEXTUREFORMAT_ASTC_10x10_FLOAT, "astc 10x10 float"},
{SDL_GPU_TEXTUREFORMAT_ASTC_12x10_FLOAT, "astc 12x10 float"},
{SDL_GPU_TEXTUREFORMAT_ASTC_12x12_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_color,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;
}
SDL_GPUBufferBinding js2SDL_GPUBufferBinding(JSContext *js, JSValue v)
{
SDL_GPUBufferBinding binding = {0};
JS_GETPROP(js, binding.buffer, v, buffer, SDL_GPUBuffer)
JS_GETPROP(js, binding.offset, v, offset, number)
return binding;
}
JS2ENUM(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,dst_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;
}
ENUM_MAPPING_TABLE(SDL_GPUShaderFormat) = {
{SDL_GPU_SHADERFORMAT_PRIVATE, "private"},
{SDL_GPU_SHADERFORMAT_SPIRV, "spv"},
{SDL_GPU_SHADERFORMAT_DXBC, "dxbc"},
{SDL_GPU_SHADERFORMAT_DXIL, "dxil"},
{SDL_GPU_SHADERFORMAT_MSL, "msl"},
{SDL_GPU_SHADERFORMAT_METALLIB, "metallib"}
};
JS2ENUM(SDL_GPUShaderFormat)
ENUM_MAPPING_TABLE(SDL_GPUVertexInputRate) = {
{SDL_GPU_VERTEXINPUTRATE_VERTEX, "vertex"},
{SDL_GPU_VERTEXINPUTRATE_INSTANCE, "instance"}
};
JS2ENUM(SDL_GPUVertexInputRate)
ENUM_MAPPING_TABLE(SDL_GPUTransferBufferUsage) = {
{SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, "upload"},
{SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD, "download"}
};
JS2ENUM(SDL_GPUTransferBufferUsage)
ENUM_MAPPING_TABLE(SDL_GPUVertexElementFormat) = {
{SDL_GPU_VERTEXELEMENTFORMAT_INVALID, "invalid"},
{SDL_GPU_VERTEXELEMENTFORMAT_INT, "int"},
{SDL_GPU_VERTEXELEMENTFORMAT_INT2, "int2"},
{SDL_GPU_VERTEXELEMENTFORMAT_INT3, "int3"},
{SDL_GPU_VERTEXELEMENTFORMAT_INT4, "int4"},
{SDL_GPU_VERTEXELEMENTFORMAT_UINT, "uint"},
{SDL_GPU_VERTEXELEMENTFORMAT_UINT2, "uint2"},
{SDL_GPU_VERTEXELEMENTFORMAT_UINT3, "uint3"},
{SDL_GPU_VERTEXELEMENTFORMAT_UINT4, "uint4"},
{SDL_GPU_VERTEXELEMENTFORMAT_FLOAT, "float"},
{SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, "float2"},
{SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3, "float3"},
{SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4, "float4"},
{SDL_GPU_VERTEXELEMENTFORMAT_BYTE2, "byte2"},
{SDL_GPU_VERTEXELEMENTFORMAT_BYTE4, "byte4"},
{SDL_GPU_VERTEXELEMENTFORMAT_UBYTE2, "ubyte2"},
{SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4, "ubyte4"},
{SDL_GPU_VERTEXELEMENTFORMAT_BYTE2_NORM, "byte2_norm"},
{SDL_GPU_VERTEXELEMENTFORMAT_BYTE4_NORM, "byte4_norm"},
{SDL_GPU_VERTEXELEMENTFORMAT_UBYTE2_NORM, "ubyte2_norm"},
{SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM, "ubyte4_norm"},
{SDL_GPU_VERTEXELEMENTFORMAT_SHORT2, "short2"},
{SDL_GPU_VERTEXELEMENTFORMAT_SHORT4, "short4"},
{SDL_GPU_VERTEXELEMENTFORMAT_USHORT2, "ushort2"},
{SDL_GPU_VERTEXELEMENTFORMAT_USHORT4, "ushort4"},
{SDL_GPU_VERTEXELEMENTFORMAT_SHORT2_NORM, "short2_norm"},
{SDL_GPU_VERTEXELEMENTFORMAT_SHORT4_NORM, "short4_norm"},
{SDL_GPU_VERTEXELEMENTFORMAT_USHORT2_NORM, "ushort2_norm"},
{SDL_GPU_VERTEXELEMENTFORMAT_USHORT4_NORM, "ushort4_norm"},
{SDL_GPU_VERTEXELEMENTFORMAT_HALF2, "half2"},
{SDL_GPU_VERTEXELEMENTFORMAT_HALF4, "half4"}
};
JS2ENUM(SDL_GPUVertexElementFormat)
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_IsNull(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_IsNull(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_IsNull(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_IsNull(pass_val)) state.pass_op = js2SDL_GPUStencilOp(js, pass_val);
JS_FreeValue(js, pass_val);
return state;
}
JSC_CCALL(gpu_claim_window,
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
SDL_Window *win = js2SDL_Window(js, argv[0]);
if (!SDL_ClaimWindowForGPUDevice(gpu,win))
return JS_ThrowInternalError(js, "couldn't claim window for GPU device: %s", SDL_GetError());
)
JSC_CCALL(gpu_set_swapchain,
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self);
SDL_Window *window = js2SDL_Window(js, argv[0]);
if (!SDL_SetGPUSwapchainParameters(gpu, window, js2SDL_GPUSwapchainComposition(js,argv[1]), js2SDL_GPUPresentMode(js,argv[2])))
return JS_ThrowReferenceError(js, "Could not set: %s\n", SDL_GetError());
)
static JSValue js_gpu_graphics_pipeline_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
if (argc < 2)
return JS_ThrowTypeError(js, "graphics pipeline constructor requires device and config parameters");
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]);
if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device");
JSValue pipe = argv[1];
if (!JS_IsObject(pipe))
return JS_ThrowTypeError(js, "gpu_pipeline argument must be an object");
SDL_GPUGraphicsPipelineCreateInfo info = {0};
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");
Uint32 slot = 0;
JS_ToUint32(js, &slot, slot_val);
JS_FreeValue(js, slot_val);
Uint32 pitch = 0;
JS_ToUint32(js, &pitch, pitch_val);
JS_FreeValue(js, pitch_val);
SDL_GPUVertexInputRate input_rate = js2SDL_GPUVertexInputRate(js, rate_val);
JS_FreeValue(js, rate_val);
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);
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");
Uint32 location = 0;
JS_ToUint32(js, &location, loc_val);
JS_FreeValue(js, loc_val);
Uint32 buffer_slot = 0;
JS_ToUint32(js, &buffer_slot, slot_val);
JS_FreeValue(js, slot_val);
SDL_GPUVertexElementFormat format = js2SDL_GPUVertexElementFormat(js, fmt_val);
JS_FreeValue(js, fmt_val);
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
};
JS_GETPROP(js,info.vertex_shader, pipe, vertex,SDL_GPUShader)
JS_GETPROP(js, info.fragment_shader, pipe, fragment, SDL_GPUShader)
JS_GETPROP(js, info.primitive_type, pipe, primitive, SDL_GPUPrimitiveType)
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)
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);
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: %s", SDL_GetError());
return SDL_GPUGraphicsPipeline2js(js, argv[0], pipeline);
}
// Standalone sampler constructor: new sdl_gpu.sampler(device, config)
static JSValue js_gpu_sampler_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
if (argc < 2) return JS_ThrowTypeError(js, "sampler constructor requires device and config parameters");
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]);
if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device");
SDL_GPUSamplerCreateInfo info = {0};
JSValue sampler = argv[1];
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: %s", SDL_GetError());
return SDL_GPUSampler2js(js, argv[0], sdl_sampler);
}
JSC_CCALL(gpu_driver,
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
ret = JS_NewString(js, SDL_GetGPUDeviceDriver(gpu));
)
// Standalone shader constructor: new sdl_gpu.shader(device, config)
static JSValue js_gpu_shader_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
if (argc < 2 || !JS_IsObject(argv[1]))
return JS_ThrowTypeError(js, "shader constructor requires device and config parameters");
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]);
if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device");
JSValue obj = argv[1];
SDL_GPUShaderCreateInfo info = {0};
JSValue code_val = JS_GetPropertyStr(js, obj, "code");
size_t code_size;
void *code_data = js_get_blob_data(js, &code_size, code_val);
JS_FreeValue(js, code_val);
if (code_data == -1)
return JS_EXCEPTION;
if (!code_data)
return JS_ThrowTypeError(js, "empty code");
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
SDL_GPUShader *shader = SDL_CreateGPUShader(gpu, &info);
JS_FreeCString(js,info.entrypoint);
if (!shader)
return JS_ThrowReferenceError(js, "Unable to create shader: %s", SDL_GetError());
return SDL_GPUShader2js(js, argv[0], 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());
return SDL_GPUCommandBuffer2js(js, cb);
)
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;
)
// Standalone compute pipeline constructor: new sdl_gpu.compute_pipeline(device, config)
static JSValue js_gpu_compute_pipeline_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
if (argc < 2) return JS_ThrowTypeError(js, "compute pipeline constructor requires device and config parameters");
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]);
if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device");
SDL_GPUComputePipelineCreateInfo info = {0};
JSValue pipe = argv[1];
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_get_blob_data(js,&info.code_size, shader);
JS_FreeValue(js,shader);
if (info.code == -1)
return JS_EXCEPTION;
if (!info.code)
return JS_ThrowTypeError(js, "compute pipeline shader must be a valid blob");
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, argv[0], pipeline);
}
int JS_GETBOOL(JSContext *js, JSValue obj, const char *prop)
{
JSValue v = JS_GetPropertyStr(js,obj,prop);
int ret = JS_ToBool(js,v);
JS_FreeValue(js,v);
return ret;
}
// Standalone buffer constructor: new sdl_gpu.buffer(device, config)
static JSValue js_gpu_buffer_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
if (argc < 2) return JS_ThrowTypeError(js, "buffer constructor requires device and config parameters");
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]);
if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device");
SDL_GPUBufferCreateInfo info = {0};
JSValue config = argv[1];
// Get size - this is required
JS_GETPROP(js, info.size, config, size, number)
if (info.size == 0) return JS_ThrowTypeError(js, "Buffer size must be greater than 0");
// Parse usage flags
if (JS_GETBOOL(js, config, "vertex"))
info.usage |= SDL_GPU_BUFFERUSAGE_VERTEX;
if (JS_GETBOOL(js, config, "index"))
info.usage |= SDL_GPU_BUFFERUSAGE_INDEX;
if (JS_GETBOOL(js, config, "indirect"))
info.usage |= SDL_GPU_BUFFERUSAGE_INDIRECT;
if (JS_GETBOOL(js, config, "graphics_storage_read"))
info.usage |= SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ;
if (JS_GETBOOL(js, config, "compute_storage_read"))
info.usage |= SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ;
if (JS_GETBOOL(js, config, "compute_storage_write"))
info.usage |= SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE;
if (info.usage == 0) return JS_ThrowTypeError(js, "Buffer must have at least one usage flag");
SDL_GPUBuffer *buffer = SDL_CreateGPUBuffer(gpu, &info);
if (!buffer) return JS_ThrowReferenceError(js, "Unable to create buffer: %s", SDL_GetError());
return SDL_GPUBuffer2js(js, argv[0], buffer);
}
static JSValue js_gpu_transfer_buffer_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
if (argc < 2) return JS_ThrowTypeError(js, "transfer_buffer constructor requires device and config parameters");
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]);
if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device");
SDL_GPUTransferBufferCreateInfo info = {0};
JSValue config = argv[1];
// Get size - this is required
JS_GETPROP(js, info.size, config, size, number)
if (info.size == 0) return JS_ThrowTypeError(js, "Transfer buffer size must be greater than 0");
JS_GETPROP(js, info.usage, config, usage, SDL_GPUTransferBufferUsage)
SDL_GPUTransferBuffer *buffer = SDL_CreateGPUTransferBuffer(gpu, &info);
if (!buffer) return JS_ThrowReferenceError(js, "Unable to create transfer buffer: %s", SDL_GetError());
return SDL_GPUTransferBuffer2js(js, argv[0], buffer);
}
// Standalone texture constructor: new sdl_gpu.texture(device, config)
static JSValue js_gpu_texture_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
if (argc < 2) return JS_ThrowTypeError(js, "texture constructor requires device and config parameters");
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]);
if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device");
SDL_GPUTextureCreateInfo info = {0};
JSValue fmt = argv[1];
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)
// Parse usage flags
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, argv[0], 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, (vec2){info.width, info.height}));
return jstex;
}
JSC_CCALL(gpu_swapchain_format,
SDL_GPUDevice *dev = js2SDL_GPUDevice(js, self);
SDL_Window *win = js2SDL_Window(js, argv[0]);
return SDL_GPUTextureFormat2js(js, SDL_GetGPUSwapchainTextureFormat(dev, win));
)
static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = {
MIST_FUNC_DEF(gpu, claim_window, 1),
MIST_FUNC_DEF(gpu, set_swapchain, 3),
MIST_FUNC_DEF(gpu, swapchain_format, 1),
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),
};
JSC_CCALL(renderpass_bind_pipeline,
SDL_GPURenderPass *r = js2SDL_GPURenderPass(js,self);
if (!r) return JS_ThrowInternalError(js, "Invalid render pass");
SDL_GPUGraphicsPipeline *pipe = js2SDL_GPUGraphicsPipeline(js,argv[0]);
if (!pipe) return JS_ThrowInternalError(js, "Invalid graphics pipeline");
SDL_BindGPUGraphicsPipeline(r,pipe);
)
JSC_CCALL(renderpass_draw_indexed,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
if (!pass) return JS_ThrowInternalError(js, "Invalid render pass");
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);
if (!pass) return JS_ThrowInternalError(js, "Invalid render pass");
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] = js2SDL_GPUBufferBinding(js, 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 = js2SDL_GPUBufferBinding(js, argv[0]);
int elen = js2number(js,argv[1]);
SDL_BindGPUIndexBuffer(pass,&bind,elen == 16 ? SDL_GPU_INDEXELEMENTSIZE_16BIT : SDL_GPU_INDEXELEMENTSIZE_32BIT);
)
JSC_CCALL(renderpass_end,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
if (!pass) return JS_ThrowInternalError(js, "Invalid render pass");
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);
binds[i] = js2SDL_GPUTextureSamplerBinding(js, val);
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);
)
JSC_CCALL(renderpass_bind_vertex_samplers,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
int first_slot = js2number(js,argv[0]);
JSValue arr = argv[1];
int num = JS_ArrayLength(js,arr);
SDL_GPUTextureSamplerBinding binds[num];
for (int i = 0; i < num; i++) {
JSValue val = JS_GetPropertyUint32(js,arr,i);
binds[i] = js2SDL_GPUTextureSamplerBinding(js,val);
JS_FreeValue(js,val);
}
SDL_BindGPUVertexSamplers(pass, first_slot, binds, num);
)
JSC_CCALL(renderpass_bind_fragment_samplers,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
int first_slot = js2number(js,argv[0]);
JSValue arr = argv[1];
int num = JS_ArrayLength(js,arr);
SDL_GPUTextureSamplerBinding binds[num];
for (int i = 0; i < num; i++) {
JSValue val = JS_GetPropertyUint32(js,arr,i);
binds[i] = js2SDL_GPUTextureSamplerBinding(js,val);
JS_FreeValue(js,val);
}
SDL_BindGPUFragmentSamplers(pass, first_slot, binds, num);
)
JSC_CCALL(renderpass_bind_vertex_buffers,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
int first_slot = js2number(js,argv[0]);
JSValue arr = argv[1];
int num = JS_ArrayLength(js,arr);
SDL_GPUBufferBinding binds[num];
for (int i = 0; i < num; i++) {
JSValue val = JS_GetPropertyUint32(js,arr,i);
JS_GETPROP(js, binds[i].buffer, val, buffer, SDL_GPUBuffer)
JS_GETPROP(js, binds[i].offset, val, offset, number)
JS_FreeValue(js,val);
}
SDL_BindGPUVertexBuffers(pass, first_slot, binds, num);
)
JSC_CCALL(renderpass_set_stencil_reference,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js, self);
Uint8 ref = js2number(js,argv[0]);
SDL_SetGPUStencilReference(pass, ref);
)
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, set_stencil_reference, 1),
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_vertex_samplers, 2),
MIST_FUNC_DEF(renderpass, bind_fragment_samplers, 2),
MIST_FUNC_DEF(renderpass, bind_vertex_buffers, 2),
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_IsNull(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: %s", SDL_GetError());
return SDL_GPURenderPass2js(js, pass);
)
JSC_CCALL(cmd_copy_pass,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
SDL_GPUCopyPass *pass = SDL_BeginGPUCopyPass(cmds);
if (!pass) return JS_ThrowInternalError(js, "copy_pass: Failed to begin copy pass: %s", SDL_GetError());
return SDL_GPUCopyPass2js(js, pass);
)
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_get_blob_data(js, &buf_size, argv[1]);
if (data == -1)
return JS_EXCEPTION;
if (!data)
return JS_ThrowTypeError(js, "uniform data must be a valid blob");
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_get_blob_data(js, &buf_size, argv[1]);
if (data == -1)
return JS_EXCEPTION;
if (!data)
return JS_ThrowTypeError(js, "uniform data must be a valid blob");
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_get_blob_data(js, &buf_size, argv[1]);
if (data == -1)
return JS_EXCEPTION;
if (!data)
return JS_ThrowTypeError(js, "uniform data must be a valid blob");
SDL_PushGPUComputeUniformData(cmds, slot, data, buf_size);
)
JSC_CCALL(cmd_submit,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js,self);
SDL_SubmitGPUCommandBuffer(cmds);
)
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_swapchain_pass,
SDL_GPUCommandBuffer *cmdbuf = js2SDL_GPUCommandBuffer(js, self);
SDL_Window *window = js2SDL_Window(js, argv[0]);
SDL_GPUTexture* swapchainTexture;
if (!SDL_WaitAndAcquireGPUSwapchainTexture(cmdbuf, window, &swapchainTexture, NULL, NULL)) {
return JS_ThrowReferenceError(js, "WaitAndAcquireGPUSwapchainTexture failed: %s", SDL_GetError());
}
if (swapchainTexture == NULL) {
return JS_ThrowReferenceError(js, "Failed to acquire swapchain texture");
}
SDL_GPUColorTargetInfo colorTargetInfo = { 0 };
colorTargetInfo.texture = swapchainTexture;
colorTargetInfo.clear_color = (SDL_FColor){ 0.0f, 0.0f, 0.0f, 1.0f };
colorTargetInfo.load_op = SDL_GPU_LOADOP_CLEAR;
colorTargetInfo.store_op = SDL_GPU_STOREOP_STORE;
SDL_GPURenderPass* renderPass = SDL_BeginGPURenderPass(cmdbuf, &colorTargetInfo, 1, NULL);
if (!renderPass) {
return JS_ThrowReferenceError(js, "Failed to begin render pass: %s", SDL_GetError());
}
return SDL_GPURenderPass2js(js, renderPass);
)
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);
)
JSC_CCALL(cmd_generate_mipmaps,
SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self);
SDL_GPUTexture *tex = js2SDL_GPUTexture(js,argv[0]);
SDL_GenerateMipmapsForGPUTexture(cmd,tex);
)
static const JSCFunctionListEntry js_SDL_GPUCommandBuffer_funcs[] = {
MIST_FUNC_DEF(cmd, render_pass, 1),
MIST_FUNC_DEF(cmd, copy_pass, 0),
MIST_FUNC_DEF(cmd, compute_pass, 2),
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, generate_mipmaps, 1),
MIST_FUNC_DEF(cmd, submit, 0),
MIST_FUNC_DEF(cmd, cancel, 0),
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),
MIST_FUNC_DEF(cmd, swapchain_pass, 1),
};
JSC_CCALL(copypass_end,
SDL_GPUCopyPass *pass = js2SDL_GPUCopyPass(js,self);
SDL_EndGPUCopyPass(pass);
)
JSC_CCALL(copypass_upload_to_buffer,
SDL_GPUCopyPass *pass = js2SDL_GPUCopyPass(js,self);
JSValue transfer_loc = argv[0];
JSValue buffer_region = argv[1];
SDL_GPUTransferBufferLocation transfer_info = {0};
JS_GETPROP(js, transfer_info.transfer_buffer, transfer_loc, transfer_buffer, SDL_GPUTransferBuffer)
JS_GETPROP(js, transfer_info.offset, transfer_loc, offset, number)
SDL_GPUBufferRegion buffer_info = {0};
JS_GETPROP(js, buffer_info.buffer, buffer_region, buffer, SDL_GPUBuffer)
JS_GETPROP(js, buffer_info.offset, buffer_region, offset, number)
JS_GETPROP(js, buffer_info.size, buffer_region, size, number)
bool cycle = argc > 2 ? JS_ToBool(js, argv[2]) : false;
SDL_UploadToGPUBuffer(pass, &transfer_info, &buffer_info, cycle);
)
JSC_CCALL(copypass_upload_to_texture,
SDL_GPUCopyPass *pass = js2SDL_GPUCopyPass(js,self);
JSValue transfer_info_val = argv[0];
JSValue texture_region_val = argv[1];
SDL_GPUTextureTransferInfo transfer_info = {0};
JS_GETPROP(js, transfer_info.transfer_buffer, transfer_info_val, transfer_buffer, SDL_GPUTransferBuffer)
JS_GETPROP(js, transfer_info.offset, transfer_info_val, offset, number)
JS_GETPROP(js, transfer_info.pixels_per_row, transfer_info_val, pixels_per_row, number)
JS_GETPROP(js, transfer_info.rows_per_layer, transfer_info_val, rows_per_layer, number)
SDL_GPUTextureRegion texture_region = {0};
JS_GETPROP(js, texture_region.texture, texture_region_val, texture, SDL_GPUTexture)
JS_GETPROP(js, texture_region.mip_level, texture_region_val, mip_level, number)
JS_GETPROP(js, texture_region.layer, texture_region_val, layer, number)
JS_GETPROP(js, texture_region.x, texture_region_val, x, number)
JS_GETPROP(js, texture_region.y, texture_region_val, y, number)
JS_GETPROP(js, texture_region.z, texture_region_val, z, number)
JS_GETPROP(js, texture_region.w, texture_region_val, w, number)
JS_GETPROP(js, texture_region.h, texture_region_val, h, number)
JS_GETPROP(js, texture_region.d, texture_region_val, d, number)
bool cycle = JS_ToBool(js, argv[2]);
SDL_UploadToGPUTexture(pass, &transfer_info, &texture_region, cycle);
)
JSC_CCALL(copypass_buffer_to_buffer,
SDL_GPUCopyPass *pass = js2SDL_GPUCopyPass(js,self);
SDL_GPUBuffer *b1 = js2SDL_GPUBuffer(js,argv[0]);
SDL_GPUBuffer *b2 = js2SDL_GPUBuffer(js,argv[1]);
size_t size = js2number(js,argv[2]);
bool cycle = js2bool(js,argv[3]);
SDL_CopyGPUBufferToBuffer(pass,b1,b2,size,cycle);
)
static const JSCFunctionListEntry js_SDL_GPUCopyPass_funcs[] = {
MIST_FUNC_DEF(copypass, end, 0),
MIST_FUNC_DEF(copypass, upload_to_buffer, 3),
MIST_FUNC_DEF(copypass, upload_to_texture, 3),
MIST_FUNC_DEF(copypass, buffer_to_buffer, 4),
};
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_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(transferbuffer_copy_blob,
SDL_GPUTransferBuffer *buffer = js2SDL_GPUTransferBuffer(js, self);
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]);
JSValue blob = argv[1];
size_t blob_size;
void *blob_data = js_get_blob_data(js, &blob_size, blob);
if (blob_data == -1)
return JS_EXCEPTION;
if (!blob_data)
return JS_ThrowTypeError(js, "copy_blob: Expected a blob");
void *mapped_data = SDL_MapGPUTransferBuffer(gpu, buffer, false);
if (!mapped_data) return JS_ThrowReferenceError(js, "copy_blob: Failed to map transfer buffer: %s", SDL_GetError());
SDL_memcpy(mapped_data, blob_data, blob_size);
// Unmap the transfer buffer
SDL_UnmapGPUTransferBuffer(gpu, buffer);
return JS_UNINITIALIZED;
)
static const JSCFunctionListEntry js_SDL_GPUTransferBuffer_funcs[] = {
MIST_FUNC_DEF(transferbuffer, copy_blob, 2),
};
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_BindGPUComputeStorageBuffers(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),
};
// Empty function arrays for classes with no methods yet
// GPU device constructor function
static JSValue js_gpu_constructor(JSContext *js, JSValueConst new_target, int argc, JSValueConst *argv)
{
SDL_PropertiesID props = SDL_CreateProperties();
// Handle properties object if provided
if (argc > 0 && JS_IsObject(argv[0])) {
JSValue opts = argv[0];
// Helper function to check and set boolean properties
#define SET_BOOL_PROP(js_name, sdl_prop) do { \
JSValue val = JS_GetPropertyStr(js, opts, js_name); \
if (!JS_IsNull(val)) { \
SDL_SetBooleanProperty(props, sdl_prop, JS_ToBool(js, val)); \
} \
JS_FreeValue(js, val); \
} while(0)
SET_BOOL_PROP("debug", SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN);
SET_BOOL_PROP("lowpower", SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN);
// Shader format properties
SET_BOOL_PROP("shaders_private", SDL_PROP_GPU_DEVICE_CREATE_SHADERS_PRIVATE_BOOLEAN);
SET_BOOL_PROP("shaders_spirv", SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN);
SET_BOOL_PROP("shaders_dxbc", SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN);
SET_BOOL_PROP("shaders_dxil", SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN);
SET_BOOL_PROP("shaders_msl", SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN);
SET_BOOL_PROP("shaders_metallib", SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN);
#undef SET_BOOL_PROP
// Handle string properties
JSValue name_val = JS_GetPropertyStr(js, opts, "name");
if (!JS_IsNull(name_val)) {
const char *name = JS_ToCString(js, name_val);
if (name) {
SDL_SetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING, name);
JS_FreeCString(js, name);
}
}
JS_FreeValue(js, name_val);
// D3D12 semantic name
JSValue semantic_val = JS_GetPropertyStr(js, opts, "d3d12_semantic_name");
if (!JS_IsNull(semantic_val)) {
const char *semantic = JS_ToCString(js, semantic_val);
if (semantic) {
SDL_SetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING, semantic);
JS_FreeCString(js, semantic);
}
}
JS_FreeValue(js, semantic_val);
}
// Create GPU device with properties
SDL_GPUDevice *device = SDL_CreateGPUDeviceWithProperties(props);
SDL_DestroyProperties(props);
if (!device) {
return JS_ThrowReferenceError(js, "Failed to create GPU device: %s", SDL_GetError());
}
// Global device storage removed - device now passed as parameter
// Create the GPU device JS object
JSValue device_obj = SDL_GPUDevice2js(js, device);
return device_obj;
}
// Function to register module
CELL_USE_INIT(
JSValue ret = JS_NewObject(js);
// Initialize classes
QJSCLASSPREP_FUNCS(SDL_GPUDevice)
QJSCLASSPREP_FUNCS(SDL_GPUBuffer)
QJSCLASSPREP_FUNCS(SDL_GPUTexture)
QJSCLASSPREP_FUNCS(SDL_GPUTransferBuffer)
QJSCLASSPREP_FUNCS(SDL_GPUCommandBuffer)
QJSCLASSPREP_FUNCS(SDL_GPUComputePass)
QJSCLASSPREP_FUNCS(SDL_GPUCopyPass)
QJSCLASSPREP_FUNCS(SDL_GPURenderPass)
QJSCLASSPREP_NO_FUNCS(SDL_GPUFence)
QJSCLASSPREP_NO_FUNCS(SDL_GPUComputePipeline)
QJSCLASSPREP_NO_FUNCS(SDL_GPUGraphicsPipeline)
QJSCLASSPREP_NO_FUNCS(SDL_GPUSampler)
QJSCLASSPREP_NO_FUNCS(SDL_GPUShader)
// Create GPU constructor
JSValue gpu_ctor = JS_NewCFunction2(js, js_gpu_constructor, "gpu", 1, JS_CFUNC_constructor, 0);
// Set prototype on constructor
JS_SetConstructor(js, gpu_ctor, SDL_GPUDevice_proto);
// Set constructor in exports
JS_SetPropertyStr(js, ret, "gpu", gpu_ctor);
// Add GPU object constructors
JSValue sampler_ctor = JS_NewCFunction2(js, js_gpu_sampler_constructor, "sampler", 2, JS_CFUNC_constructor, 0);
JS_SetPropertyStr(js, ret, "sampler", sampler_ctor);
JSValue shader_ctor = JS_NewCFunction2(js, js_gpu_shader_constructor, "shader", 2, JS_CFUNC_constructor, 0);
JS_SetPropertyStr(js, ret, "shader", shader_ctor);
JSValue graphics_pipeline_ctor = JS_NewCFunction2(js, js_gpu_graphics_pipeline_constructor, "graphics_pipeline", 2, JS_CFUNC_constructor, 0);
JS_SetPropertyStr(js, ret, "graphics_pipeline", graphics_pipeline_ctor);
JSValue compute_pipeline_ctor = JS_NewCFunction2(js, js_gpu_compute_pipeline_constructor, "compute_pipeline", 2, JS_CFUNC_constructor, 0);
JS_SetPropertyStr(js, ret, "compute_pipeline", compute_pipeline_ctor);
JSValue buffer_ctor = JS_NewCFunction2(js, js_gpu_buffer_constructor, "buffer", 2, JS_CFUNC_constructor, 0);
JS_SetPropertyStr(js, ret, "buffer", buffer_ctor);
JSValue transfer_buffer_ctor = JS_NewCFunction2(js, js_gpu_transfer_buffer_constructor, "transfer_buffer", 2, JS_CFUNC_constructor, 0);
JS_SetPropertyStr(js, ret, "transfer_buffer", transfer_buffer_ctor);
JSValue texture_ctor = JS_NewCFunction2(js, js_gpu_texture_constructor, "texture", 2, JS_CFUNC_constructor, 0);
JS_SetPropertyStr(js, ret, "texture", texture_ctor);
return ret;
)