diff --git a/build/ImGui.linemap.json b/build/ImGui.linemap.json index eab3e19..15d7489 100644 --- a/build/ImGui.linemap.json +++ b/build/ImGui.linemap.json @@ -1 +1 @@ -[[12, "imgui.lua"], [13795, "imgui_demo.lua"], [14728, "imgui_demo_2.lua"], [17803, "imgui_demo_apps.lua"], [19062, "imgui_demo_tools.lua"], [20388, "imgui_demo_widgets.lua"], [22312, "imgui_docking.lua"], [25440, "imgui_dragdrop.lua"], [25732, "imgui_draw.lua"], [30979, "imgui_h.lua"], [33153, "imgui_impl_roblox.lua"], [34094, "imgui_impl_roblox_fonts.lua"], [40497, "imgui_internal.lua"], [43669, "imgui_tables.lua"], [48016, "imgui_tabs.lua"], [49183, "imgui_widgets.lua"], [57717, "imstb_rectpack.lua"], [58163, "imstb_textedit.lua"], [59404, "imstb_truetype.lua"], [61872, "imstd_minstdio.lua"]] \ No newline at end of file +[[12, "imgui.lua"], [13795, "imgui_demo.lua"], [14728, "imgui_demo_2.lua"], [17803, "imgui_demo_apps.lua"], [19062, "imgui_demo_tools.lua"], [20388, "imgui_demo_widgets.lua"], [22312, "imgui_docking.lua"], [25440, "imgui_dragdrop.lua"], [25732, "imgui_draw.lua"], [30979, "imgui_h.lua"], [33153, "imgui_impl_roblox.lua"], [34121, "imgui_impl_roblox_fonts.lua"], [40524, "imgui_internal.lua"], [43696, "imgui_tables.lua"], [48043, "imgui_tabs.lua"], [49210, "imgui_widgets.lua"], [57744, "imstb_rectpack.lua"], [58190, "imstb_textedit.lua"], [59431, "imstb_truetype.lua"], [61899, "imstd_minstdio.lua"]] \ No newline at end of file diff --git a/build/ImGui.luau b/build/ImGui.luau index a9f1488..69d9474 100644 --- a/build/ImGui.luau +++ b/build/ImGui.luau @@ -33680,7 +33680,7 @@ local function GetTile(bd, layer, key, tx, ty) label.Visible = false label.Active = bd.TilesActive label.Parent = layer.Frame - tile = { Image = img, Label = label, Buffer = buffer.create(TILE * TILE * 4), H1 = -1, H2 = -1, Visible = false } + tile = { Image = img, Label = label, Buffer = buffer.create(TILE * TILE * 4), H1 = -1, H2 = -1, Visible = false, BX0 = TILE, BY0 = TILE, BX1 = 0, BY1 = 0 } layer.Tiles[key] = tile return tile end @@ -33879,8 +33879,28 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data) local x0, y0 = lx0 + tx * TILE, ly0 + ty * TILE local x1, y1 = x0 + TILE, y0 + TILE buf, buf_w = tile.Buffer, TILE - buffer.fill(buf, 0, 0) local list = tl[key] + -- Dirty region = old content bounds + new content bounds. Outside it the tile is already + -- transparent, so only this region is cleared and uploaded (edge tiles are often mostly empty). + local nx0, ny0, nx1, ny1 = TILE, TILE, 0, 0 + for k = 1, count do + local p = list[k] + local a, b = pr_bx0[p] - x0, pr_by0[p] - y0 + local c, d = pr_bx1[p] - x0, pr_by1[p] - y0 + if a < nx0 then nx0 = a end + if b < ny0 then ny0 = b end + if c > nx1 then nx1 = c end + if d > ny1 then ny1 = d end + end + nx0, ny0, nx1, ny1 = max(nx0, 0), max(ny0, 0), min(nx1, TILE), min(ny1, TILE) + local rx0, ry0, rx1, ry1 = min(nx0, tile.BX0), min(ny0, tile.BY0), max(nx1, tile.BX1), max(ny1, tile.BY1) + tile.BX0, tile.BY0, tile.BX1, tile.BY1 = nx0, ny0, nx1, ny1 + local rw = rx1 - rx0 + if rw == TILE then + buffer.fill(buf, ry0 * TILE * 4, 0, (ry1 - ry0) * TILE * 4) + else + for y = ry0, ry1 - 1 do buffer.fill(buf, (y * TILE + rx0) * 4, 0, rw * 4) end + end local tox, toy = ox + x0, oy + y0 for k = 1, count do local p = list[k] @@ -33902,7 +33922,14 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data) end end PB("ImGui upload") - tile.Image:WritePixelsBuffer(Vector2.zero, TILE_SIZE, buf) + if rw == TILE and ry0 == 0 and ry1 == TILE then + tile.Image:WritePixelsBuffer(Vector2.zero, TILE_SIZE, buf) + elseif rw > 0 and ry1 > ry0 then + local rh = ry1 - ry0 + local sub = buffer.create(rw * rh * 4) + for y = 0, rh - 1 do buffer.copy(sub, y * rw * 4, buf, ((ry0 + y) * TILE + rx0) * 4, rw * 4) end + tile.Image:WritePixelsBuffer(Vector2.new(rx0, ry0), Vector2.new(rw, rh), sub) + end PE() end if not tile.Visible then diff --git a/imgui/backend/imgui_impl_roblox.lua b/imgui/backend/imgui_impl_roblox.lua index 7470702..20ea591 100644 --- a/imgui/backend/imgui_impl_roblox.lua +++ b/imgui/backend/imgui_impl_roblox.lua @@ -528,7 +528,7 @@ local function GetTile(bd, layer, key, tx, ty) label.Visible = false label.Active = bd.TilesActive label.Parent = layer.Frame - tile = { Image = img, Label = label, Buffer = buffer.create(TILE * TILE * 4), H1 = -1, H2 = -1, Visible = false } + tile = { Image = img, Label = label, Buffer = buffer.create(TILE * TILE * 4), H1 = -1, H2 = -1, Visible = false, BX0 = TILE, BY0 = TILE, BX1 = 0, BY1 = 0 } layer.Tiles[key] = tile return tile end @@ -727,8 +727,28 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data) local x0, y0 = lx0 + tx * TILE, ly0 + ty * TILE local x1, y1 = x0 + TILE, y0 + TILE buf, buf_w = tile.Buffer, TILE - buffer.fill(buf, 0, 0) local list = tl[key] + -- Dirty region = old content bounds + new content bounds. Outside it the tile is already + -- transparent, so only this region is cleared and uploaded (edge tiles are often mostly empty). + local nx0, ny0, nx1, ny1 = TILE, TILE, 0, 0 + for k = 1, count do + local p = list[k] + local a, b = pr_bx0[p] - x0, pr_by0[p] - y0 + local c, d = pr_bx1[p] - x0, pr_by1[p] - y0 + if a < nx0 then nx0 = a end + if b < ny0 then ny0 = b end + if c > nx1 then nx1 = c end + if d > ny1 then ny1 = d end + end + nx0, ny0, nx1, ny1 = max(nx0, 0), max(ny0, 0), min(nx1, TILE), min(ny1, TILE) + local rx0, ry0, rx1, ry1 = min(nx0, tile.BX0), min(ny0, tile.BY0), max(nx1, tile.BX1), max(ny1, tile.BY1) + tile.BX0, tile.BY0, tile.BX1, tile.BY1 = nx0, ny0, nx1, ny1 + local rw = rx1 - rx0 + if rw == TILE then + buffer.fill(buf, ry0 * TILE * 4, 0, (ry1 - ry0) * TILE * 4) + else + for y = ry0, ry1 - 1 do buffer.fill(buf, (y * TILE + rx0) * 4, 0, rw * 4) end + end local tox, toy = ox + x0, oy + y0 for k = 1, count do local p = list[k] @@ -750,7 +770,14 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data) end end PB("ImGui upload") - tile.Image:WritePixelsBuffer(Vector2.zero, TILE_SIZE, buf) + if rw == TILE and ry0 == 0 and ry1 == TILE then + tile.Image:WritePixelsBuffer(Vector2.zero, TILE_SIZE, buf) + elseif rw > 0 and ry1 > ry0 then + local rh = ry1 - ry0 + local sub = buffer.create(rw * rh * 4) + for y = 0, rh - 1 do buffer.copy(sub, y * rw * 4, buf, ((ry0 + y) * TILE + rx0) * 4, rw * 4) end + tile.Image:WritePixelsBuffer(Vector2.new(rx0, ry0), Vector2.new(rw, rh), sub) + end PE() end if not tile.Visible then diff --git a/tools/test/roblox_mock.luau b/tools/test/roblox_mock.luau index ca9fc8c..698638e 100644 --- a/tools/test/roblox_mock.luau +++ b/tools/test/roblox_mock.luau @@ -111,7 +111,12 @@ local AssetService = {} function AssetService:CreateEditableImage(opts) local img = { Size = opts.Size, Pixels = buffer.create(opts.Size.X * opts.Size.Y * 4) } UPLOADS = 0 - function img:WritePixelsBuffer(origin, size, buf) buffer.copy(img.Pixels, 0, buf); _G_UPLOADS = (_G_UPLOADS or 0) + 1 end + function img:WritePixelsBuffer(origin, size, buf) + assert(buffer.len(buf) == size.X * size.Y * 4, "WritePixelsBuffer: buffer size mismatch") + assert(origin.X >= 0 and origin.Y >= 0 and origin.X + size.X <= opts.Size.X and origin.Y + size.Y <= opts.Size.Y, "WritePixelsBuffer: out of bounds") + for y = 0, size.Y - 1 do buffer.copy(img.Pixels, ((origin.Y + y) * opts.Size.X + origin.X) * 4, buf, y * size.X * 4, size.X * 4) end + _G_UPLOADS = (_G_UPLOADS or 0) + 1; _G_UPLOAD_PX = (_G_UPLOAD_PX or 0) + size.X * size.Y + end function img:Destroy() end return img end @@ -223,4 +228,4 @@ for frame = 1, NFRAMES do if frame == 10 then render_time = 0; _G_UPLOADS = 0 end end -print(string.format("avg frame total %.2f ms, of which render %.2f ms, uploads/frame %.2f", total / (NFRAMES - 10) * 1000, render_time / (NFRAMES - 10) * 1000, _G_UPLOADS / (NFRAMES - 10))) +print(string.format("avg frame total %.2f ms, of which render %.2f ms, uploads/frame %.2f, upload KB/frame %.0f", total / (NFRAMES - 10) * 1000, render_time / (NFRAMES - 10) * 1000, _G_UPLOADS / (NFRAMES - 10), (_G_UPLOAD_PX or 0) * 4 / 1024 / (NFRAMES - 10))) diff --git a/tools/test/scripts/t_composite.luau b/tools/test/scripts/t_composite.luau index a3bc531..185bfe5 100644 --- a/tools/test/scripts/t_composite.luau +++ b/tools/test/scripts/t_composite.luau @@ -56,6 +56,8 @@ game:GetService("RunService").Stepped:Connect(function() G.ImGui_ImplRoblox.NewFrame() io.DeltaTime = 1 / 60 io:AddMousePosEvent(300 + (frame * 37) % 300, 100 + (frame * 53) % 500) + local dw = ImGui.FindWindowByName("ImGui Sincerely Demo") + if dw and frame % 3 == 0 then io:AddMousePosEvent(dw.Pos.x + 200, dw.Pos.y + 300); io:AddMouseWheelEvent(0, (frame % 30 < 18) and -1 or 1) end ImGui.NewFrame() ImGui.ShowDemoWindow(true) ImGui.SetNextWindowPos(ImVec2(20, 20)); ImGui.SetNextWindowSize(ImVec2(600, 700)) diff --git a/tools/test/scripts/t_drag.luau b/tools/test/scripts/t_drag.luau index 3aa1748..5891c01 100644 --- a/tools/test/scripts/t_drag.luau +++ b/tools/test/scripts/t_drag.luau @@ -9,7 +9,7 @@ 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" +local MODE = ARG_OPEN == true and "picker" or (type(ARG_OPEN) == "table" and "scroll" or "drag") game:GetService("RunService").Stepped:Connect(function() frame += 1 G.ImGui_ImplRoblox.NewFrame() @@ -18,12 +18,15 @@ game:GetService("RunService").Stepped:Connect(function() 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 + elseif MODE == "scroll" then + local w = ImGui.FindWindowByName("ImGui Sincerely Demo"); if w then io:AddMousePosEvent(w.Pos.x + w.Size.x * 0.5, w.Pos.y + w.Size.y * 0.5) end + io:AddMouseWheelEvent(0, (frame % 20 < 10) and -1 or 1) 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 MODE ~= "picker" then if frame < 3 then ImGui.SetNextWindowPos(ImVec2(20, 20)); ImGui.SetNextWindowSize(ImVec2(500, 650)) end ImGui.ShowDemoWindow(true) else