rework hot reload

This commit is contained in:
2024-11-09 22:19:37 -06:00
parent be453fdad1
commit ba9b1c1c9e
7 changed files with 123 additions and 92 deletions

View File

@@ -1,7 +1,6 @@
var actor = {};
var actor_urs = {};
var script_times = {};
var actor_spawns = {};
@@ -17,7 +16,6 @@ globalThis.class_use = function (script, config, base, callback) {
if (!actor_urs[file]) {
var newur = Object.create(base);
actor_urs[file] = newur;
script_times[file] = io.mod(file);
actor_spawns[file] = [];
}
@@ -61,28 +59,23 @@ actor.__stats = function () {
return stats;
};
actor.hotreload = function () {
for (var i in script_times) {
if (io.mod(i) > script_times[i]) {
script_times[i] = io.mod(i);
var script = Resources.replstrs(i);
script = `(function() {
var self = this;
var $ = this.__proto__;
${script};
})`;
var fn = os.eval(i, script);
actor.hotreload = function (file) {
var script = Resources.replstrs(file);
script = `(function() {
var self = this;
var $ = this.__proto__;
${script};
})`;
var fn = os.eval(file, script);
for (var obj of actor_spawns[i]) {
var a = obj;
a.timers.forEachRight(t=>t());
a.timers = [];
var save = json.decode(json.encode(a));
fn.call(a);
Object.merge(a, save);
check_registers(a);
}
}
for (var obj of actor_spawns[file]) {
var a = obj;
a.timers.forEachRight(t=>t());
a.timers = [];
var save = json.decode(json.encode(a));
fn.call(a);
Object.merge(a, save);
check_registers(a);
}
};

View File

@@ -53,6 +53,7 @@ Resources.rm_fn = function rm_fn(fn, text) {
};
Resources.rm_fn.doc = "Remove calls to a given function from a given text script.";
// Normalizes paths for use in prosperon
Resources.replpath = function replpath(str, path) {
if (!str) return str;
if (str[0] === "/") return str.rm(0);
@@ -70,6 +71,7 @@ Resources.replpath = function replpath(str, path) {
return str;
};
// Given a script path, loads it, and replaces certain function calls to conform to environment
Resources.replstrs = function replstrs(path) {
if (!path) return;
var script = io.slurp(path);
@@ -160,7 +162,6 @@ function find_ext(file, ext, root = "") {
}
var all_files = io.glob(`**/${file}.*`);
all_files = all_files.filter(x => !x.startsWith('.'));
var find = undefined;
for (var e of ext) {
var finds = all_files.filter(x => x.ext() === e);
@@ -328,9 +329,9 @@ io.exists = function(path)
var tmpslurp = io.slurp;
io.slurp = function(path)
{
path = Resources.replpath(path);
var ret = tmpslurp(path, true) || core_db.slurp(path, true);
if (!ret) console.info(`could not slurp ${path}`);
var findpath = Resources.replpath(path);
var ret = tmpslurp(findpath, true) || core_db.slurp(findpath, true);
if (!ret) console.info(`could not slurp path ${path} as ${findpath}`)
return ret;
}
@@ -410,13 +411,4 @@ bare_use("preconfig.js");
if (!profile.enabled) use = stripped_use;
Object.assign(globalThis, use("prosperon.js"));
/*app.interval(_ => {
profile.report("hotreload");
actor.hotreload();
render.hotreload();
game.tex_hotreload();
repl.hotreload();
profile.endreport("hotreload");
}, 1);
*/

View File

@@ -178,21 +178,42 @@ game.doc.pause = "Pause game simulation.";
game.doc.play = "Resume or start game simulation.";
game.doc.camera = "Current camera.";
game.tex_hotreload = function () {
return;
for (var path in game.texture.cache) {
if (io.mod(path) > game.texture.time_cache[path]) {
var tex = game.texture.cache[path];
game.texture.time_cache[path] = io.mod(path);
SpriteAnim.hotreload(path);
os.texture_swap(path, game.texture.cache[path]);
for (var sprite of Object.values(allsprites)) {
if (sprite.texture == tex) {
sprite.tex_sync();
}
game.tex_hotreload = function (file) {
if (!(file in game.texture.cache)) return;
var data = io.slurpbytes(file);
var tex;
if (file.endsWith('.gif')) {
var anim = os.make_gif(data);
if (anim.frames.length !== 1) return;
console.info(json.encode(anim));
tex = anim.frames[0].texture;
} else if (file.endsWith('.ase') || file.endsWith('.aseprite')) {
var anim = os.make_aseprite(data);
if (anim.texture) // single picture
tex = anim.texture;
else {
var oldanim = game.texture.cache[file];
// load all into gpu
for (var a in anim) {
oldanim[a] = anim[a];
for (let frame of anim[a].frames)
frame.texture.load_gpu();
}
return;
}
}
} else
tex = os.make_texture(data);
var img = game.texture.cache[file];
tex.load_gpu();
console.info(`replacing ${json.encode(img)}`)
img.texture = tex;
img.rect = {
x:0,
y:0,
width:1,
height:1
};
};
var image = {};
@@ -303,6 +324,7 @@ game.is_image = function(obj)
// Any request to it returns an image, which is a texture and rect. But they can
game.texture = function (path) {
if (typeof path !== 'string') throw new Error('need a string for game.texture')
var parts = path.split(':');
path = Resources.find_image(parts[0]);
@@ -343,8 +365,16 @@ game.texture = function (path) {
var ext = path.ext();
if (ext === 'ase' || ext === 'aseprite') {
anim = os.make_aseprite(path);
console.info(`making out of an aseprite for ${path}`);
anim = os.make_aseprite(io.slurpbytes(path));
console.info(`raw anim is ${json.encode(anim)}`)
if (!anim) return;
if (anim.texture) {
// it was a single image
anim.texture.load_gpu();
game.texture.cache[path] = anim;
return anim;
}
// load all into gpu
for (var a in anim)
for (let frame of anim[a].frames)
@@ -357,7 +387,7 @@ game.texture = function (path) {
}
if (ext === 'gif') {
anim = os.make_gif(io.slurp(path));
anim = os.make_gif(io.slurpbytes(path));
if (!anim) return;
if (anim.frames.length === 1) {
// in this case, it's just a single image

View File

@@ -1433,17 +1433,11 @@ try{
profile.report("imgui");
if (debug.show) imgui_fn();
profile.endreport("imgui");
// render.end_pass();
// render.commit();
// profile.report_frame(profile.secs(profile.now()) - frame_t);
// endframe();
} catch(e) {
throw e;
} finally {
render.end_pass();
profile.report_frame(profile.secs(profile.now()) - frame_t);
render.commit();
endframe();
}
@@ -1455,6 +1449,12 @@ function dmon_cb(e)
{
if (e.file.startsWith('.')) return;
console.info(json.encode(e))
if (e.file.endsWith('.js'))
actor.hotreload(e.file);
else if (Resources.is_image(e.file))
game.tex_hotreload(e.file);
else if (e.file.endsWith('.cg')) // shader
render.hotreload(e.file);
}
prosperon.process = function process() {

View File

@@ -235,7 +235,7 @@ Cmdline.register_order(
else global.app = actor.spawn("nogame.js");
var icon = game.texture(project.icon);
if (icon) window.set_icon(game.texture(icon.texture));
if (icon) window.set_icon(icon.texture);
game.camera = world.spawn("camera2d");
});
},

View File

@@ -2393,21 +2393,20 @@ JSC_CCALL(os_obj_size,
JSC_CCALL(os_make_texture,
size_t len;
void *raw = JS_GetArrayBuffer(js, &len, argv[0]);
if (!raw) {
JS_ThrowReferenceError(js, "could not load texture with array buffer");
return JS_EXCEPTION;
}
ret = texture2js(texture_fromdata(raw, len));
if (!raw) return JS_ThrowReferenceError(js, "could not load texture with array buffer");
texture *tex = texture_fromdata(raw, len);
if (!tex) return JS_ThrowReferenceError(js, "unable to make texture from the given array buffer");
ret = texture2js(tex);
JS_SetPropertyStr(js, ret, "path", JS_DupValue(js,argv[0]));
)
JSC_CCALL(os_make_gif,
size_t rawlen;
void *raw = JS_GetArrayBuffer(js, &rawlen, argv[0]);
if (!raw) {
JS_ThrowReferenceError(js, "could not load gif from supplied array buffer");
return JS_UNDEFINED;
}
if (!raw) return JS_ThrowReferenceError(js, "could not load gif from supplied array buffer");
int n;
texture *tex = calloc(1,sizeof(*tex));
int frames;
@@ -2442,20 +2441,43 @@ JSC_CCALL(os_make_gif,
ret = gif;
)
JSValue aseframe2js(JSContext *js, ase_frame_t aframe)
{
JSValue frame = JS_NewObject(js);
texture *tex = calloc(1,sizeof(*tex));
tex->width = aframe.ase->w;
tex->height = aframe.ase->h;
tex->data = malloc(tex->width*tex->height*4);
memcpy(tex->data, aframe.pixels, tex->width*tex->height*4);
js_setpropstr(frame, "texture", texture2js(tex));
js_setpropstr(frame, "rect", rect2js((rect){.x=0,.y=0,.w=1,.h=1}));
js_setpropstr(frame, "time", number2js((float)aframe.duration_milliseconds/1000.0));
return frame;
}
JSC_CCALL(os_make_aseprite,
size_t rawlen;
void *raw = JS_GetArrayBuffer(js,&rawlen,argv[0]);
if (!raw) return JS_UNDEFINED;
if (!raw) return JS_ThrowReferenceError(js, "could not load aseprite from supplied array buffer");
ase_t *ase = cute_aseprite_load_from_memory(raw, rawlen, NULL);
JSValue obj = JS_NewObject(js);
int w = ase->w;
int h = ase->h;
int pixels = w*h;
if (ase->tag_count == 0) {
// we're dealing with a single frame image, or single animation
if (ase->frame_count == 1) {
JSValue obj = aseframe2js(js,ase->frames[0]);
cute_aseprite_free(ase);
return obj;
}
}
JSValue obj = JS_NewObject(js);
for (int t = 0; t < ase->tag_count; t++) {
ase_tag_t tag = ase->tags[t];
JSValue anim = JS_NewObject(js);
@@ -2475,17 +2497,7 @@ JSC_CCALL(os_make_aseprite,
int _frame = 0;
JSValue frames = JS_NewArray(js);
for (int f = tag.from_frame; f <= tag.to_frame; f++) {
ase_frame_t aframe = ase->frames[f];
JSValue frame = JS_NewObject(js);
texture *tex = calloc(1,sizeof(*tex));
tex->width = w;
tex->height = h;
tex->data = malloc(w*h*4);
memcpy(tex->data, aframe.pixels, w*h*4);
js_setpropstr(frame, "texture", texture2js(tex));
js_setpropstr(frame, "rect", rect2js((rect){.x=0,.y=0,.w=1,.h=1}));
js_setpropstr(frame, "time", number2js((float)aframe.duration_milliseconds/1000.0));
JSValue frame = aseframe2js(js,ase->frames[f]);
js_setprop_num(frames, _frame, frame);
_frame++;
}
@@ -2498,13 +2510,6 @@ JSC_CCALL(os_make_aseprite,
cute_aseprite_free(ase);
)
JSC_SCALL(os_texture_swap,
texture *old = js2texture(argv[1]);
texture *tex = texture_from_file(str);
JS_SetOpaque(argv[1], tex);
texture_free(old);
)
JSC_CCALL(os_make_tex_data,
ret = texture2js(texture_empty(js2number(argv[0]), js2number(argv[1])))
)
@@ -2788,7 +2793,6 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, make_texture, 1),
MIST_FUNC_DEF(os, make_gif, 1),
MIST_FUNC_DEF(os, make_aseprite, 1),
MIST_FUNC_DEF(os, texture_swap, 2),
MIST_FUNC_DEF(os, make_tex_data, 3),
MIST_FUNC_DEF(os, make_font, 2),
MIST_FUNC_DEF(os, make_transform, 0),

View File

@@ -69,7 +69,19 @@ static uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filen
void script_startup() {
rt = JS_NewRuntime();
js = JS_NewContext(rt);
js = JS_NewContextRaw(rt);
JS_AddIntrinsicBaseObjects(js);
JS_AddIntrinsicEval(js);
JS_AddIntrinsicRegExp(js);
JS_AddIntrinsicJSON(js);
JS_AddIntrinsicMapSet(js);
JS_AddIntrinsicTypedArrays(js);
JS_AddIntrinsicPromise(js);
JS_AddIntrinsicProxy(js);
JS_AddIntrinsicBigInt(js);
JS_AddIntrinsicBigFloat(js);
JS_AddIntrinsicBigDecimal(js);
JS_AddIntrinsicOperators(js);
ffi_load();