sdl compute

This commit is contained in:
2025-01-07 20:22:06 -06:00
parent 854e3b4c24
commit 996691d66f
3 changed files with 205 additions and 81 deletions

View File

@@ -82,19 +82,6 @@ sprite_pipeline.target = {
depth: "d32 float s8"
};
var post_pipeline = Object.create(base_pipeline);
post_pipeline.stencil = {
enabled: false,
test: false,
};
post_pipeline.depth = {
test: false,
write: false
};
post_pipeline.target = "post";
//post_pipeline.vertex = "post.vert"
//post_pipeline.fragment = "post.frag"
var dbgline_pipeline = Object.create(base_pipeline);
dbgline_pipeline.vertex = "dbgline.vert.hlsl"
dbgline_pipeline.fragment = "dbgline.frag.hlsl"
@@ -370,7 +357,7 @@ function upload_model(model)
if (i === 'indices') model[i].index = true;
bufs.push(model[i]);
}
tbuffer = render._main.upload(this, bufs, tbuffer);
tbuffer = render._main.upload(this, bufs, tbuffer);
}
function bind_model(pass,pipeline,model)
@@ -451,7 +438,7 @@ var main_depth = {
function render_camera(cmds, camera)
{
if (render_queue.length == 0) return;
if (!camera.target) {
main_color.width = main_depth.width = prosperon.camera.size.x;
main_color.height = main_depth.height = prosperon.camera.size.y;
@@ -477,7 +464,9 @@ function render_camera(cmds, camera)
};
}
if (render_queue.length == 0) return;
var spritemesh = render._main.make_sprite_mesh(render_queue);
return;
cmds.upload_model(spritemesh);
var pass = cmds.render_pass(camera.target);
var camera = prosperon.camera;
@@ -504,8 +493,50 @@ function render_camera(cmds, camera)
pass.end();
render_queue.length = 0;
render_queue = [];
spritemesh = undefined;
}
function mode_rect(src,dst,mode = "stretch")
{
var aspect_src = src.width/src.height;
var aspect_dst = dst.width/dst.height;
var out = {
x:dst.x,
y:dst.y,
width:dst.width,
height:dst.height
};
if (mode == "stretch") return out;
if (mode == "letterbox") {
if (aspect_src > aspect_dst) {
var scaled_h = out.width/aspect_src;
var off = (out.height - scaled_h) * 0.5;
out.y += off;
out.height = scaled_h;
} else {
var scaled_w =out.height * aspect_src;
var off = (out.width - scaled_w) * 0.5;
out.x += off;
out.width = scaled_w;
}
} else if (mode == "overscan"){
if (aspect_src > aspect_dst) {
var scaled_w = out.height * aspect_src;
var off = (out.width - scaled_w) * 0.5;
out.x += off;
out.width = scaled_w;
} else {
var scaled_h = out.width / aspect_src;
var off = (out.height - scaled_h) * 0.5;
out.y += off;
out.height = scaled_h;
}
}
return out;
}
var swaps = [];
function gpupresent()
{
@@ -517,16 +548,22 @@ function gpupresent()
if (!swapchain_tex)
cmds.cancel();
else {
var mode = prosperon.camera.presentation || "letterbox"
var src_rect = {x:0,y:0,width:640,height:360}
var dst_rect = {x:0,y:0,width:swapchain_tex.width,height:swapchain_tex.height};
var torect = mode_rect(src_rect,dst_rect,mode);
torect.texture = swapchain_tex;
if (swapchain_tex) cmds.blit({
src: prosperon.camera.target.color_targets[0].texture,
dst: swapchain_tex,
dst: torect,
filter:"nearest",
load: "clear"
});
cmds.submit()
}
}
}
var ducky;
var pipeline_model;
pipeline_model = Object.create(base_pipeline);
@@ -549,23 +586,8 @@ render.init = function () {
quad_model = render._main.make_quad();
io.mount("core");
render._main.present = gpupresent;
ducky = os.model_buffer("Duck.glb");
for (var mesh of ducky) {
var mat = mesh.material;
for (var i in mat) {
var img = {};
img.surface = os.make_texture(mat[i]);
img.texture = render._main.load_texture(img.surface);
img.sampler = std_sampler;
mat[i] = img;
}
}
ducky = ducky[0];
var cmds = render._main.acquire_cmd_buffer();
cmds.__proto__.upload_model = upload_model;
cmds.upload_model(ducky.mesh);
cmds.upload_model(quad_model);
cmds.submit();
};
@@ -1255,14 +1277,20 @@ try {
} catch(e) { console.error(e); }
}
var waittime = 1/60;
var last_frame_time = 0;
// Ran once per frame
prosperon.process = function process() {
var now = profile.now();
var dt = now - last_frame_time;
if (dt < waittime) os.sleep(waittime-dt);
last_frame_time = profile.now();
try {
layout.newframe();
// check for hot reloading
if (dmon) dmon.poll(dmon_cb);
var dt = profile.now() - frame_t;
frame_t = profile.now();
var dt = last_frame_time - frame_t;
frame_t = last_frame_time;
game.engine_input(e => {
prosperon[e.type]?.(e);
@@ -1275,7 +1303,6 @@ try {
update_emitters(dt * game.timescale);
os.update_timers(dt * game.timescale);
prosperon.update(dt*game.timescale);
prosperon.draw();
} catch(e) { console.error(e) }
if (sim.mode === "step") sim.pause();
@@ -1296,7 +1323,6 @@ try {
} catch(e) {
console.error(e)
}
tracy.end_frame();
};

View File

@@ -179,10 +179,8 @@ 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);
}
static inline 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); }
double js2number(JSContext *js, JSValue v) {
@@ -1236,6 +1234,14 @@ SDL_FColor js2SDL_FColor(JSContext *js, JSValue 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);
@@ -2570,6 +2576,8 @@ static JSValue event2js(JSContext *js, SDL_Event event)
case SDL_EVENT_USER:
JS_SetPropertyStr(js,e,"cb", JS_DupValue(js,*(JSValue*)event.user.data1));
JS_FreeValue(js,*(JSValue*)event.user.data1);
free(event.user.data1);
event.user.data1 = NULL;
break;
}
return e;
@@ -2588,19 +2596,6 @@ JSC_CCALL(game_engine_input,
}
)
JSC_CCALL(game_engine_delay,
SDL_Delay(js2number(js,argv[0]));
)
JSC_CCALL(game_renderers,
int num = SDL_GetNumRenderDrivers();
JSValue arr = JS_NewArray(js);
for (int i = 0; i < num; i++)
JS_SetPropertyUint32(js, arr, i, JS_NewString(js, SDL_GetRenderDriver(i)));
return arr;
)
JSC_CCALL(game_cameras,
int num;
SDL_CameraID *ids = SDL_GetCameras(&num);
@@ -2647,8 +2642,6 @@ JSC_CCALL(game_camera_position,
static const JSCFunctionListEntry js_game_funcs[] = {
MIST_FUNC_DEF(game, engine_start, 1),
MIST_FUNC_DEF(game, engine_input,1),
MIST_FUNC_DEF(game, engine_delay, 1),
MIST_FUNC_DEF(game, renderers, 0),
MIST_FUNC_DEF(game, cameras, 0),
MIST_FUNC_DEF(game, open_camera, 1),
MIST_FUNC_DEF(game, camera_name,1),
@@ -3246,6 +3239,7 @@ JSC_CCALL(renderer_make_sprite_mesh,
JSValue sub = JS_GetPropertyUint32(js,sprites,i);
JSValue jstransform = JS_GetProperty(js,sub,transform_atom);
transform *tr = js2transform(js,jstransform);
JSValue jssrc = JS_GetProperty(js,sub,src_atom);
JSValue jscolor = JS_GetProperty(js,sub,color_atom);
HMM_Vec4 color;
@@ -3285,7 +3279,8 @@ JSC_CCALL(renderer_make_sprite_mesh,
colordata[base+1] = color;
colordata[base+2] = color;
colordata[base+3] = color;
JS_FreeValue(js,jstransform);
JS_FreeValue(js,sub);
JS_FreeValue(js,jscolor);
JS_FreeValue(js,jssrc);
@@ -3928,6 +3923,7 @@ JSC_CCALL(gpu_make_sprite_mesh,
colordata[base+2] = color;
colordata[base+3] = color;
JS_FreeValue(js,jstransform);
JS_FreeValue(js,sub);
JS_FreeValue(js,jscolor);
JS_FreeValue(js,jssrc);
@@ -3947,21 +3943,12 @@ JSC_CCALL(gpu_make_sprite_mesh,
BufferCheckResult uv_chk = get_or_extend_buffer(js, old_mesh, uv_atom, uv_size, JS_TYPED_ARRAY_FLOAT32, 2, 1, 0);
BufferCheckResult color_chk = get_or_extend_buffer(js, old_mesh, color_atom, color_size, JS_TYPED_ARRAY_FLOAT32, 4, 1, 0);
// For indices, we might have a shared global buffer like idx_buffer. If we want a per-mesh buffer,
// do the same check. If we rely on a global make_quad_indices_buffer that can handle extension,
// we can just call it. Otherwise, implement similarly:
int need_idx_new = 0;
// Suppose we have a function get_or_extend_buffer for indices as well, or we just always call
// make_quad_indices_buffer. If it returns a shared buffer that can handle it, that's fine.
// If you want per-mesh indices, do something similar:
// But since original code calls make_quad_indices_buffer(js, quads), we assume it's global and can handle reuse.
// If any buffer needs a new allocation, we discard reuse and build all new buffers.
int need_new_all = pos_chk.need_new || uv_chk.need_new || color_chk.need_new || need_idx_new;
int need_new_all = pos_chk.need_new || uv_chk.need_new || color_chk.need_new;
ret = JS_NewObject(js);
if (need_new_all) {
printf("NEED NEW ALL\n");
// 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);
@@ -4062,12 +4049,6 @@ JSC_CCALL(gpu_driver,
ret = JS_NewString(js, SDL_GetGPUDeviceDriver(gpu));
)
JSC_CCALL(gpu_set_pipeline,
JSValue pipeline = JS_GetPropertyStr(js, argv[0], "gpu");
if (JS_IsUndefined(pipeline))
return JS_ThrowReferenceError(js, "Must use make_pipeline before setting.");
)
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");
@@ -4164,7 +4145,7 @@ JSC_CCALL(gpu_acquire_cmd_buffer,
/* 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
2: an optional transfer buffer to use; if undefined a temporary one is used
*/
JSC_CCALL(gpu_upload,
JSValue js_cmd = argv[0];
@@ -4208,19 +4189,19 @@ JSC_CCALL(gpu_upload,
JS_FreeValue(js, js_buf);
}
size_t total_size = 0;
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) {
if (transfer_size < total_size_needed) {
printf("New transfer buffer needed of size %d, old was %d\n", total_size_needed, transfer_size);
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 {
@@ -4230,7 +4211,7 @@ JSC_CCALL(gpu_upload,
}
);
ret = SDL_GPUTransferBuffer2js(js,transfer);
JS_SetPropertyStr(js,ret,"size", number2js(js,total_size_needed));
}
SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(cmds);
@@ -4275,7 +4256,6 @@ JSC_CCALL(gpu_upload,
}
SDL_EndGPUCopyPass(copy_pass);
free(items);
)
@@ -4315,12 +4295,37 @@ JSC_CCALL(gpu_shader_format,
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, make_sampler,1),
MIST_FUNC_DEF(gpu, set_pipeline, 1), // grabs the gpu property off a pipeline value to load
MIST_FUNC_DEF(gpu, load_texture, 2),
MIST_FUNC_DEF(gpu, logical_size, 1),
MIST_FUNC_DEF(gpu, texture, 1),
@@ -4731,8 +4736,36 @@ JSC_CCALL(cmd_cancel,
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),
@@ -4750,6 +4783,70 @@ static const JSCFunctionListEntry js_SDL_GPUCommandBuffer_funcs[] = {
MIST_FUNC_DEF(cmd, blit, 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),
};
JSC_CCALL(surface_blit,
SDL_Surface *dst = js2SDL_Surface(js,self);
rect dstrect = js2rect(js,argv[0]);

View File

@@ -50,6 +50,7 @@ static JSValue js_tracy_plot(JSContext *js, JSValue self, int argc, JSValue *arg
JS_ToFloat64(js, &n, argv[1]);
TracyCPlot(str, n);
JS_FreeCString(js,str);
return JS_UNDEFINED;
}
static JSValue js_tracy_plot_config(JSContext *js, JSValue self, int argc, JSValue *argv)