From d2ca019f5097d2b42ec1c0d5e7d1670cf05bb45c Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak <dos@dosowisko.net> Date: Tue, 14 Nov 2023 00:00:38 +0100 Subject: [PATCH] sim: Show the overlay in simulator --- sim/fakes/_sim.py | 63 +++++++++++++++++++++++++++++++++++++-- sim/fakes/ctx.py | 40 +++++++++++++++++++------ sim/fakes/sys_display.py | 9 +++--- sim/wasm/build.sh | 2 +- sim/wasm/ctx.wasm | Bin 1098155 -> 1099393 bytes 5 files changed, 97 insertions(+), 17 deletions(-) diff --git a/sim/fakes/_sim.py b/sim/fakes/_sim.py index 5b05d8ff91..6280359093 100644 --- a/sim/fakes/_sim.py +++ b/sim/fakes/_sim.py @@ -545,11 +545,23 @@ _sim = Simulation() class FramebufferManager: def __init__(self): self._free = [] + + # Significant difference between on-device Ctx and simulation Ctx: we + # render to a BRGA8 (24bpp color + 8bpp alpha) buffer instead of 16bpp + # RGB565 like the device does. This allows us to directly blit the ctx + # framebuffer into pygame's surfaces, which is a _huge_ speed benefit + # (difference between ~10FPS and 500+FPS!). + for _ in range(1): - fb, c = ctx._wasm.ctx_new_for_framebuffer(240, 240) + fb, c = ctx._wasm.ctx_new_for_framebuffer(240, 240, 240 * 4, ctx.RGBA8) ctx._wasm.ctx_apply_transform(c, 1, 0, 120, 0, 1, 120, 0, 0, 1) self._free.append((fb, c)) + self._overlay = ctx._wasm.ctx_new_for_framebuffer(240, 240, 240 * 4, ctx.RGBA8) + ctx._wasm.ctx_apply_transform(self._overlay[1], 1, 0, 120, 0, 1, 120, 0, 0, 1) + + self._output = ctx._wasm.ctx_new_for_framebuffer(240, 240, 240 * 4, ctx.BGRA8) + def get(self): if len(self._free) == 0: return None, None @@ -561,8 +573,42 @@ class FramebufferManager: def put(self, fb, ctx): self._free.append((fb, ctx)) + def get_overlay(self): + return self._overlay + + def get_output(self, fbp): + return self._output + + def draw(self, fb): + ctx._wasm.ctx_define_texture( + self._output[1], "!fb", 240, 240, 240 * 4, ctx.RGBA8, fb, 0 + ) + ctx._wasm.ctx_parse(self._output[1], "compositingMode copy") + ctx._wasm.ctx_draw_texture(self._output[1], "!fb", 0, 0, 240, 240) + + if overlay_clip[2] and overlay_clip[3]: + ctx._wasm.ctx_define_texture( + self._output[1], + "!overlay", + 240, + 240, + 240 * 4, + ctx.RGBA8, + self._overlay[0], + 0, + ) + ctx._wasm.ctx_parse(self._output[1], "compositingMode sourceOver") + ctx._wasm.ctx_draw_texture(self._output[1], "!overlay", 0, 0, 240, 240) + fbm = FramebufferManager() +overlay_ctxs = [] +overlay_clip = (0, 0, 240, 240) + + +def set_overlay_clip(x, y, x2, y2): + global overlay_clip + overlay_clip = (x, y, x2 - x, y2 - y) def get_ctx(): @@ -572,19 +618,32 @@ def get_ctx(): def get_overlay_ctx(): dctx = ctx._wasm.ctx_new_drawlist(240, 240) + overlay_ctxs.append(dctx) return ctx.Context(dctx) def display_update(subctx): _sim.process_events() + + if subctx._ctx in overlay_ctxs: + overlay_ctxs.remove(subctx._ctx) + fbp, c = fbm.get_overlay() + ctx._wasm.ctx_render_ctx(subctx._ctx, c) + ctx._wasm.ctx_destroy(subctx._ctx) + return + fbp, c = fbm.get() + if fbp is None: return ctx._wasm.ctx_render_ctx(subctx._ctx, c) ctx._wasm.ctx_destroy(subctx._ctx) - fb = ctx._wasm._i.exports.memory.uint8_view(fbp) + fbm.draw(fbp) + + fb = ctx._wasm._i.exports.memory.uint8_view(fbm.get_output(fbp)[0]) + _sim.render_display(fb) _sim.render_gui_now() diff --git a/sim/fakes/ctx.py b/sim/fakes/ctx.py index c3864bb4b9..71ce397b7c 100644 --- a/sim/fakes/ctx.py +++ b/sim/fakes/ctx.py @@ -46,20 +46,14 @@ class Wasm: self._i.exports.ctx_parse(ctx, p) self.free(p) - def ctx_new_for_framebuffer(self, width, height): + def ctx_new_for_framebuffer(self, width, height, stride, format): """ Call ctx_new_for_framebuffer, but also first allocate the underlying framebuffer and return it alongside the Ctx*. """ - fb = self.malloc(width * height * 4) - # Significant difference between on-device Ctx and simulation Ctx: we - # render to a BRGA8 (24bpp color + 8bpp alpha) buffer instead of 16bpp - # RGB565 like the device does. This allows us to directly blit the ctx - # framebuffer into pygame's surfaces, which is a _huge_ speed benefit - # (difference between ~10FPS and 500+FPS!). - BRGA8 = 5 + fb = self.malloc(stride * height) return fb, self._i.exports.ctx_new_for_framebuffer( - fb, width, height, width * 4, BRGA8 + fb, width, height, stride, format ) def ctx_new_drawlist(self, width, height): @@ -69,6 +63,29 @@ class Wasm: args = [float(a) for a in args] return self._i.exports.ctx_apply_transform(ctx, *args) + def ctx_define_texture(self, ctx, eid, *args): + s = eid.encode("utf-8") + slen = len(s) + 1 + p = self.malloc(slen) + mem = self._i.exports.memory.uint8_view(p) + mem[0 : slen - 1] = s + mem[slen - 1] = 0 + res = self._i.exports.ctx_define_texture(ctx, p, *args) + self.free(p) + return res + + def ctx_draw_texture(self, ctx, eid, *args): + s = eid.encode("utf-8") + slen = len(s) + 1 + p = self.malloc(slen) + mem = self._i.exports.memory.uint8_view(p) + mem[0 : slen - 1] = s + mem[slen - 1] = 0 + args = [float(a) for a in args] + res = self._i.exports.ctx_draw_texture(ctx, p, *args) + self.free(p) + return res + def ctx_text_width(self, ctx, text): s = text.encode("utf-8") slen = len(s) + 1 @@ -371,3 +388,8 @@ class Context: self.line_to(-130, 130) self.line_to(-130, 0) return self + + +RGBA8 = 4 +BGRA8 = 5 +RGB565_BYTESWAPPED = 7 diff --git a/sim/fakes/sys_display.py b/sim/fakes/sys_display.py index d2d945b6e1..2bdfd24958 100644 --- a/sim/fakes/sys_display.py +++ b/sim/fakes/sys_display.py @@ -9,12 +9,8 @@ def pipe_available(): return True -def overlay_clip(x0, y0, x1, y1): - pass - - def get_mode(): - return 0 + return osd def set_mode(no): @@ -36,10 +32,13 @@ def fps(): update = _sim.display_update get_ctx = _sim.get_ctx get_overlay_ctx = _sim.get_overlay_ctx +overlay_clip = _sim.set_overlay_clip osd = 256 def ctx(foo): + if foo == osd: + return _sim.get_overlay_ctx() return _sim.get_ctx() diff --git a/sim/wasm/build.sh b/sim/wasm/build.sh index 8055e67129..b66dbc532b 100755 --- a/sim/wasm/build.sh +++ b/sim/wasm/build.sh @@ -18,7 +18,7 @@ emcc ctx.c \ -I ../../components/ctx/ \ -I ../../components/ctx/fonts/ \ -D SIMULATOR \ - -s EXPORTED_FUNCTIONS=_ctx_new_for_framebuffer,_ctx_new_drawlist,_ctx_parse,_ctx_apply_transform,_ctx_text_width,_ctx_render_ctx,_ctx_destroy,_malloc,_free \ + -s EXPORTED_FUNCTIONS=_ctx_new_for_framebuffer,_ctx_new_drawlist,_ctx_parse,_ctx_apply_transform,_ctx_text_width,_ctx_render_ctx,_ctx_define_texture,_ctx_draw_texture,_ctx_destroy,_malloc,_free \ --no-entry -flto -O3 \ -o ctx.wasm diff --git a/sim/wasm/ctx.wasm b/sim/wasm/ctx.wasm index 40f825db8028f177ffaecdef08caca646305f13e..a7bbe5dd123872f32db40f6074af8c82b8ae5b11 100755 GIT binary patch delta 11892 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 K{pThAP458wz7R|R delta 10721 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 -- GitLab