feat(render): #86 @ClearColor — Render phase auto-clears + auto-presents
@ClearColor(0xRRGGBB) declares the framebuffer clear colour, so the engine owns the per-frame clear and flip: the Render phase clears to the colour at the top and presents after the handlers run. Games drop the repeated Screen.clear(color)/Screen.show() boilerplate, and the colour is configured declaratively (an annotation) rather than in the handler body. Opt-in and backward-compatible: a program with no @ClearColor is byte-for-byte identical (it clears/presents itself, or the light system owns the present). Parser reads @ClearColor(int) into g_clear_color/g_has_clear_color; emit_game_main emits rt_clear before and rt_present after the Render phase, gated on the flag. Example clear_color (pixel-readback verified — an undrawn pixel holds the clear colour, proving the engine cleared), docs page + inventory entry. Full suite 108/0, goldens byte-identical, fixpoint holds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bccd26fb29
commit
d9287d6b1d
8 changed files with 18108 additions and 17936 deletions
|
|
@ -486,7 +486,18 @@ function emit_game_main() -> void {
|
|||
emit_calls_for_phase("FixedUpdate")
|
||||
emit_calls_for_phase("Update")
|
||||
emit_calls_for_phase("LateUpdate")
|
||||
# #86: @ClearColor(N) makes the Render phase auto-clear to N at the top and
|
||||
# auto-present after the handlers run, so a game drops the clear/show boilerplate
|
||||
# (the clear colour is declared, not written in the handler body). Opt-in — a game
|
||||
# with no @ClearColor is byte-identical (it clears/shows itself, or the light
|
||||
# system owns the present).
|
||||
if g_has_clear_color and (find_fn("rt_clear") != null) {
|
||||
emit(" call void @fn_rt_clear(i32 "); emit(itoa(g_clear_color)); emit(")\n")
|
||||
}
|
||||
emit_calls_for_phase("Render")
|
||||
if g_has_clear_color and (find_fn("rt_present") != null) {
|
||||
emit(" call void @fn_rt_present()\n")
|
||||
}
|
||||
let fcur = emit_bind("load i32, ptr @L_frame") # Time.frame(): count completed frames
|
||||
let fnext = emit_bind(`add i32 {fcur}, 1`)
|
||||
emit(" store i32 "); emit(fnext); emit(", ptr @L_frame\n")
|
||||
|
|
|
|||
|
|
@ -443,6 +443,8 @@ var g_uses_tiled: bool = false # Tiled.* -> splice the Tiled map runtime (#69
|
|||
var g_uses_reflect_io: bool = false # Reflect.serialize/apply -> splice the reflection serializer
|
||||
var g_uses_esys: bool = false # an engine-owned system component (SpriteAnim/Motion/Light2D) is declared -> splice systems.ludic + force the reflection ABI
|
||||
var g_uses_input: bool = false # a program used Input.bind/down/poll/… (action maps + record/replay) -> splice input.ludic
|
||||
var g_has_clear_color: bool = false # @ClearColor(N) declared -> the Render phase auto-clears + auto-presents (#86)
|
||||
var g_clear_color: int = 0 # the declared clear colour (0x00RRGGBB)
|
||||
var g_uses_anim_rt: bool = false # Anim.play/clip/on_frame/fired or Motion.to (#48) -> splice systems.ludic + force the reflection ABI
|
||||
var g_uses_tween_rt: bool = false # Tween.to/chain/delay/… (#48) -> splice tween.ludic + run esys_tween each Update
|
||||
var g_uses_audio: bool = false # Audio.* (#22) -> splice audio.ludic; a windowed build also links audio.ll + AVFoundation
|
||||
|
|
@ -542,8 +544,11 @@ function parse_one_decl() -> void {
|
|||
else { if (a == "System") { sys_phase = "Update"; if is_op("(") { pi = pi + 1; sys_phase = eat_id(); eat_op(")") } } # @System(Phase) binary-module system (#64)
|
||||
else { if (a == "EngineSystem") { eat_op("("); esys_comp = eat_id(); eat_op(","); esys_phase = eat_id(); eat_op(")") } # @EngineSystem(Comp, Phase) package engine system (#62)
|
||||
else { if (a == "Namespace") { eat_op("("); ns_name = eat_id(); eat_op(")") } # @Namespace(Name) package Foo.* namespace (#62)
|
||||
else { if (a == "ClearColor") { eat_op("(") # @ClearColor(N) — Render auto-clear + auto-present (#86)
|
||||
if toks[pi].kind != TK_INT { perr("@ClearColor expects an integer colour, e.g. @ClearColor(0x101018)") }
|
||||
g_clear_color = toks[pi].ival; g_has_clear_color = true; pi = pi + 1; eat_op(")") }
|
||||
else { if is_op("(") { var d = 0 # any other @anno(args) — parsed and skipped
|
||||
while true { if is_op("(") { d = d + 1 }; if is_op(")") { d = d - 1 }; pi = pi + 1; if d == 0 { break } } } } } } } } } } } } } } } } } } } } } } } }
|
||||
while true { if is_op("(") { d = d + 1 }; if is_op(")") { d = d - 1 }; pi = pi + 1; if d == 0 { break } } } } } } } } } } } } } } } } } } } } } } } } }
|
||||
skipnl()
|
||||
}
|
||||
if is_id("import") { pi = pi + 1
|
||||
|
|
@ -875,6 +880,8 @@ function parse_program() -> void {
|
|||
g_uses_reflect = false
|
||||
g_uses_esys = false
|
||||
g_uses_input = false
|
||||
g_has_clear_color = false
|
||||
g_clear_color = 0
|
||||
g_uses_light = false
|
||||
g_uses_value = false
|
||||
g_uses_xml = false
|
||||
|
|
|
|||
35967
selfhost/ludicc.seed.ll
35967
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue