Roblox backend: one layer (Frame + tiles) per draw list so moving windows costs no redraw; gradient rect fast path; t_drag/t_composite checks

This commit is contained in:
4DBug
2026-10-08 11:10:53 -05:00
parent 71843392f1
commit ceb66d3067
7 changed files with 564 additions and 190 deletions
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env python3
# Compare two t_composite.luau outputs: compare_composite.py a.txt b.txt
import sys
def load(p):
rows = {}
for l in open(p):
if l.startswith("ROW"):
_, y, h = l.split(); rows[int(y)] = bytes.fromhex(h)
return rows
a, b = load(sys.argv[1]), load(sys.argv[2])
assert a and len(a) == len(b), "missing/uneven output"
diff = big = 0; worst = 0
for y in a:
for x, (p, q) in enumerate(zip(a[y], b[y])):
d = abs(p - q)
if d: diff += 1
if d > 2: big += 1
worst = max(worst, d)
print("channels differing: %d, by more than 2: %d, max diff: %d" % (diff, big, worst))
sys.exit(1 if big else 0)
+1 -1
View File
@@ -67,7 +67,7 @@ def main():
args = ap.parse_args()
code, out = run(args, "")
print(out[-4000:])
print(out if os.environ.get("FULL_OUTPUT") else out[-4000:])
sys.exit(code)
try:
+77
View File
@@ -0,0 +1,77 @@
-- Composites every visible tile ImageLabel (walking the ScreenGui like Roblox: ZIndex order, nested offsets) onto a
-- black screen and prints the RGB of a region as hex rows ("ROW y hex"). Compare two backend versions with
-- tools/test/compare_composite.py to prove a backend change renders the same pixels.
local G = require(game:GetService("ReplicatedStorage"):WaitForChild("ImGui"))
local ImGui, ImVec2, ImVec4 = G.ImGui, G.ImVec2, G.ImVec4
ImGui.CreateContext()
G.ImGui_ImplRoblox.Init()
G.ImGui_ImplRoblox.SetRenderRate(0)
local io = ImGui.GetIO()
local col = ImVec4(0.4, 0.7, 0.2, 0.5)
local W, H = 1280, 760
local frame = 0
local function composite(gui)
local screen = buffer.create(W * H * 3)
local function walk(inst, ox, oy)
local kids = inst:GetChildren()
table.sort(kids, function(a, b) return (a.ZIndex or 1) < (b.ZIndex or 1) end) -- table.sort isn't stable, but siblings with equal ZIndex never overlap here
for _, c in ipairs(kids) do
if c.Visible ~= false then
local x, y = ox + (c.Position and c.Position.X or 0), oy + (c.Position and c.Position.Y or 0)
if c.ClassName == "ImageLabel" then
local img = c.ImageContent.Object
local px, sz = img.Pixels, img.Size.X
for ty = 0, sz - 1 do
local sy = y + ty
if sy >= 0 and sy < H then
for tx = 0, sz - 1 do
local sx = x + tx
if sx >= 0 and sx < W then
local o = (ty * sz + tx) * 4
local a = buffer.readu8(px, o + 3)
if a > 0 then
local d = (sy * W + sx) * 3
local fa = a / 255
for ch = 0, 2 do
local v = buffer.readu8(px, o + ch) * fa + buffer.readu8(screen, d + ch) * (1 - fa)
buffer.writeu8(screen, d + ch, math.floor(v + 0.5))
end
end
end
end
end
end
end
walk(c, x, y)
end
end
end
walk(gui, 0, 0)
return screen
end
game:GetService("RunService").Stepped:Connect(function()
frame += 1
G.ImGui_ImplRoblox.NewFrame()
io.DeltaTime = 1 / 60
io:AddMousePosEvent(300 + (frame * 37) % 300, 100 + (frame * 53) % 500)
ImGui.NewFrame()
ImGui.ShowDemoWindow(true)
ImGui.SetNextWindowPos(ImVec2(20, 20)); ImGui.SetNextWindowSize(ImVec2(600, 700))
ImGui.Begin("Pickers")
ImGui.ColorPicker4("bar", col, G.ImGuiColorEditFlags.PickerHueBar)
ImGui.ColorPicker4("wheel", col, G.ImGuiColorEditFlags.PickerHueWheel)
ImGui.End()
ImGui.Render()
G.ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
if frame == ARG_FRAMES - 1 then
local gui = game:GetService("Players").LocalPlayer.PlayerGui:FindFirstChild("ImGui")
local screen = composite(gui)
for y = 0, H - 1 do
local t = table.create(W * 3)
for i = 0, W * 3 - 1 do t[i + 1] = string.format("%02x", buffer.readu8(screen, y * W * 3 + i)) end
print("ROW", y, table.concat(t))
end
end
end)
+43
View File
@@ -0,0 +1,43 @@
-- Benchmark: drags the demo window (real mouse drag on its title bar) and keeps a color picker window open.
-- Watch "render" ms and uploads/frame: with per-window layers, dragging should upload ~nothing.
local G = require(game:GetService("ReplicatedStorage"):WaitForChild("ImGui"))
local ImGui, ImVec2, ImVec4 = G.ImGui, G.ImVec2, G.ImVec4
ImGui.CreateContext()
G.ImGui_ImplRoblox.Init()
G.ImGui_ImplRoblox.SetRenderRate(0)
local io = ImGui.GetIO()
local col = ImVec4(0.4, 0.7, 0.2, 0.5)
local frame = 0
local bx, by = 0, 0
local MODE = ARG_OPEN == true and "picker" or "drag"
game:GetService("RunService").Stepped:Connect(function()
frame += 1
G.ImGui_ImplRoblox.NewFrame()
io.DeltaTime = 1 / 60
if MODE == "drag" then
local t = math.abs((frame % 200) - 100) -- triangle wave, smooth back and forth
if frame == 5 then local w = ImGui.FindWindowByName("ImGui Sincerely Demo"); bx, by = w.Pos.x + 80, w.Pos.y + 8; io:AddMousePosEvent(bx, by) elseif frame == 6 then io:AddMouseButtonEvent(0, true)
elseif frame > 6 then io:AddMousePosEvent(bx - t * 5, by + t * 2) end
else
io:AddMousePosEvent(60 + (frame * 7) % 200, 80 + (frame * 5) % 200)
if frame == 6 then io:AddMouseButtonEvent(0, true) end
end
ImGui.NewFrame()
if MODE == "drag" then
if frame < 3 then ImGui.SetNextWindowPos(ImVec2(20, 20)); ImGui.SetNextWindowSize(ImVec2(500, 650)) end
ImGui.ShowDemoWindow(true)
else
ImGui.SetNextWindowPos(ImVec2(20, 20)); ImGui.SetNextWindowSize(ImVec2(700, 700))
ImGui.Begin("Pickers")
ImGui.ColorPicker4("bar", col, G.ImGuiColorEditFlags.PickerHueBar)
ImGui.ColorPicker4("wheel", col, G.ImGuiColorEditFlags.PickerHueWheel)
ImGui.End()
end
ImGui.Render()
G.ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
if frame == ARG_FRAMES - 1 and MODE == "drag" then
local w = ImGui.FindWindowByName("ImGui Sincerely Demo")
assert(w.Pos.y > 25, "window was not dragged")
print("DRAG OK", w.Pos.x, w.Pos.y, w.Collapsed, ImGui.GetDrawData().TotalVtxCount)
end
end)