Roblox backend: window content in its own clipped layer at the scroll origin (scrolling moves pixels instead of redrawing), 128px tiles
This commit is contained in:
@@ -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"], [34140, "imgui_impl_roblox_fonts.lua"], [40543, "imgui_internal.lua"], [43715, "imgui_tables.lua"], [48062, "imgui_tabs.lua"], [49229, "imgui_widgets.lua"], [57763, "imstb_rectpack.lua"], [58209, "imstb_textedit.lua"], [59450, "imstb_truetype.lua"], [61918, "imstd_minstdio.lua"]]
|
||||
[[12, "imgui.lua"], [13799, "imgui_demo.lua"], [14732, "imgui_demo_2.lua"], [17807, "imgui_demo_apps.lua"], [19066, "imgui_demo_tools.lua"], [20392, "imgui_demo_widgets.lua"], [22316, "imgui_docking.lua"], [25444, "imgui_dragdrop.lua"], [25736, "imgui_draw.lua"], [30984, "imgui_h.lua"], [33158, "imgui_impl_roblox.lua"], [34231, "imgui_impl_roblox_fonts.lua"], [40634, "imgui_internal.lua"], [43806, "imgui_tables.lua"], [48153, "imgui_tabs.lua"], [49320, "imgui_widgets.lua"], [57854, "imstb_rectpack.lua"], [58300, "imstb_textedit.lua"], [59541, "imstb_truetype.lua"], [62009, "imstd_minstdio.lua"]]
|
||||
+244
-153
@@ -7161,6 +7161,10 @@ function ImGui.Begin(name, open, flags)
|
||||
|
||||
if bit32.band(flags, ImGuiWindowFlags.DockNodeHost) == 0 and not window.SkipRefresh then
|
||||
ImGui.PushClipRect(window.InnerClipRect.Min, window.InnerClipRect.Max, true)
|
||||
-- [Roblox backend] content clip + scroll origin: lets the backend move scrolled content instead of redrawing it
|
||||
local dl, cr = window.DrawList, window.DrawList._CmdHeader.ClipRect
|
||||
dl._ContentClipX0, dl._ContentClipY0, dl._ContentClipX1, dl._ContentClipY1 = cr.x, cr.y, cr.z, cr.w
|
||||
dl._ScrollOriginX, dl._ScrollOriginY = window.Pos.x - window.Scroll.x, window.Pos.y - window.Scroll.y
|
||||
end
|
||||
|
||||
window.WriteAccessed = false
|
||||
@@ -28889,6 +28893,7 @@ end
|
||||
|
||||
-- TODO:
|
||||
function MT.ImDrawList:_ResetForNewFrame()
|
||||
self._ContentClipX0 = nil -- set again by Begin() (Roblox backend scroll reuse)
|
||||
if self._Splitter._Count > 1 then
|
||||
self._Splitter:Merge(self)
|
||||
end
|
||||
@@ -33573,7 +33578,7 @@ end
|
||||
-- relative to the layer's origin (top-left of its content). Primitives are hashed into tiles in layer-relative
|
||||
-- coordinates, so moving a window only moves its Frame: no tile changes, nothing re-rasterized or uploaded.
|
||||
-- Only tiles whose hash changed are redrawn; the hash covers everything that affects a tile's pixels.
|
||||
local TILE = 256
|
||||
local TILE = 128
|
||||
TILE_SIZE = Vector2.new(TILE, TILE)
|
||||
local P1, P2 = 2147483647, 2147483629 -- two independent 31 bit hashes per tile (collision odds ~2^-62)
|
||||
local KEY_W = 64 -- tile key = ty * KEY_W + tx (layers up to 16384 px wide)
|
||||
@@ -33585,6 +33590,8 @@ local pr_a, pr_b, pr_c, pr_d, pr_t, pr_g = {}, {}, {}, {}, {}, {} -- vertices (p
|
||||
local pr_cx0, pr_cy0, pr_cx1, pr_cy1 = {}, {}, {}, {} -- clip (screen pixels)
|
||||
local pr_bx0, pr_by0, pr_bx1, pr_by1 = {}, {}, {}, {} -- clipped bounds (screen pixels)
|
||||
local pr_tex = {} -- texture id
|
||||
local pr_k = {} -- layer kind (1..3)
|
||||
local plist1, plist2, plist3 = {}, {}, {} -- primitive indices per layer kind
|
||||
|
||||
@native
|
||||
local function HashVertex(h1, h2, v, ox, oy)
|
||||
@@ -33647,22 +33654,43 @@ local function DrawRectGradient(x0, y0, x1, y1, ca, cb, kind, t, u, v, cx0, cy0,
|
||||
end
|
||||
end
|
||||
|
||||
local function GetLayer(bd, draw_list)
|
||||
local layer = bd.Layers[draw_list]
|
||||
if layer then return layer end
|
||||
-- Layer kinds per draw list: 1 = drawn before the window content (background, title bar, borders, scrollbar),
|
||||
-- 2 = window content (positioned at the scroll origin inside a ClipsDescendants frame, so scrolling just moves it),
|
||||
-- 3 = anything not clipped to the content drawn after content started (menu bar, overlays). Stacked 1 < 2 < 3.
|
||||
local function NewFrame(parent, clips)
|
||||
local frame = Instance.new("Frame")
|
||||
frame.Name = "Layer"
|
||||
frame.BackgroundTransparency = 1
|
||||
frame.BorderSizePixel = 0
|
||||
frame.Size = UDim2.fromOffset(0, 0)
|
||||
frame.ClipsDescendants = clips
|
||||
frame.Visible = false
|
||||
frame.Parent = bd.Gui
|
||||
layer = { Frame = frame, Tiles = {}, TH1 = {}, TH2 = {}, TN = {}, TL = {}, Touched = {}, NTouched = 0,
|
||||
Visible = false, X = false, Y = false, Z = -1, LastUsed = 0 }
|
||||
bd.Layers[draw_list] = layer
|
||||
frame.Parent = parent
|
||||
return frame
|
||||
end
|
||||
|
||||
local function GetLayer(bd, draw_list, kind)
|
||||
local set = bd.Layers[draw_list]
|
||||
if not set then set = { LastUsed = 0 }; bd.Layers[draw_list] = set end
|
||||
local layer = set[kind]
|
||||
if layer then return layer end
|
||||
local root, frame
|
||||
if kind == 2 then
|
||||
root = NewFrame(bd.Gui, true)
|
||||
frame = NewFrame(root, false); frame.Visible = true
|
||||
else
|
||||
frame = NewFrame(bd.Gui, false); root = frame
|
||||
end
|
||||
layer = { Root = root, Frame = frame, Tiles = {}, TH1 = {}, TH2 = {}, TN = {}, TL = {}, TX = {}, TY = {}, Touched = {}, NTouched = 0,
|
||||
Visible = false, X = false, Y = false, Z = -1, CX0 = false, CY0 = false, CX1 = false, CY1 = false }
|
||||
set[kind] = layer
|
||||
return layer
|
||||
end
|
||||
|
||||
local function HideLayer(layer)
|
||||
if layer.Visible then layer.Visible = false; layer.Root.Visible = false end
|
||||
end
|
||||
|
||||
local function GetTile(bd, layer, key, tx, ty)
|
||||
local tile = layer.Tiles[key]
|
||||
if tile then return tile end
|
||||
@@ -33685,13 +33713,18 @@ local function GetTile(bd, layer, key, tx, ty)
|
||||
return tile
|
||||
end
|
||||
|
||||
local function DestroyLayer(layer)
|
||||
for _, tile in pairs(layer.Tiles) do tile.Image:Destroy() end
|
||||
layer.Frame:Destroy()
|
||||
local function DestroyLayerSet(set)
|
||||
for kind = 1, 3 do
|
||||
local layer = set[kind]
|
||||
if layer then
|
||||
for _, tile in pairs(layer.Tiles) do tile.Image:Destroy() end
|
||||
layer.Root:Destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function DestroyTiles(bd)
|
||||
for _, layer in pairs(bd.Layers) do DestroyLayer(layer) end
|
||||
for _, set in pairs(bd.Layers) do DestroyLayerSet(set) end
|
||||
bd.Layers = {}
|
||||
end
|
||||
|
||||
@@ -33701,6 +33734,150 @@ local profiling = false
|
||||
local function PB(name) if profiling then profilebegin(name) end end
|
||||
local function PE() if profiling then profileend() end end
|
||||
|
||||
@native
|
||||
local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_x0, clip_y0, clip_x1, clip_y1)
|
||||
local scale = bd.Scale
|
||||
-- Pass 2: hash primitives into layer-relative tiles
|
||||
PB("ImGui tile hash")
|
||||
local th1, th2, tn, tl, touched = layer.TH1, layer.TH2, layer.TN, layer.TL, layer.Touched
|
||||
for k = 1, layer.NTouched do tn[touched[k]] = 0 end
|
||||
local nt = 0
|
||||
for pi = 1, np do
|
||||
local p = plist[pi]
|
||||
local tex_id = pr_tex[p]
|
||||
-- The clip rect only affects pixels inside the primitive's bounds, so hash the clipped bounds (relative)
|
||||
-- instead of the clip itself: a window clipped by the (fixed) viewport rect stays hash-stable when moved.
|
||||
local cx0, cy0, cx1, cy1 = pr_bx0[p] - lx0, pr_by0[p] - ly0, pr_bx1[p] - lx0, pr_by1[p] - ly0
|
||||
local h1 = ((cx0 * 8191 + cy0) * 8191 + cx1) % P1
|
||||
local h2 = ((cy1 * 8191 + cx1) * 8191 + cy0 + tex_id) % P2
|
||||
local vax, vay = lx0 + ox, ly0 + oy
|
||||
h1, h2 = HashVertex(h1, h2, pr_a[p], vax, vay)
|
||||
h1, h2 = HashVertex(h1, h2, pr_b[p], vax, vay)
|
||||
h1, h2 = HashVertex(h1, h2, pr_c[p], vax, vay)
|
||||
local vd = pr_d[p]
|
||||
if vd then h1, h2 = HashVertex(h1, h2, vd, vax, vay) end
|
||||
for ty = floor((pr_by0[p] - ly0) / TILE), floor((pr_by1[p] - 1 - ly0) / TILE) do
|
||||
for tx = floor((pr_bx0[p] - lx0) / TILE), floor((pr_bx1[p] - 1 - lx0) / TILE) do
|
||||
local key = ty * KEY_W + tx
|
||||
local k = tn[key]
|
||||
if not k or k == 0 then
|
||||
k = 0
|
||||
th1[key], th2[key] = 0, 0
|
||||
if not tl[key] then tl[key] = {} end
|
||||
nt = nt + 1
|
||||
touched[nt] = key
|
||||
layer.TX[key], layer.TY[key] = tx, ty
|
||||
end
|
||||
k = k + 1
|
||||
tn[key] = k
|
||||
tl[key][k] = p
|
||||
th1[key] = (th1[key] * 48271 + h1) % P1
|
||||
th2[key] = (th2[key] * 16807 + h2 + k) % P2
|
||||
end
|
||||
end
|
||||
end
|
||||
layer.NTouched = nt
|
||||
PE()
|
||||
|
||||
-- Place the layer (this is all a window move costs)
|
||||
local root = layer.Root
|
||||
if root ~= layer.Frame then
|
||||
-- content: clip frame at the content clip rect, tiles frame at the scroll origin relative to it
|
||||
if layer.CX0 ~= clip_x0 or layer.CY0 ~= clip_y0 or layer.CX1 ~= clip_x1 or layer.CY1 ~= clip_y1 then
|
||||
layer.CX0, layer.CY0, layer.CX1, layer.CY1 = clip_x0, clip_y0, clip_x1, clip_y1
|
||||
root.Position = UDim2.fromOffset(clip_x0 / scale, clip_y0 / scale)
|
||||
root.Size = UDim2.fromOffset((clip_x1 - clip_x0) / scale, (clip_y1 - clip_y0) / scale)
|
||||
layer.X = false
|
||||
end
|
||||
if layer.X ~= lx0 or layer.Y ~= ly0 then
|
||||
layer.X, layer.Y = lx0, ly0
|
||||
layer.Frame.Position = UDim2.fromOffset((lx0 - clip_x0) / scale, (ly0 - clip_y0) / scale)
|
||||
end
|
||||
elseif layer.X ~= lx0 or layer.Y ~= ly0 then
|
||||
layer.X, layer.Y = lx0, ly0
|
||||
root.Position = UDim2.fromOffset(lx0 / scale, ly0 / scale)
|
||||
end
|
||||
if layer.Z ~= z then layer.Z = z; root.ZIndex = z end
|
||||
if not layer.Visible then layer.Visible = true; root.Visible = true end
|
||||
|
||||
-- Pass 3: redraw + upload changed tiles, hide unused ones
|
||||
PB("ImGui raster+upload")
|
||||
for key, tile in pairs(layer.Tiles) do
|
||||
if not tn[key] or tn[key] == 0 then
|
||||
if tile.Visible then tile.Visible = false; tile.Label.Visible = false; tile.H1 = -1 end
|
||||
end
|
||||
end
|
||||
for kk = 1, nt do
|
||||
local key = touched[kk]
|
||||
local count = tn[key]
|
||||
local tx, ty = layer.TX[key], layer.TY[key]
|
||||
local tile = GetTile(bd, layer, key, tx, ty)
|
||||
if force or tile.H1 ~= th1[key] or tile.H2 ~= th2[key] then
|
||||
tile.H1, tile.H2 = th1[key], th2[key]
|
||||
local x0, y0 = lx0 + tx * TILE, ly0 + ty * TILE
|
||||
local x1, y1 = x0 + TILE, y0 + TILE
|
||||
buf, buf_w = tile.Buffer, TILE
|
||||
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]
|
||||
local cx0, cy0 = max(pr_cx0[p], x0) - x0, max(pr_cy0[p], y0) - y0
|
||||
local cx1, cy1 = min(pr_cx1[p], x1) - x0, min(pr_cy1[p], y1) - y0
|
||||
local va, vc, vd = pr_a[p], pr_c[p], pr_d[p]
|
||||
if vd then
|
||||
local pa, pc, ta, tc = va[1], vc[1], va[2], vc[2]
|
||||
local gk = pr_g[p]
|
||||
if gk == 1 then
|
||||
DrawRectGradient(pa.x - tox, pa.y - toy, pc.x - tox, pc.y - toy, va[3], vc[3], 1, pr_t[p], ta.x, ta.y, cx0, cy0, cx1, cy1)
|
||||
elseif gk == 2 then
|
||||
DrawRectGradient(pa.x - tox, pa.y - toy, pc.x - tox, pc.y - toy, va[3], vc[3], 2, pr_t[p], ta.x, ta.y, cx0, cy0, cx1, cy1)
|
||||
else
|
||||
DrawRect(pa.x - tox, pa.y - toy, pc.x - tox, pc.y - toy, ta.x, ta.y, tc.x, tc.y, va[3], pr_t[p], cx0, cy0, cx1, cy1)
|
||||
end
|
||||
else
|
||||
DrawTriangle(va, pr_b[p], vc, pr_t[p], cx0, cy0, cx1, cy1, tox, toy)
|
||||
end
|
||||
end
|
||||
PB("ImGui upload")
|
||||
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
|
||||
tile.Visible = true
|
||||
tile.Label.Visible = true
|
||||
end
|
||||
end
|
||||
PE()
|
||||
end
|
||||
|
||||
@native
|
||||
function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||
local bd = ImGui_ImplRoblox_GetBackendData()
|
||||
@@ -33745,7 +33922,6 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||
local frame_no = bd.FrameNo
|
||||
local force = bd.ForceRedraw
|
||||
bd.ForceRedraw = false
|
||||
local scale = bd.Scale
|
||||
|
||||
local n = 0
|
||||
local z = 0
|
||||
@@ -33753,8 +33929,14 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||
-- Pass 1: collect this list's primitives and their bounds
|
||||
PB("ImGui collect")
|
||||
local first = n + 1
|
||||
local lx0, ly0 = math.huge, math.huge
|
||||
local vtx, idx = draw_list.VtxBuffer.Data, draw_list.IdxBuffer.Data
|
||||
local ccx0, ccy0, ccx1, ccy1 = draw_list._ContentClipX0, draw_list._ContentClipY0, draw_list._ContentClipX1, draw_list._ContentClipY1
|
||||
local content_x0, content_y0, content_x1, content_y1
|
||||
if ccx0 then
|
||||
content_x0, content_y0 = max(floor(ccx0 - ox), lim_x0), max(floor(ccy0 - oy), lim_y0)
|
||||
content_x1, content_y1 = min(floor(ccx1 - ox), lim_x1), min(floor(ccy1 - oy), lim_y1)
|
||||
end
|
||||
local seen_content = false
|
||||
for _, pcmd in draw_list.CmdBuffer:iter() do
|
||||
if pcmd.UserCallback ~= nil then
|
||||
pcmd.UserCallback(draw_list, pcmd)
|
||||
@@ -33762,6 +33944,16 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||
local clip = pcmd.ClipRect
|
||||
local cx0, cy0 = max(floor(clip.x - ox), lim_x0), max(floor(clip.y - oy), lim_y0)
|
||||
local cx1, cy1 = min(floor(clip.z - ox), lim_x1), min(floor(clip.w - oy), lim_y1)
|
||||
local kind = 1
|
||||
if ccx0 and clip.x >= ccx0 and clip.y >= ccy0 and clip.z <= ccx1 and clip.w <= ccy1 then
|
||||
kind = 2; seen_content = true
|
||||
if clip.x == ccx0 and clip.y == ccy0 and clip.z == ccx1 and clip.w == ccy1 then
|
||||
-- clipped by the layer's clip frame instead: partly visible rows stay hash-stable while scrolling
|
||||
cx0, cy0, cx1, cy1 = lim_x0, lim_y0, lim_x1, lim_y1
|
||||
end
|
||||
elseif seen_content then
|
||||
kind = 3
|
||||
end
|
||||
if cx1 > cx0 and cy1 > cy0 then
|
||||
local tex_id = pcmd:GetTexID()
|
||||
local t = bd.Textures[tex_id]
|
||||
@@ -33810,8 +34002,7 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||
pr_a[n], pr_b[n], pr_c[n], pr_d[n], pr_t[n], pr_g[n], pr_tex[n] = va, vb, vc, vd, t, gk, tex_id
|
||||
pr_cx0[n], pr_cy0[n], pr_cx1[n], pr_cy1[n] = cx0, cy0, cx1, cy1
|
||||
pr_bx0[n], pr_by0[n], pr_bx1[n], pr_by1[n] = bx0, by0, bx1, by1
|
||||
if bx0 < lx0 then lx0 = bx0 end
|
||||
if by0 < ly0 then ly0 = by0 end
|
||||
pr_k[n] = kind
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -33819,147 +34010,47 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||
end
|
||||
PE()
|
||||
|
||||
local layer = GetLayer(bd, draw_list)
|
||||
layer.LastUsed = frame_no
|
||||
if n < first then
|
||||
if layer.Visible then layer.Visible = false; layer.Frame.Visible = false end
|
||||
else
|
||||
-- Pass 2: hash primitives into layer-relative tiles
|
||||
PB("ImGui tile hash")
|
||||
local th1, th2, tn, tl, touched = layer.TH1, layer.TH2, layer.TN, layer.TL, layer.Touched
|
||||
for k = 1, layer.NTouched do tn[touched[k]] = 0 end
|
||||
local nt = 0
|
||||
for p = first, n do
|
||||
local tex_id = pr_tex[p]
|
||||
-- The clip rect only affects pixels inside the primitive's bounds, so hash the clipped bounds (relative)
|
||||
-- instead of the clip itself: a window clipped by the (fixed) viewport rect stays hash-stable when moved.
|
||||
local cx0, cy0, cx1, cy1 = pr_bx0[p] - lx0, pr_by0[p] - ly0, pr_bx1[p] - lx0, pr_by1[p] - ly0
|
||||
local h1 = ((cx0 * 8191 + cy0) * 8191 + cx1) % P1
|
||||
local h2 = ((cy1 * 8191 + cx1) * 8191 + cy0 + tex_id) % P2
|
||||
local vax, vay = lx0 + ox, ly0 + oy
|
||||
h1, h2 = HashVertex(h1, h2, pr_a[p], vax, vay)
|
||||
h1, h2 = HashVertex(h1, h2, pr_b[p], vax, vay)
|
||||
h1, h2 = HashVertex(h1, h2, pr_c[p], vax, vay)
|
||||
local vd = pr_d[p]
|
||||
if vd then h1, h2 = HashVertex(h1, h2, vd, vax, vay) end
|
||||
for ty = floor((pr_by0[p] - ly0) / TILE), floor((pr_by1[p] - 1 - ly0) / TILE) do
|
||||
for tx = floor((pr_bx0[p] - lx0) / TILE), floor((pr_bx1[p] - 1 - lx0) / TILE) do
|
||||
local key = ty * KEY_W + tx
|
||||
local k = tn[key]
|
||||
if not k or k == 0 then
|
||||
k = 0
|
||||
th1[key], th2[key] = 0, 0
|
||||
if not tl[key] then tl[key] = {} end
|
||||
nt = nt + 1
|
||||
touched[nt] = key
|
||||
end
|
||||
k = k + 1
|
||||
tn[key] = k
|
||||
tl[key][k] = p
|
||||
th1[key] = (th1[key] * 48271 + h1) % P1
|
||||
th2[key] = (th2[key] * 16807 + h2 + k) % P2
|
||||
end
|
||||
end
|
||||
-- Split into layer kinds and place each one
|
||||
local set = bd.Layers[draw_list]
|
||||
local nk1, nk2, nk3 = 0, 0, 0
|
||||
local o1x, o1y, o3x, o3y = math.huge, math.huge, math.huge, math.huge
|
||||
for p = first, n do
|
||||
local k = pr_k[p]
|
||||
if k == 1 then
|
||||
nk1 += 1; plist1[nk1] = p
|
||||
if pr_bx0[p] < o1x then o1x = pr_bx0[p] end
|
||||
if pr_by0[p] < o1y then o1y = pr_by0[p] end
|
||||
elseif k == 2 then
|
||||
nk2 += 1; plist2[nk2] = p
|
||||
else
|
||||
nk3 += 1; plist3[nk3] = p
|
||||
if pr_bx0[p] < o3x then o3x = pr_bx0[p] end
|
||||
if pr_by0[p] < o3y then o3y = pr_by0[p] end
|
||||
end
|
||||
layer.NTouched = nt
|
||||
PE()
|
||||
|
||||
-- Place the layer (this is all a window move costs)
|
||||
local frame = layer.Frame
|
||||
if layer.X ~= lx0 or layer.Y ~= ly0 then
|
||||
layer.X, layer.Y = lx0, ly0
|
||||
frame.Position = UDim2.fromOffset(lx0 / scale, ly0 / scale)
|
||||
end
|
||||
z = z + 1
|
||||
if layer.Z ~= z then layer.Z = z; frame.ZIndex = z end
|
||||
if not layer.Visible then layer.Visible = true; frame.Visible = true end
|
||||
|
||||
-- Pass 3: redraw + upload changed tiles, hide unused ones
|
||||
PB("ImGui raster+upload")
|
||||
for key, tile in pairs(layer.Tiles) do
|
||||
if not tn[key] or tn[key] == 0 then
|
||||
if tile.Visible then tile.Visible = false; tile.Label.Visible = false; tile.H1 = -1 end
|
||||
end
|
||||
end
|
||||
for kk = 1, nt do
|
||||
local key = touched[kk]
|
||||
local count = tn[key]
|
||||
local tx, ty = key % KEY_W, floor(key / KEY_W)
|
||||
if tx < 0 then tx = tx + KEY_W; ty = ty - 1 end
|
||||
local tile = GetTile(bd, layer, key, tx, ty)
|
||||
if force or tile.H1 ~= th1[key] or tile.H2 ~= th2[key] then
|
||||
tile.H1, tile.H2 = th1[key], th2[key]
|
||||
local x0, y0 = lx0 + tx * TILE, ly0 + ty * TILE
|
||||
local x1, y1 = x0 + TILE, y0 + TILE
|
||||
buf, buf_w = tile.Buffer, TILE
|
||||
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]
|
||||
local cx0, cy0 = max(pr_cx0[p], x0) - x0, max(pr_cy0[p], y0) - y0
|
||||
local cx1, cy1 = min(pr_cx1[p], x1) - x0, min(pr_cy1[p], y1) - y0
|
||||
local va, vc, vd = pr_a[p], pr_c[p], pr_d[p]
|
||||
if vd then
|
||||
local pa, pc, ta, tc = va[1], vc[1], va[2], vc[2]
|
||||
local gk = pr_g[p]
|
||||
if gk == 1 then
|
||||
DrawRectGradient(pa.x - tox, pa.y - toy, pc.x - tox, pc.y - toy, va[3], vc[3], 1, pr_t[p], ta.x, ta.y, cx0, cy0, cx1, cy1)
|
||||
elseif gk == 2 then
|
||||
DrawRectGradient(pa.x - tox, pa.y - toy, pc.x - tox, pc.y - toy, va[3], vc[3], 2, pr_t[p], ta.x, ta.y, cx0, cy0, cx1, cy1)
|
||||
else
|
||||
DrawRect(pa.x - tox, pa.y - toy, pc.x - tox, pc.y - toy, ta.x, ta.y, tc.x, tc.y, va[3], pr_t[p], cx0, cy0, cx1, cy1)
|
||||
end
|
||||
else
|
||||
DrawTriangle(va, pr_b[p], vc, pr_t[p], cx0, cy0, cx1, cy1, tox, toy)
|
||||
end
|
||||
end
|
||||
PB("ImGui upload")
|
||||
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
|
||||
tile.Visible = true
|
||||
tile.Label.Visible = true
|
||||
end
|
||||
end
|
||||
PE()
|
||||
end
|
||||
if nk1 > 0 then
|
||||
z += 1
|
||||
DrawLayer(bd, GetLayer(bd, draw_list, 1), plist1, nk1, o1x, o1y, ox, oy, force, z)
|
||||
elseif set and set[1] then HideLayer(set[1]) end
|
||||
if nk2 > 0 then
|
||||
z += 1
|
||||
DrawLayer(bd, GetLayer(bd, draw_list, 2), plist2, nk2, floor(draw_list._ScrollOriginX - ox), floor(draw_list._ScrollOriginY - oy), ox, oy, force, z,
|
||||
content_x0, content_y0, content_x1, content_y1)
|
||||
elseif set and set[2] then HideLayer(set[2]) end
|
||||
if nk3 > 0 then
|
||||
z += 1
|
||||
DrawLayer(bd, GetLayer(bd, draw_list, 3), plist3, nk3, o3x, o3y, ox, oy, force, z)
|
||||
elseif set and set[3] then HideLayer(set[3]) end
|
||||
set = bd.Layers[draw_list]
|
||||
if set then set.LastUsed = frame_no end
|
||||
end
|
||||
|
||||
-- Hide layers not drawn this frame; free long unused ones (closed windows, old tooltips)
|
||||
for draw_list, layer in pairs(bd.Layers) do
|
||||
if layer.LastUsed ~= frame_no then
|
||||
if layer.Visible then layer.Visible = false; layer.Frame.Visible = false end
|
||||
if frame_no - layer.LastUsed > LAYER_KEEP_FRAMES then
|
||||
DestroyLayer(layer)
|
||||
for draw_list, set in pairs(bd.Layers) do
|
||||
if set.LastUsed ~= frame_no then
|
||||
for kind = 1, 3 do if set[kind] then HideLayer(set[kind]) end end
|
||||
if frame_no - set.LastUsed > LAYER_KEEP_FRAMES then
|
||||
DestroyLayerSet(set)
|
||||
bd.Layers[draw_list] = nil
|
||||
end
|
||||
end
|
||||
@@ -34093,7 +34184,7 @@ function ImGui_ImplRoblox_NewFrame()
|
||||
local want = io.WantCaptureMouse
|
||||
if want ~= bd.TilesActive then
|
||||
bd.TilesActive = want
|
||||
for _, layer in pairs(bd.Layers) do for _, tile in pairs(layer.Tiles) do tile.Label.Active = want end end
|
||||
for _, set in pairs(bd.Layers) do for kind = 1, 3 do local layer = set[kind]; if layer then for _, tile in pairs(layer.Tiles) do tile.Label.Active = want end end end end
|
||||
end
|
||||
PE()
|
||||
end
|
||||
|
||||
+239
-153
@@ -421,7 +421,7 @@ end
|
||||
-- relative to the layer's origin (top-left of its content). Primitives are hashed into tiles in layer-relative
|
||||
-- coordinates, so moving a window only moves its Frame: no tile changes, nothing re-rasterized or uploaded.
|
||||
-- Only tiles whose hash changed are redrawn; the hash covers everything that affects a tile's pixels.
|
||||
local TILE = 256
|
||||
local TILE = 128
|
||||
TILE_SIZE = Vector2.new(TILE, TILE)
|
||||
local P1, P2 = 2147483647, 2147483629 -- two independent 31 bit hashes per tile (collision odds ~2^-62)
|
||||
local KEY_W = 64 -- tile key = ty * KEY_W + tx (layers up to 16384 px wide)
|
||||
@@ -433,6 +433,8 @@ local pr_a, pr_b, pr_c, pr_d, pr_t, pr_g = {}, {}, {}, {}, {}, {} -- vertices (p
|
||||
local pr_cx0, pr_cy0, pr_cx1, pr_cy1 = {}, {}, {}, {} -- clip (screen pixels)
|
||||
local pr_bx0, pr_by0, pr_bx1, pr_by1 = {}, {}, {}, {} -- clipped bounds (screen pixels)
|
||||
local pr_tex = {} -- texture id
|
||||
local pr_k = {} -- layer kind (1..3)
|
||||
local plist1, plist2, plist3 = {}, {}, {} -- primitive indices per layer kind
|
||||
|
||||
@native
|
||||
local function HashVertex(h1, h2, v, ox, oy)
|
||||
@@ -495,22 +497,43 @@ local function DrawRectGradient(x0, y0, x1, y1, ca, cb, kind, t, u, v, cx0, cy0,
|
||||
end
|
||||
end
|
||||
|
||||
local function GetLayer(bd, draw_list)
|
||||
local layer = bd.Layers[draw_list]
|
||||
if layer then return layer end
|
||||
-- Layer kinds per draw list: 1 = drawn before the window content (background, title bar, borders, scrollbar),
|
||||
-- 2 = window content (positioned at the scroll origin inside a ClipsDescendants frame, so scrolling just moves it),
|
||||
-- 3 = anything not clipped to the content drawn after content started (menu bar, overlays). Stacked 1 < 2 < 3.
|
||||
local function NewFrame(parent, clips)
|
||||
local frame = Instance.new("Frame")
|
||||
frame.Name = "Layer"
|
||||
frame.BackgroundTransparency = 1
|
||||
frame.BorderSizePixel = 0
|
||||
frame.Size = UDim2.fromOffset(0, 0)
|
||||
frame.ClipsDescendants = clips
|
||||
frame.Visible = false
|
||||
frame.Parent = bd.Gui
|
||||
layer = { Frame = frame, Tiles = {}, TH1 = {}, TH2 = {}, TN = {}, TL = {}, Touched = {}, NTouched = 0,
|
||||
Visible = false, X = false, Y = false, Z = -1, LastUsed = 0 }
|
||||
bd.Layers[draw_list] = layer
|
||||
frame.Parent = parent
|
||||
return frame
|
||||
end
|
||||
|
||||
local function GetLayer(bd, draw_list, kind)
|
||||
local set = bd.Layers[draw_list]
|
||||
if not set then set = { LastUsed = 0 }; bd.Layers[draw_list] = set end
|
||||
local layer = set[kind]
|
||||
if layer then return layer end
|
||||
local root, frame
|
||||
if kind == 2 then
|
||||
root = NewFrame(bd.Gui, true)
|
||||
frame = NewFrame(root, false); frame.Visible = true
|
||||
else
|
||||
frame = NewFrame(bd.Gui, false); root = frame
|
||||
end
|
||||
layer = { Root = root, Frame = frame, Tiles = {}, TH1 = {}, TH2 = {}, TN = {}, TL = {}, TX = {}, TY = {}, Touched = {}, NTouched = 0,
|
||||
Visible = false, X = false, Y = false, Z = -1, CX0 = false, CY0 = false, CX1 = false, CY1 = false }
|
||||
set[kind] = layer
|
||||
return layer
|
||||
end
|
||||
|
||||
local function HideLayer(layer)
|
||||
if layer.Visible then layer.Visible = false; layer.Root.Visible = false end
|
||||
end
|
||||
|
||||
local function GetTile(bd, layer, key, tx, ty)
|
||||
local tile = layer.Tiles[key]
|
||||
if tile then return tile end
|
||||
@@ -533,13 +556,18 @@ local function GetTile(bd, layer, key, tx, ty)
|
||||
return tile
|
||||
end
|
||||
|
||||
local function DestroyLayer(layer)
|
||||
for _, tile in pairs(layer.Tiles) do tile.Image:Destroy() end
|
||||
layer.Frame:Destroy()
|
||||
local function DestroyLayerSet(set)
|
||||
for kind = 1, 3 do
|
||||
local layer = set[kind]
|
||||
if layer then
|
||||
for _, tile in pairs(layer.Tiles) do tile.Image:Destroy() end
|
||||
layer.Root:Destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function DestroyTiles(bd)
|
||||
for _, layer in pairs(bd.Layers) do DestroyLayer(layer) end
|
||||
for _, set in pairs(bd.Layers) do DestroyLayerSet(set) end
|
||||
bd.Layers = {}
|
||||
end
|
||||
|
||||
@@ -549,6 +577,150 @@ local profiling = false
|
||||
local function PB(name) if profiling then profilebegin(name) end end
|
||||
local function PE() if profiling then profileend() end end
|
||||
|
||||
@native
|
||||
local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_x0, clip_y0, clip_x1, clip_y1)
|
||||
local scale = bd.Scale
|
||||
-- Pass 2: hash primitives into layer-relative tiles
|
||||
PB("ImGui tile hash")
|
||||
local th1, th2, tn, tl, touched = layer.TH1, layer.TH2, layer.TN, layer.TL, layer.Touched
|
||||
for k = 1, layer.NTouched do tn[touched[k]] = 0 end
|
||||
local nt = 0
|
||||
for pi = 1, np do
|
||||
local p = plist[pi]
|
||||
local tex_id = pr_tex[p]
|
||||
-- The clip rect only affects pixels inside the primitive's bounds, so hash the clipped bounds (relative)
|
||||
-- instead of the clip itself: a window clipped by the (fixed) viewport rect stays hash-stable when moved.
|
||||
local cx0, cy0, cx1, cy1 = pr_bx0[p] - lx0, pr_by0[p] - ly0, pr_bx1[p] - lx0, pr_by1[p] - ly0
|
||||
local h1 = ((cx0 * 8191 + cy0) * 8191 + cx1) % P1
|
||||
local h2 = ((cy1 * 8191 + cx1) * 8191 + cy0 + tex_id) % P2
|
||||
local vax, vay = lx0 + ox, ly0 + oy
|
||||
h1, h2 = HashVertex(h1, h2, pr_a[p], vax, vay)
|
||||
h1, h2 = HashVertex(h1, h2, pr_b[p], vax, vay)
|
||||
h1, h2 = HashVertex(h1, h2, pr_c[p], vax, vay)
|
||||
local vd = pr_d[p]
|
||||
if vd then h1, h2 = HashVertex(h1, h2, vd, vax, vay) end
|
||||
for ty = floor((pr_by0[p] - ly0) / TILE), floor((pr_by1[p] - 1 - ly0) / TILE) do
|
||||
for tx = floor((pr_bx0[p] - lx0) / TILE), floor((pr_bx1[p] - 1 - lx0) / TILE) do
|
||||
local key = ty * KEY_W + tx
|
||||
local k = tn[key]
|
||||
if not k or k == 0 then
|
||||
k = 0
|
||||
th1[key], th2[key] = 0, 0
|
||||
if not tl[key] then tl[key] = {} end
|
||||
nt = nt + 1
|
||||
touched[nt] = key
|
||||
layer.TX[key], layer.TY[key] = tx, ty
|
||||
end
|
||||
k = k + 1
|
||||
tn[key] = k
|
||||
tl[key][k] = p
|
||||
th1[key] = (th1[key] * 48271 + h1) % P1
|
||||
th2[key] = (th2[key] * 16807 + h2 + k) % P2
|
||||
end
|
||||
end
|
||||
end
|
||||
layer.NTouched = nt
|
||||
PE()
|
||||
|
||||
-- Place the layer (this is all a window move costs)
|
||||
local root = layer.Root
|
||||
if root ~= layer.Frame then
|
||||
-- content: clip frame at the content clip rect, tiles frame at the scroll origin relative to it
|
||||
if layer.CX0 ~= clip_x0 or layer.CY0 ~= clip_y0 or layer.CX1 ~= clip_x1 or layer.CY1 ~= clip_y1 then
|
||||
layer.CX0, layer.CY0, layer.CX1, layer.CY1 = clip_x0, clip_y0, clip_x1, clip_y1
|
||||
root.Position = UDim2.fromOffset(clip_x0 / scale, clip_y0 / scale)
|
||||
root.Size = UDim2.fromOffset((clip_x1 - clip_x0) / scale, (clip_y1 - clip_y0) / scale)
|
||||
layer.X = false
|
||||
end
|
||||
if layer.X ~= lx0 or layer.Y ~= ly0 then
|
||||
layer.X, layer.Y = lx0, ly0
|
||||
layer.Frame.Position = UDim2.fromOffset((lx0 - clip_x0) / scale, (ly0 - clip_y0) / scale)
|
||||
end
|
||||
elseif layer.X ~= lx0 or layer.Y ~= ly0 then
|
||||
layer.X, layer.Y = lx0, ly0
|
||||
root.Position = UDim2.fromOffset(lx0 / scale, ly0 / scale)
|
||||
end
|
||||
if layer.Z ~= z then layer.Z = z; root.ZIndex = z end
|
||||
if not layer.Visible then layer.Visible = true; root.Visible = true end
|
||||
|
||||
-- Pass 3: redraw + upload changed tiles, hide unused ones
|
||||
PB("ImGui raster+upload")
|
||||
for key, tile in pairs(layer.Tiles) do
|
||||
if not tn[key] or tn[key] == 0 then
|
||||
if tile.Visible then tile.Visible = false; tile.Label.Visible = false; tile.H1 = -1 end
|
||||
end
|
||||
end
|
||||
for kk = 1, nt do
|
||||
local key = touched[kk]
|
||||
local count = tn[key]
|
||||
local tx, ty = layer.TX[key], layer.TY[key]
|
||||
local tile = GetTile(bd, layer, key, tx, ty)
|
||||
if force or tile.H1 ~= th1[key] or tile.H2 ~= th2[key] then
|
||||
tile.H1, tile.H2 = th1[key], th2[key]
|
||||
local x0, y0 = lx0 + tx * TILE, ly0 + ty * TILE
|
||||
local x1, y1 = x0 + TILE, y0 + TILE
|
||||
buf, buf_w = tile.Buffer, TILE
|
||||
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]
|
||||
local cx0, cy0 = max(pr_cx0[p], x0) - x0, max(pr_cy0[p], y0) - y0
|
||||
local cx1, cy1 = min(pr_cx1[p], x1) - x0, min(pr_cy1[p], y1) - y0
|
||||
local va, vc, vd = pr_a[p], pr_c[p], pr_d[p]
|
||||
if vd then
|
||||
local pa, pc, ta, tc = va[1], vc[1], va[2], vc[2]
|
||||
local gk = pr_g[p]
|
||||
if gk == 1 then
|
||||
DrawRectGradient(pa.x - tox, pa.y - toy, pc.x - tox, pc.y - toy, va[3], vc[3], 1, pr_t[p], ta.x, ta.y, cx0, cy0, cx1, cy1)
|
||||
elseif gk == 2 then
|
||||
DrawRectGradient(pa.x - tox, pa.y - toy, pc.x - tox, pc.y - toy, va[3], vc[3], 2, pr_t[p], ta.x, ta.y, cx0, cy0, cx1, cy1)
|
||||
else
|
||||
DrawRect(pa.x - tox, pa.y - toy, pc.x - tox, pc.y - toy, ta.x, ta.y, tc.x, tc.y, va[3], pr_t[p], cx0, cy0, cx1, cy1)
|
||||
end
|
||||
else
|
||||
DrawTriangle(va, pr_b[p], vc, pr_t[p], cx0, cy0, cx1, cy1, tox, toy)
|
||||
end
|
||||
end
|
||||
PB("ImGui upload")
|
||||
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
|
||||
tile.Visible = true
|
||||
tile.Label.Visible = true
|
||||
end
|
||||
end
|
||||
PE()
|
||||
end
|
||||
|
||||
@native
|
||||
function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||
local bd = ImGui_ImplRoblox_GetBackendData()
|
||||
@@ -593,7 +765,6 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||
local frame_no = bd.FrameNo
|
||||
local force = bd.ForceRedraw
|
||||
bd.ForceRedraw = false
|
||||
local scale = bd.Scale
|
||||
|
||||
local n = 0
|
||||
local z = 0
|
||||
@@ -601,8 +772,14 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||
-- Pass 1: collect this list's primitives and their bounds
|
||||
PB("ImGui collect")
|
||||
local first = n + 1
|
||||
local lx0, ly0 = math.huge, math.huge
|
||||
local vtx, idx = draw_list.VtxBuffer.Data, draw_list.IdxBuffer.Data
|
||||
local ccx0, ccy0, ccx1, ccy1 = draw_list._ContentClipX0, draw_list._ContentClipY0, draw_list._ContentClipX1, draw_list._ContentClipY1
|
||||
local content_x0, content_y0, content_x1, content_y1
|
||||
if ccx0 then
|
||||
content_x0, content_y0 = max(floor(ccx0 - ox), lim_x0), max(floor(ccy0 - oy), lim_y0)
|
||||
content_x1, content_y1 = min(floor(ccx1 - ox), lim_x1), min(floor(ccy1 - oy), lim_y1)
|
||||
end
|
||||
local seen_content = false
|
||||
for _, pcmd in draw_list.CmdBuffer:iter() do
|
||||
if pcmd.UserCallback ~= nil then
|
||||
pcmd.UserCallback(draw_list, pcmd)
|
||||
@@ -610,6 +787,16 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||
local clip = pcmd.ClipRect
|
||||
local cx0, cy0 = max(floor(clip.x - ox), lim_x0), max(floor(clip.y - oy), lim_y0)
|
||||
local cx1, cy1 = min(floor(clip.z - ox), lim_x1), min(floor(clip.w - oy), lim_y1)
|
||||
local kind = 1
|
||||
if ccx0 and clip.x >= ccx0 and clip.y >= ccy0 and clip.z <= ccx1 and clip.w <= ccy1 then
|
||||
kind = 2; seen_content = true
|
||||
if clip.x == ccx0 and clip.y == ccy0 and clip.z == ccx1 and clip.w == ccy1 then
|
||||
-- clipped by the layer's clip frame instead: partly visible rows stay hash-stable while scrolling
|
||||
cx0, cy0, cx1, cy1 = lim_x0, lim_y0, lim_x1, lim_y1
|
||||
end
|
||||
elseif seen_content then
|
||||
kind = 3
|
||||
end
|
||||
if cx1 > cx0 and cy1 > cy0 then
|
||||
local tex_id = pcmd:GetTexID()
|
||||
local t = bd.Textures[tex_id]
|
||||
@@ -658,8 +845,7 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||
pr_a[n], pr_b[n], pr_c[n], pr_d[n], pr_t[n], pr_g[n], pr_tex[n] = va, vb, vc, vd, t, gk, tex_id
|
||||
pr_cx0[n], pr_cy0[n], pr_cx1[n], pr_cy1[n] = cx0, cy0, cx1, cy1
|
||||
pr_bx0[n], pr_by0[n], pr_bx1[n], pr_by1[n] = bx0, by0, bx1, by1
|
||||
if bx0 < lx0 then lx0 = bx0 end
|
||||
if by0 < ly0 then ly0 = by0 end
|
||||
pr_k[n] = kind
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -667,147 +853,47 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||
end
|
||||
PE()
|
||||
|
||||
local layer = GetLayer(bd, draw_list)
|
||||
layer.LastUsed = frame_no
|
||||
if n < first then
|
||||
if layer.Visible then layer.Visible = false; layer.Frame.Visible = false end
|
||||
else
|
||||
-- Pass 2: hash primitives into layer-relative tiles
|
||||
PB("ImGui tile hash")
|
||||
local th1, th2, tn, tl, touched = layer.TH1, layer.TH2, layer.TN, layer.TL, layer.Touched
|
||||
for k = 1, layer.NTouched do tn[touched[k]] = 0 end
|
||||
local nt = 0
|
||||
for p = first, n do
|
||||
local tex_id = pr_tex[p]
|
||||
-- The clip rect only affects pixels inside the primitive's bounds, so hash the clipped bounds (relative)
|
||||
-- instead of the clip itself: a window clipped by the (fixed) viewport rect stays hash-stable when moved.
|
||||
local cx0, cy0, cx1, cy1 = pr_bx0[p] - lx0, pr_by0[p] - ly0, pr_bx1[p] - lx0, pr_by1[p] - ly0
|
||||
local h1 = ((cx0 * 8191 + cy0) * 8191 + cx1) % P1
|
||||
local h2 = ((cy1 * 8191 + cx1) * 8191 + cy0 + tex_id) % P2
|
||||
local vax, vay = lx0 + ox, ly0 + oy
|
||||
h1, h2 = HashVertex(h1, h2, pr_a[p], vax, vay)
|
||||
h1, h2 = HashVertex(h1, h2, pr_b[p], vax, vay)
|
||||
h1, h2 = HashVertex(h1, h2, pr_c[p], vax, vay)
|
||||
local vd = pr_d[p]
|
||||
if vd then h1, h2 = HashVertex(h1, h2, vd, vax, vay) end
|
||||
for ty = floor((pr_by0[p] - ly0) / TILE), floor((pr_by1[p] - 1 - ly0) / TILE) do
|
||||
for tx = floor((pr_bx0[p] - lx0) / TILE), floor((pr_bx1[p] - 1 - lx0) / TILE) do
|
||||
local key = ty * KEY_W + tx
|
||||
local k = tn[key]
|
||||
if not k or k == 0 then
|
||||
k = 0
|
||||
th1[key], th2[key] = 0, 0
|
||||
if not tl[key] then tl[key] = {} end
|
||||
nt = nt + 1
|
||||
touched[nt] = key
|
||||
end
|
||||
k = k + 1
|
||||
tn[key] = k
|
||||
tl[key][k] = p
|
||||
th1[key] = (th1[key] * 48271 + h1) % P1
|
||||
th2[key] = (th2[key] * 16807 + h2 + k) % P2
|
||||
end
|
||||
end
|
||||
-- Split into layer kinds and place each one
|
||||
local set = bd.Layers[draw_list]
|
||||
local nk1, nk2, nk3 = 0, 0, 0
|
||||
local o1x, o1y, o3x, o3y = math.huge, math.huge, math.huge, math.huge
|
||||
for p = first, n do
|
||||
local k = pr_k[p]
|
||||
if k == 1 then
|
||||
nk1 += 1; plist1[nk1] = p
|
||||
if pr_bx0[p] < o1x then o1x = pr_bx0[p] end
|
||||
if pr_by0[p] < o1y then o1y = pr_by0[p] end
|
||||
elseif k == 2 then
|
||||
nk2 += 1; plist2[nk2] = p
|
||||
else
|
||||
nk3 += 1; plist3[nk3] = p
|
||||
if pr_bx0[p] < o3x then o3x = pr_bx0[p] end
|
||||
if pr_by0[p] < o3y then o3y = pr_by0[p] end
|
||||
end
|
||||
layer.NTouched = nt
|
||||
PE()
|
||||
|
||||
-- Place the layer (this is all a window move costs)
|
||||
local frame = layer.Frame
|
||||
if layer.X ~= lx0 or layer.Y ~= ly0 then
|
||||
layer.X, layer.Y = lx0, ly0
|
||||
frame.Position = UDim2.fromOffset(lx0 / scale, ly0 / scale)
|
||||
end
|
||||
z = z + 1
|
||||
if layer.Z ~= z then layer.Z = z; frame.ZIndex = z end
|
||||
if not layer.Visible then layer.Visible = true; frame.Visible = true end
|
||||
|
||||
-- Pass 3: redraw + upload changed tiles, hide unused ones
|
||||
PB("ImGui raster+upload")
|
||||
for key, tile in pairs(layer.Tiles) do
|
||||
if not tn[key] or tn[key] == 0 then
|
||||
if tile.Visible then tile.Visible = false; tile.Label.Visible = false; tile.H1 = -1 end
|
||||
end
|
||||
end
|
||||
for kk = 1, nt do
|
||||
local key = touched[kk]
|
||||
local count = tn[key]
|
||||
local tx, ty = key % KEY_W, floor(key / KEY_W)
|
||||
if tx < 0 then tx = tx + KEY_W; ty = ty - 1 end
|
||||
local tile = GetTile(bd, layer, key, tx, ty)
|
||||
if force or tile.H1 ~= th1[key] or tile.H2 ~= th2[key] then
|
||||
tile.H1, tile.H2 = th1[key], th2[key]
|
||||
local x0, y0 = lx0 + tx * TILE, ly0 + ty * TILE
|
||||
local x1, y1 = x0 + TILE, y0 + TILE
|
||||
buf, buf_w = tile.Buffer, TILE
|
||||
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]
|
||||
local cx0, cy0 = max(pr_cx0[p], x0) - x0, max(pr_cy0[p], y0) - y0
|
||||
local cx1, cy1 = min(pr_cx1[p], x1) - x0, min(pr_cy1[p], y1) - y0
|
||||
local va, vc, vd = pr_a[p], pr_c[p], pr_d[p]
|
||||
if vd then
|
||||
local pa, pc, ta, tc = va[1], vc[1], va[2], vc[2]
|
||||
local gk = pr_g[p]
|
||||
if gk == 1 then
|
||||
DrawRectGradient(pa.x - tox, pa.y - toy, pc.x - tox, pc.y - toy, va[3], vc[3], 1, pr_t[p], ta.x, ta.y, cx0, cy0, cx1, cy1)
|
||||
elseif gk == 2 then
|
||||
DrawRectGradient(pa.x - tox, pa.y - toy, pc.x - tox, pc.y - toy, va[3], vc[3], 2, pr_t[p], ta.x, ta.y, cx0, cy0, cx1, cy1)
|
||||
else
|
||||
DrawRect(pa.x - tox, pa.y - toy, pc.x - tox, pc.y - toy, ta.x, ta.y, tc.x, tc.y, va[3], pr_t[p], cx0, cy0, cx1, cy1)
|
||||
end
|
||||
else
|
||||
DrawTriangle(va, pr_b[p], vc, pr_t[p], cx0, cy0, cx1, cy1, tox, toy)
|
||||
end
|
||||
end
|
||||
PB("ImGui upload")
|
||||
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
|
||||
tile.Visible = true
|
||||
tile.Label.Visible = true
|
||||
end
|
||||
end
|
||||
PE()
|
||||
end
|
||||
if nk1 > 0 then
|
||||
z += 1
|
||||
DrawLayer(bd, GetLayer(bd, draw_list, 1), plist1, nk1, o1x, o1y, ox, oy, force, z)
|
||||
elseif set and set[1] then HideLayer(set[1]) end
|
||||
if nk2 > 0 then
|
||||
z += 1
|
||||
DrawLayer(bd, GetLayer(bd, draw_list, 2), plist2, nk2, floor(draw_list._ScrollOriginX - ox), floor(draw_list._ScrollOriginY - oy), ox, oy, force, z,
|
||||
content_x0, content_y0, content_x1, content_y1)
|
||||
elseif set and set[2] then HideLayer(set[2]) end
|
||||
if nk3 > 0 then
|
||||
z += 1
|
||||
DrawLayer(bd, GetLayer(bd, draw_list, 3), plist3, nk3, o3x, o3y, ox, oy, force, z)
|
||||
elseif set and set[3] then HideLayer(set[3]) end
|
||||
set = bd.Layers[draw_list]
|
||||
if set then set.LastUsed = frame_no end
|
||||
end
|
||||
|
||||
-- Hide layers not drawn this frame; free long unused ones (closed windows, old tooltips)
|
||||
for draw_list, layer in pairs(bd.Layers) do
|
||||
if layer.LastUsed ~= frame_no then
|
||||
if layer.Visible then layer.Visible = false; layer.Frame.Visible = false end
|
||||
if frame_no - layer.LastUsed > LAYER_KEEP_FRAMES then
|
||||
DestroyLayer(layer)
|
||||
for draw_list, set in pairs(bd.Layers) do
|
||||
if set.LastUsed ~= frame_no then
|
||||
for kind = 1, 3 do if set[kind] then HideLayer(set[kind]) end end
|
||||
if frame_no - set.LastUsed > LAYER_KEEP_FRAMES then
|
||||
DestroyLayerSet(set)
|
||||
bd.Layers[draw_list] = nil
|
||||
end
|
||||
end
|
||||
@@ -941,7 +1027,7 @@ function ImGui_ImplRoblox_NewFrame()
|
||||
local want = io.WantCaptureMouse
|
||||
if want ~= bd.TilesActive then
|
||||
bd.TilesActive = want
|
||||
for _, layer in pairs(bd.Layers) do for _, tile in pairs(layer.Tiles) do tile.Label.Active = want end end
|
||||
for _, set in pairs(bd.Layers) do for kind = 1, 3 do local layer = set[kind]; if layer then for _, tile in pairs(layer.Tiles) do tile.Label.Active = want end end end end
|
||||
end
|
||||
PE()
|
||||
end
|
||||
|
||||
@@ -7150,6 +7150,10 @@ function ImGui.Begin(name, open, flags)
|
||||
|
||||
if bit32.band(flags, ImGuiWindowFlags.DockNodeHost) == 0 and not window.SkipRefresh then
|
||||
ImGui.PushClipRect(window.InnerClipRect.Min, window.InnerClipRect.Max, true)
|
||||
-- [Roblox backend] content clip + scroll origin: lets the backend move scrolled content instead of redrawing it
|
||||
local dl, cr = window.DrawList, window.DrawList._CmdHeader.ClipRect
|
||||
dl._ContentClipX0, dl._ContentClipY0, dl._ContentClipX1, dl._ContentClipY1 = cr.x, cr.y, cr.z, cr.w
|
||||
dl._ScrollOriginX, dl._ScrollOriginY = window.Pos.x - window.Scroll.x, window.Pos.y - window.Scroll.y
|
||||
end
|
||||
|
||||
window.WriteAccessed = false
|
||||
|
||||
@@ -3158,6 +3158,7 @@ end
|
||||
|
||||
-- TODO:
|
||||
function MT.ImDrawList:_ResetForNewFrame()
|
||||
self._ContentClipX0 = nil -- set again by Begin() (Roblox backend scroll reuse)
|
||||
if self._Splitter._Count > 1 then
|
||||
self._Splitter:Merge(self)
|
||||
end
|
||||
|
||||
@@ -13,21 +13,26 @@ local frame = 0
|
||||
|
||||
local function composite(gui)
|
||||
local screen = buffer.create(W * H * 3)
|
||||
local function walk(inst, ox, oy)
|
||||
local function walk(inst, ox, oy, kx0, ky0, kx1, ky1)
|
||||
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)
|
||||
local cx0, cy0, cx1, cy1 = kx0, ky0, kx1, ky1
|
||||
if c.ClipsDescendants then
|
||||
cx0, cy0 = math.max(cx0, x), math.max(cy0, y)
|
||||
cx1, cy1 = math.min(cx1, x + c.Size.X), math.min(cy1, y + c.Size.Y)
|
||||
end
|
||||
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
|
||||
if sy >= ky0 and sy < ky1 then
|
||||
for tx = 0, sz - 1 do
|
||||
local sx = x + tx
|
||||
if sx >= 0 and sx < W then
|
||||
if sx >= kx0 and sx < kx1 then
|
||||
local o = (ty * sz + tx) * 4
|
||||
local a = buffer.readu8(px, o + 3)
|
||||
if a > 0 then
|
||||
@@ -43,11 +48,11 @@ local function composite(gui)
|
||||
end
|
||||
end
|
||||
end
|
||||
walk(c, x, y)
|
||||
walk(c, x, y, cx0, cy0, cx1, cy1)
|
||||
end
|
||||
end
|
||||
end
|
||||
walk(gui, 0, 0)
|
||||
walk(gui, 0, 0, 0, 0, W, H)
|
||||
return screen
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user