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"]]
|
||||||
+237
-146
@@ -7161,6 +7161,10 @@ function ImGui.Begin(name, open, flags)
|
|||||||
|
|
||||||
if bit32.band(flags, ImGuiWindowFlags.DockNodeHost) == 0 and not window.SkipRefresh then
|
if bit32.band(flags, ImGuiWindowFlags.DockNodeHost) == 0 and not window.SkipRefresh then
|
||||||
ImGui.PushClipRect(window.InnerClipRect.Min, window.InnerClipRect.Max, true)
|
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
|
end
|
||||||
|
|
||||||
window.WriteAccessed = false
|
window.WriteAccessed = false
|
||||||
@@ -28889,6 +28893,7 @@ end
|
|||||||
|
|
||||||
-- TODO:
|
-- TODO:
|
||||||
function MT.ImDrawList:_ResetForNewFrame()
|
function MT.ImDrawList:_ResetForNewFrame()
|
||||||
|
self._ContentClipX0 = nil -- set again by Begin() (Roblox backend scroll reuse)
|
||||||
if self._Splitter._Count > 1 then
|
if self._Splitter._Count > 1 then
|
||||||
self._Splitter:Merge(self)
|
self._Splitter:Merge(self)
|
||||||
end
|
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
|
-- 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.
|
-- 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.
|
-- 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)
|
TILE_SIZE = Vector2.new(TILE, TILE)
|
||||||
local P1, P2 = 2147483647, 2147483629 -- two independent 31 bit hashes per tile (collision odds ~2^-62)
|
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)
|
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_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_bx0, pr_by0, pr_bx1, pr_by1 = {}, {}, {}, {} -- clipped bounds (screen pixels)
|
||||||
local pr_tex = {} -- texture id
|
local pr_tex = {} -- texture id
|
||||||
|
local pr_k = {} -- layer kind (1..3)
|
||||||
|
local plist1, plist2, plist3 = {}, {}, {} -- primitive indices per layer kind
|
||||||
|
|
||||||
@native
|
@native
|
||||||
local function HashVertex(h1, h2, v, ox, oy)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
local function GetLayer(bd, draw_list)
|
-- Layer kinds per draw list: 1 = drawn before the window content (background, title bar, borders, scrollbar),
|
||||||
local layer = bd.Layers[draw_list]
|
-- 2 = window content (positioned at the scroll origin inside a ClipsDescendants frame, so scrolling just moves it),
|
||||||
if layer then return layer end
|
-- 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")
|
local frame = Instance.new("Frame")
|
||||||
frame.Name = "Layer"
|
frame.Name = "Layer"
|
||||||
frame.BackgroundTransparency = 1
|
frame.BackgroundTransparency = 1
|
||||||
frame.BorderSizePixel = 0
|
frame.BorderSizePixel = 0
|
||||||
frame.Size = UDim2.fromOffset(0, 0)
|
frame.Size = UDim2.fromOffset(0, 0)
|
||||||
|
frame.ClipsDescendants = clips
|
||||||
frame.Visible = false
|
frame.Visible = false
|
||||||
frame.Parent = bd.Gui
|
frame.Parent = parent
|
||||||
layer = { Frame = frame, Tiles = {}, TH1 = {}, TH2 = {}, TN = {}, TL = {}, Touched = {}, NTouched = 0,
|
return frame
|
||||||
Visible = false, X = false, Y = false, Z = -1, LastUsed = 0 }
|
end
|
||||||
bd.Layers[draw_list] = layer
|
|
||||||
|
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
|
return layer
|
||||||
end
|
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 function GetTile(bd, layer, key, tx, ty)
|
||||||
local tile = layer.Tiles[key]
|
local tile = layer.Tiles[key]
|
||||||
if tile then return tile end
|
if tile then return tile end
|
||||||
@@ -33685,13 +33713,18 @@ local function GetTile(bd, layer, key, tx, ty)
|
|||||||
return tile
|
return tile
|
||||||
end
|
end
|
||||||
|
|
||||||
local function DestroyLayer(layer)
|
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
|
for _, tile in pairs(layer.Tiles) do tile.Image:Destroy() end
|
||||||
layer.Frame:Destroy()
|
layer.Root:Destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function DestroyTiles(bd)
|
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 = {}
|
bd.Layers = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -33702,134 +33735,15 @@ local function PB(name) if profiling then profilebegin(name) end end
|
|||||||
local function PE() if profiling then profileend() end end
|
local function PE() if profiling then profileend() end end
|
||||||
|
|
||||||
@native
|
@native
|
||||||
function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_x0, clip_y0, clip_x1, clip_y1)
|
||||||
local bd = ImGui_ImplRoblox_GetBackendData()
|
|
||||||
PB("ImGui RenderDrawData")
|
|
||||||
|
|
||||||
PB("ImGui textures")
|
|
||||||
if draw_data.Textures ~= nil then
|
|
||||||
for _, tex in draw_data.Textures:iter() do
|
|
||||||
if tex.Status ~= ImTextureStatus.OK then
|
|
||||||
ImGui_ImplRoblox_UpdateTexture(tex)
|
|
||||||
bd.ForceRedraw = true -- texture contents changed: tiles sampling it must be redrawn
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
PE()
|
|
||||||
|
|
||||||
-- Render rate cap: textures are always updated (imgui expects it), tiles only at bd.RenderRate Hz
|
|
||||||
-- Optional lower cap while scrolling / resizing / moving a window (SetBusyRenderRate, off by default)
|
|
||||||
local rate = bd.RenderRate
|
|
||||||
if bd.BusyRenderRate > 0 then
|
|
||||||
local g = ImGui.GetCurrentContext()
|
|
||||||
local busy = g.WheelingWindow ~= nil or g.MovingWindow ~= nil -- (io.MouseWheel is already cleared by EndFrame)
|
|
||||||
if not busy and g.ActiveId ~= 0 then
|
|
||||||
local w = g.ActiveIdWindow
|
|
||||||
local cur = g.MouseCursor -- ResizeAll..ResizeNWSE while a resize grip/border is held
|
|
||||||
busy = (cur >= ImGuiMouseCursor.ResizeAll and cur <= ImGuiMouseCursor.ResizeNWSE)
|
|
||||||
or (w ~= nil and (g.ActiveId == ImGui.GetWindowScrollbarID(w, ImGuiAxis.X) or g.ActiveId == ImGui.GetWindowScrollbarID(w, ImGuiAxis.Y)))
|
|
||||||
end
|
|
||||||
if busy and (rate <= 0 or bd.BusyRenderRate < rate) then rate = bd.BusyRenderRate end
|
|
||||||
end
|
|
||||||
if rate > 0 then
|
|
||||||
local now = os.clock()
|
|
||||||
if now + 0.002 < bd.NextRender then PE(); return end
|
|
||||||
bd.NextRender = math.max(bd.NextRender + 1 / rate, now)
|
|
||||||
end
|
|
||||||
|
|
||||||
local sw, sh = floor(draw_data.DisplaySize.x), floor(draw_data.DisplaySize.y)
|
|
||||||
if sw <= 0 or sh <= 0 then PE(); return end
|
|
||||||
local ox, oy = draw_data.DisplayPos.x, draw_data.DisplayPos.y
|
|
||||||
local lim_x0, lim_y0, lim_x1, lim_y1 = -CLIP_PAD, -CLIP_PAD, sw + CLIP_PAD, sh + CLIP_PAD
|
|
||||||
bd.FrameNo += 1
|
|
||||||
local frame_no = bd.FrameNo
|
|
||||||
local force = bd.ForceRedraw
|
|
||||||
bd.ForceRedraw = false
|
|
||||||
local scale = bd.Scale
|
local scale = bd.Scale
|
||||||
|
|
||||||
local n = 0
|
|
||||||
local z = 0
|
|
||||||
for _, draw_list in draw_data.CmdLists:iter() do
|
|
||||||
-- 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
|
|
||||||
for _, pcmd in draw_list.CmdBuffer:iter() do
|
|
||||||
if pcmd.UserCallback ~= nil then
|
|
||||||
pcmd.UserCallback(draw_list, pcmd)
|
|
||||||
elseif pcmd.ElemCount > 0 then
|
|
||||||
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)
|
|
||||||
if cx1 > cx0 and cy1 > cy0 then
|
|
||||||
local tex_id = pcmd:GetTexID()
|
|
||||||
local t = bd.Textures[tex_id]
|
|
||||||
local vo = pcmd.VtxOffset
|
|
||||||
local i, last = pcmd.IdxOffset + 1, pcmd.IdxOffset + pcmd.ElemCount
|
|
||||||
while i <= last do
|
|
||||||
local ia, ic = idx[i], idx[i + 2]
|
|
||||||
local va, vb, vc = vtx[vo + ia], vtx[vo + idx[i + 1]], vtx[vo + ic]
|
|
||||||
local vd = false
|
|
||||||
local gk = false
|
|
||||||
local pa, pb, pc = va[1], vb[1], vc[1]
|
|
||||||
local bx0, by0, bx1, by1
|
|
||||||
if i + 5 <= last and idx[i + 3] == ia and idx[i + 4] == ic then
|
|
||||||
local d = vtx[vo + idx[i + 5]]
|
|
||||||
local pd = d[1]
|
|
||||||
-- axis aligned quad (TL, TR, BR, BL)
|
|
||||||
if pa.y == pb.y and pb.x == pc.x and pc.y == pd.y and pd.x == pa.x and pa.x < pb.x and pa.y < pd.y then
|
|
||||||
local col = va[3]
|
|
||||||
local ta, tb, tc, td = va[2], vb[2], vc[2], d[2]
|
|
||||||
if vb[3] == col and vc[3] == col and d[3] == col then
|
|
||||||
if tb.x == tc.x and tb.y == ta.y and td.x == ta.x and td.y == tc.y then vd = d end
|
|
||||||
elseif ta.x == tb.x and ta.x == tc.x and ta.x == td.x and ta.y == tb.y and ta.y == tc.y and ta.y == td.y then
|
|
||||||
-- constant uv, linear gradient: vertical (top/bottom pairs equal) or horizontal (left/right pairs equal)
|
|
||||||
if vb[3] == col and d[3] == vc[3] then vd = d; gk = 1
|
|
||||||
elseif d[3] == col and vb[3] == vc[3] then vd = d; gk = 2 end
|
|
||||||
end
|
|
||||||
if vd then
|
|
||||||
bx0, by0 = ceil(pa.x - ox - 0.5), ceil(pa.y - oy - 0.5)
|
|
||||||
bx1, by1 = ceil(pc.x - ox - 0.5), ceil(pc.y - oy - 0.5)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if vd then
|
|
||||||
i = i + 6
|
|
||||||
else
|
|
||||||
bx0, by0 = floor(min(pa.x, pb.x, pc.x) - ox), floor(min(pa.y, pb.y, pc.y) - oy)
|
|
||||||
bx1, by1 = ceil(max(pa.x, pb.x, pc.x) - ox) + 1, ceil(max(pa.y, pb.y, pc.y) - oy) + 1
|
|
||||||
i = i + 3
|
|
||||||
end
|
|
||||||
if bx0 < cx0 then bx0 = cx0 end
|
|
||||||
if by0 < cy0 then by0 = cy0 end
|
|
||||||
if bx1 > cx1 then bx1 = cx1 end
|
|
||||||
if by1 > cy1 then by1 = cy1 end
|
|
||||||
if bx0 < bx1 and by0 < by1 then
|
|
||||||
n = n + 1
|
|
||||||
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
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
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
|
-- Pass 2: hash primitives into layer-relative tiles
|
||||||
PB("ImGui tile hash")
|
PB("ImGui tile hash")
|
||||||
local th1, th2, tn, tl, touched = layer.TH1, layer.TH2, layer.TN, layer.TL, layer.Touched
|
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
|
for k = 1, layer.NTouched do tn[touched[k]] = 0 end
|
||||||
local nt = 0
|
local nt = 0
|
||||||
for p = first, n do
|
for pi = 1, np do
|
||||||
|
local p = plist[pi]
|
||||||
local tex_id = pr_tex[p]
|
local tex_id = pr_tex[p]
|
||||||
-- The clip rect only affects pixels inside the primitive's bounds, so hash the clipped bounds (relative)
|
-- 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.
|
-- instead of the clip itself: a window clipped by the (fixed) viewport rect stays hash-stable when moved.
|
||||||
@@ -33852,6 +33766,7 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
if not tl[key] then tl[key] = {} end
|
if not tl[key] then tl[key] = {} end
|
||||||
nt = nt + 1
|
nt = nt + 1
|
||||||
touched[nt] = key
|
touched[nt] = key
|
||||||
|
layer.TX[key], layer.TY[key] = tx, ty
|
||||||
end
|
end
|
||||||
k = k + 1
|
k = k + 1
|
||||||
tn[key] = k
|
tn[key] = k
|
||||||
@@ -33865,14 +33780,25 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
PE()
|
PE()
|
||||||
|
|
||||||
-- Place the layer (this is all a window move costs)
|
-- Place the layer (this is all a window move costs)
|
||||||
local frame = layer.Frame
|
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
|
if layer.X ~= lx0 or layer.Y ~= ly0 then
|
||||||
layer.X, layer.Y = lx0, ly0
|
layer.X, layer.Y = lx0, ly0
|
||||||
frame.Position = UDim2.fromOffset(lx0 / scale, ly0 / scale)
|
layer.Frame.Position = UDim2.fromOffset((lx0 - clip_x0) / scale, (ly0 - clip_y0) / scale)
|
||||||
end
|
end
|
||||||
z = z + 1
|
elseif layer.X ~= lx0 or layer.Y ~= ly0 then
|
||||||
if layer.Z ~= z then layer.Z = z; frame.ZIndex = z end
|
layer.X, layer.Y = lx0, ly0
|
||||||
if not layer.Visible then layer.Visible = true; frame.Visible = true end
|
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
|
-- Pass 3: redraw + upload changed tiles, hide unused ones
|
||||||
PB("ImGui raster+upload")
|
PB("ImGui raster+upload")
|
||||||
@@ -33884,8 +33810,7 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
for kk = 1, nt do
|
for kk = 1, nt do
|
||||||
local key = touched[kk]
|
local key = touched[kk]
|
||||||
local count = tn[key]
|
local count = tn[key]
|
||||||
local tx, ty = key % KEY_W, floor(key / KEY_W)
|
local tx, ty = layer.TX[key], layer.TY[key]
|
||||||
if tx < 0 then tx = tx + KEY_W; ty = ty - 1 end
|
|
||||||
local tile = GetTile(bd, layer, key, tx, ty)
|
local tile = GetTile(bd, layer, key, tx, ty)
|
||||||
if force or tile.H1 ~= th1[key] or tile.H2 ~= th2[key] then
|
if force or tile.H1 ~= th1[key] or tile.H2 ~= th2[key] then
|
||||||
tile.H1, tile.H2 = th1[key], th2[key]
|
tile.H1, tile.H2 = th1[key], th2[key]
|
||||||
@@ -33952,14 +33877,180 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
end
|
end
|
||||||
PE()
|
PE()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@native
|
||||||
|
function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||||
|
local bd = ImGui_ImplRoblox_GetBackendData()
|
||||||
|
PB("ImGui RenderDrawData")
|
||||||
|
|
||||||
|
PB("ImGui textures")
|
||||||
|
if draw_data.Textures ~= nil then
|
||||||
|
for _, tex in draw_data.Textures:iter() do
|
||||||
|
if tex.Status ~= ImTextureStatus.OK then
|
||||||
|
ImGui_ImplRoblox_UpdateTexture(tex)
|
||||||
|
bd.ForceRedraw = true -- texture contents changed: tiles sampling it must be redrawn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
PE()
|
||||||
|
|
||||||
|
-- Render rate cap: textures are always updated (imgui expects it), tiles only at bd.RenderRate Hz
|
||||||
|
-- Optional lower cap while scrolling / resizing / moving a window (SetBusyRenderRate, off by default)
|
||||||
|
local rate = bd.RenderRate
|
||||||
|
if bd.BusyRenderRate > 0 then
|
||||||
|
local g = ImGui.GetCurrentContext()
|
||||||
|
local busy = g.WheelingWindow ~= nil or g.MovingWindow ~= nil -- (io.MouseWheel is already cleared by EndFrame)
|
||||||
|
if not busy and g.ActiveId ~= 0 then
|
||||||
|
local w = g.ActiveIdWindow
|
||||||
|
local cur = g.MouseCursor -- ResizeAll..ResizeNWSE while a resize grip/border is held
|
||||||
|
busy = (cur >= ImGuiMouseCursor.ResizeAll and cur <= ImGuiMouseCursor.ResizeNWSE)
|
||||||
|
or (w ~= nil and (g.ActiveId == ImGui.GetWindowScrollbarID(w, ImGuiAxis.X) or g.ActiveId == ImGui.GetWindowScrollbarID(w, ImGuiAxis.Y)))
|
||||||
|
end
|
||||||
|
if busy and (rate <= 0 or bd.BusyRenderRate < rate) then rate = bd.BusyRenderRate end
|
||||||
|
end
|
||||||
|
if rate > 0 then
|
||||||
|
local now = os.clock()
|
||||||
|
if now + 0.002 < bd.NextRender then PE(); return end
|
||||||
|
bd.NextRender = math.max(bd.NextRender + 1 / rate, now)
|
||||||
|
end
|
||||||
|
|
||||||
|
local sw, sh = floor(draw_data.DisplaySize.x), floor(draw_data.DisplaySize.y)
|
||||||
|
if sw <= 0 or sh <= 0 then PE(); return end
|
||||||
|
local ox, oy = draw_data.DisplayPos.x, draw_data.DisplayPos.y
|
||||||
|
local lim_x0, lim_y0, lim_x1, lim_y1 = -CLIP_PAD, -CLIP_PAD, sw + CLIP_PAD, sh + CLIP_PAD
|
||||||
|
bd.FrameNo += 1
|
||||||
|
local frame_no = bd.FrameNo
|
||||||
|
local force = bd.ForceRedraw
|
||||||
|
bd.ForceRedraw = false
|
||||||
|
|
||||||
|
local n = 0
|
||||||
|
local z = 0
|
||||||
|
for _, draw_list in draw_data.CmdLists:iter() do
|
||||||
|
-- Pass 1: collect this list's primitives and their bounds
|
||||||
|
PB("ImGui collect")
|
||||||
|
local first = n + 1
|
||||||
|
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)
|
||||||
|
elseif pcmd.ElemCount > 0 then
|
||||||
|
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]
|
||||||
|
local vo = pcmd.VtxOffset
|
||||||
|
local i, last = pcmd.IdxOffset + 1, pcmd.IdxOffset + pcmd.ElemCount
|
||||||
|
while i <= last do
|
||||||
|
local ia, ic = idx[i], idx[i + 2]
|
||||||
|
local va, vb, vc = vtx[vo + ia], vtx[vo + idx[i + 1]], vtx[vo + ic]
|
||||||
|
local vd = false
|
||||||
|
local gk = false
|
||||||
|
local pa, pb, pc = va[1], vb[1], vc[1]
|
||||||
|
local bx0, by0, bx1, by1
|
||||||
|
if i + 5 <= last and idx[i + 3] == ia and idx[i + 4] == ic then
|
||||||
|
local d = vtx[vo + idx[i + 5]]
|
||||||
|
local pd = d[1]
|
||||||
|
-- axis aligned quad (TL, TR, BR, BL)
|
||||||
|
if pa.y == pb.y and pb.x == pc.x and pc.y == pd.y and pd.x == pa.x and pa.x < pb.x and pa.y < pd.y then
|
||||||
|
local col = va[3]
|
||||||
|
local ta, tb, tc, td = va[2], vb[2], vc[2], d[2]
|
||||||
|
if vb[3] == col and vc[3] == col and d[3] == col then
|
||||||
|
if tb.x == tc.x and tb.y == ta.y and td.x == ta.x and td.y == tc.y then vd = d end
|
||||||
|
elseif ta.x == tb.x and ta.x == tc.x and ta.x == td.x and ta.y == tb.y and ta.y == tc.y and ta.y == td.y then
|
||||||
|
-- constant uv, linear gradient: vertical (top/bottom pairs equal) or horizontal (left/right pairs equal)
|
||||||
|
if vb[3] == col and d[3] == vc[3] then vd = d; gk = 1
|
||||||
|
elseif d[3] == col and vb[3] == vc[3] then vd = d; gk = 2 end
|
||||||
|
end
|
||||||
|
if vd then
|
||||||
|
bx0, by0 = ceil(pa.x - ox - 0.5), ceil(pa.y - oy - 0.5)
|
||||||
|
bx1, by1 = ceil(pc.x - ox - 0.5), ceil(pc.y - oy - 0.5)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if vd then
|
||||||
|
i = i + 6
|
||||||
|
else
|
||||||
|
bx0, by0 = floor(min(pa.x, pb.x, pc.x) - ox), floor(min(pa.y, pb.y, pc.y) - oy)
|
||||||
|
bx1, by1 = ceil(max(pa.x, pb.x, pc.x) - ox) + 1, ceil(max(pa.y, pb.y, pc.y) - oy) + 1
|
||||||
|
i = i + 3
|
||||||
|
end
|
||||||
|
if bx0 < cx0 then bx0 = cx0 end
|
||||||
|
if by0 < cy0 then by0 = cy0 end
|
||||||
|
if bx1 > cx1 then bx1 = cx1 end
|
||||||
|
if by1 > cy1 then by1 = cy1 end
|
||||||
|
if bx0 < bx1 and by0 < by1 then
|
||||||
|
n = n + 1
|
||||||
|
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
|
||||||
|
pr_k[n] = kind
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
PE()
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
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
|
end
|
||||||
|
|
||||||
-- Hide layers not drawn this frame; free long unused ones (closed windows, old tooltips)
|
-- Hide layers not drawn this frame; free long unused ones (closed windows, old tooltips)
|
||||||
for draw_list, layer in pairs(bd.Layers) do
|
for draw_list, set in pairs(bd.Layers) do
|
||||||
if layer.LastUsed ~= frame_no then
|
if set.LastUsed ~= frame_no then
|
||||||
if layer.Visible then layer.Visible = false; layer.Frame.Visible = false end
|
for kind = 1, 3 do if set[kind] then HideLayer(set[kind]) end end
|
||||||
if frame_no - layer.LastUsed > LAYER_KEEP_FRAMES then
|
if frame_no - set.LastUsed > LAYER_KEEP_FRAMES then
|
||||||
DestroyLayer(layer)
|
DestroyLayerSet(set)
|
||||||
bd.Layers[draw_list] = nil
|
bd.Layers[draw_list] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -34093,7 +34184,7 @@ function ImGui_ImplRoblox_NewFrame()
|
|||||||
local want = io.WantCaptureMouse
|
local want = io.WantCaptureMouse
|
||||||
if want ~= bd.TilesActive then
|
if want ~= bd.TilesActive then
|
||||||
bd.TilesActive = want
|
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
|
end
|
||||||
PE()
|
PE()
|
||||||
end
|
end
|
||||||
|
|||||||
+232
-146
@@ -421,7 +421,7 @@ end
|
|||||||
-- relative to the layer's origin (top-left of its content). Primitives are hashed into tiles in layer-relative
|
-- 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.
|
-- 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.
|
-- 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)
|
TILE_SIZE = Vector2.new(TILE, TILE)
|
||||||
local P1, P2 = 2147483647, 2147483629 -- two independent 31 bit hashes per tile (collision odds ~2^-62)
|
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)
|
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_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_bx0, pr_by0, pr_bx1, pr_by1 = {}, {}, {}, {} -- clipped bounds (screen pixels)
|
||||||
local pr_tex = {} -- texture id
|
local pr_tex = {} -- texture id
|
||||||
|
local pr_k = {} -- layer kind (1..3)
|
||||||
|
local plist1, plist2, plist3 = {}, {}, {} -- primitive indices per layer kind
|
||||||
|
|
||||||
@native
|
@native
|
||||||
local function HashVertex(h1, h2, v, ox, oy)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
local function GetLayer(bd, draw_list)
|
-- Layer kinds per draw list: 1 = drawn before the window content (background, title bar, borders, scrollbar),
|
||||||
local layer = bd.Layers[draw_list]
|
-- 2 = window content (positioned at the scroll origin inside a ClipsDescendants frame, so scrolling just moves it),
|
||||||
if layer then return layer end
|
-- 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")
|
local frame = Instance.new("Frame")
|
||||||
frame.Name = "Layer"
|
frame.Name = "Layer"
|
||||||
frame.BackgroundTransparency = 1
|
frame.BackgroundTransparency = 1
|
||||||
frame.BorderSizePixel = 0
|
frame.BorderSizePixel = 0
|
||||||
frame.Size = UDim2.fromOffset(0, 0)
|
frame.Size = UDim2.fromOffset(0, 0)
|
||||||
|
frame.ClipsDescendants = clips
|
||||||
frame.Visible = false
|
frame.Visible = false
|
||||||
frame.Parent = bd.Gui
|
frame.Parent = parent
|
||||||
layer = { Frame = frame, Tiles = {}, TH1 = {}, TH2 = {}, TN = {}, TL = {}, Touched = {}, NTouched = 0,
|
return frame
|
||||||
Visible = false, X = false, Y = false, Z = -1, LastUsed = 0 }
|
end
|
||||||
bd.Layers[draw_list] = layer
|
|
||||||
|
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
|
return layer
|
||||||
end
|
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 function GetTile(bd, layer, key, tx, ty)
|
||||||
local tile = layer.Tiles[key]
|
local tile = layer.Tiles[key]
|
||||||
if tile then return tile end
|
if tile then return tile end
|
||||||
@@ -533,13 +556,18 @@ local function GetTile(bd, layer, key, tx, ty)
|
|||||||
return tile
|
return tile
|
||||||
end
|
end
|
||||||
|
|
||||||
local function DestroyLayer(layer)
|
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
|
for _, tile in pairs(layer.Tiles) do tile.Image:Destroy() end
|
||||||
layer.Frame:Destroy()
|
layer.Root:Destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function DestroyTiles(bd)
|
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 = {}
|
bd.Layers = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -550,134 +578,15 @@ local function PB(name) if profiling then profilebegin(name) end end
|
|||||||
local function PE() if profiling then profileend() end end
|
local function PE() if profiling then profileend() end end
|
||||||
|
|
||||||
@native
|
@native
|
||||||
function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
local function DrawLayer(bd, layer, plist, np, lx0, ly0, ox, oy, force, z, clip_x0, clip_y0, clip_x1, clip_y1)
|
||||||
local bd = ImGui_ImplRoblox_GetBackendData()
|
|
||||||
PB("ImGui RenderDrawData")
|
|
||||||
|
|
||||||
PB("ImGui textures")
|
|
||||||
if draw_data.Textures ~= nil then
|
|
||||||
for _, tex in draw_data.Textures:iter() do
|
|
||||||
if tex.Status ~= ImTextureStatus.OK then
|
|
||||||
ImGui_ImplRoblox_UpdateTexture(tex)
|
|
||||||
bd.ForceRedraw = true -- texture contents changed: tiles sampling it must be redrawn
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
PE()
|
|
||||||
|
|
||||||
-- Render rate cap: textures are always updated (imgui expects it), tiles only at bd.RenderRate Hz
|
|
||||||
-- Optional lower cap while scrolling / resizing / moving a window (SetBusyRenderRate, off by default)
|
|
||||||
local rate = bd.RenderRate
|
|
||||||
if bd.BusyRenderRate > 0 then
|
|
||||||
local g = ImGui.GetCurrentContext()
|
|
||||||
local busy = g.WheelingWindow ~= nil or g.MovingWindow ~= nil -- (io.MouseWheel is already cleared by EndFrame)
|
|
||||||
if not busy and g.ActiveId ~= 0 then
|
|
||||||
local w = g.ActiveIdWindow
|
|
||||||
local cur = g.MouseCursor -- ResizeAll..ResizeNWSE while a resize grip/border is held
|
|
||||||
busy = (cur >= ImGuiMouseCursor.ResizeAll and cur <= ImGuiMouseCursor.ResizeNWSE)
|
|
||||||
or (w ~= nil and (g.ActiveId == ImGui.GetWindowScrollbarID(w, ImGuiAxis.X) or g.ActiveId == ImGui.GetWindowScrollbarID(w, ImGuiAxis.Y)))
|
|
||||||
end
|
|
||||||
if busy and (rate <= 0 or bd.BusyRenderRate < rate) then rate = bd.BusyRenderRate end
|
|
||||||
end
|
|
||||||
if rate > 0 then
|
|
||||||
local now = os.clock()
|
|
||||||
if now + 0.002 < bd.NextRender then PE(); return end
|
|
||||||
bd.NextRender = math.max(bd.NextRender + 1 / rate, now)
|
|
||||||
end
|
|
||||||
|
|
||||||
local sw, sh = floor(draw_data.DisplaySize.x), floor(draw_data.DisplaySize.y)
|
|
||||||
if sw <= 0 or sh <= 0 then PE(); return end
|
|
||||||
local ox, oy = draw_data.DisplayPos.x, draw_data.DisplayPos.y
|
|
||||||
local lim_x0, lim_y0, lim_x1, lim_y1 = -CLIP_PAD, -CLIP_PAD, sw + CLIP_PAD, sh + CLIP_PAD
|
|
||||||
bd.FrameNo += 1
|
|
||||||
local frame_no = bd.FrameNo
|
|
||||||
local force = bd.ForceRedraw
|
|
||||||
bd.ForceRedraw = false
|
|
||||||
local scale = bd.Scale
|
local scale = bd.Scale
|
||||||
|
|
||||||
local n = 0
|
|
||||||
local z = 0
|
|
||||||
for _, draw_list in draw_data.CmdLists:iter() do
|
|
||||||
-- 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
|
|
||||||
for _, pcmd in draw_list.CmdBuffer:iter() do
|
|
||||||
if pcmd.UserCallback ~= nil then
|
|
||||||
pcmd.UserCallback(draw_list, pcmd)
|
|
||||||
elseif pcmd.ElemCount > 0 then
|
|
||||||
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)
|
|
||||||
if cx1 > cx0 and cy1 > cy0 then
|
|
||||||
local tex_id = pcmd:GetTexID()
|
|
||||||
local t = bd.Textures[tex_id]
|
|
||||||
local vo = pcmd.VtxOffset
|
|
||||||
local i, last = pcmd.IdxOffset + 1, pcmd.IdxOffset + pcmd.ElemCount
|
|
||||||
while i <= last do
|
|
||||||
local ia, ic = idx[i], idx[i + 2]
|
|
||||||
local va, vb, vc = vtx[vo + ia], vtx[vo + idx[i + 1]], vtx[vo + ic]
|
|
||||||
local vd = false
|
|
||||||
local gk = false
|
|
||||||
local pa, pb, pc = va[1], vb[1], vc[1]
|
|
||||||
local bx0, by0, bx1, by1
|
|
||||||
if i + 5 <= last and idx[i + 3] == ia and idx[i + 4] == ic then
|
|
||||||
local d = vtx[vo + idx[i + 5]]
|
|
||||||
local pd = d[1]
|
|
||||||
-- axis aligned quad (TL, TR, BR, BL)
|
|
||||||
if pa.y == pb.y and pb.x == pc.x and pc.y == pd.y and pd.x == pa.x and pa.x < pb.x and pa.y < pd.y then
|
|
||||||
local col = va[3]
|
|
||||||
local ta, tb, tc, td = va[2], vb[2], vc[2], d[2]
|
|
||||||
if vb[3] == col and vc[3] == col and d[3] == col then
|
|
||||||
if tb.x == tc.x and tb.y == ta.y and td.x == ta.x and td.y == tc.y then vd = d end
|
|
||||||
elseif ta.x == tb.x and ta.x == tc.x and ta.x == td.x and ta.y == tb.y and ta.y == tc.y and ta.y == td.y then
|
|
||||||
-- constant uv, linear gradient: vertical (top/bottom pairs equal) or horizontal (left/right pairs equal)
|
|
||||||
if vb[3] == col and d[3] == vc[3] then vd = d; gk = 1
|
|
||||||
elseif d[3] == col and vb[3] == vc[3] then vd = d; gk = 2 end
|
|
||||||
end
|
|
||||||
if vd then
|
|
||||||
bx0, by0 = ceil(pa.x - ox - 0.5), ceil(pa.y - oy - 0.5)
|
|
||||||
bx1, by1 = ceil(pc.x - ox - 0.5), ceil(pc.y - oy - 0.5)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if vd then
|
|
||||||
i = i + 6
|
|
||||||
else
|
|
||||||
bx0, by0 = floor(min(pa.x, pb.x, pc.x) - ox), floor(min(pa.y, pb.y, pc.y) - oy)
|
|
||||||
bx1, by1 = ceil(max(pa.x, pb.x, pc.x) - ox) + 1, ceil(max(pa.y, pb.y, pc.y) - oy) + 1
|
|
||||||
i = i + 3
|
|
||||||
end
|
|
||||||
if bx0 < cx0 then bx0 = cx0 end
|
|
||||||
if by0 < cy0 then by0 = cy0 end
|
|
||||||
if bx1 > cx1 then bx1 = cx1 end
|
|
||||||
if by1 > cy1 then by1 = cy1 end
|
|
||||||
if bx0 < bx1 and by0 < by1 then
|
|
||||||
n = n + 1
|
|
||||||
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
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
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
|
-- Pass 2: hash primitives into layer-relative tiles
|
||||||
PB("ImGui tile hash")
|
PB("ImGui tile hash")
|
||||||
local th1, th2, tn, tl, touched = layer.TH1, layer.TH2, layer.TN, layer.TL, layer.Touched
|
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
|
for k = 1, layer.NTouched do tn[touched[k]] = 0 end
|
||||||
local nt = 0
|
local nt = 0
|
||||||
for p = first, n do
|
for pi = 1, np do
|
||||||
|
local p = plist[pi]
|
||||||
local tex_id = pr_tex[p]
|
local tex_id = pr_tex[p]
|
||||||
-- The clip rect only affects pixels inside the primitive's bounds, so hash the clipped bounds (relative)
|
-- 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.
|
-- instead of the clip itself: a window clipped by the (fixed) viewport rect stays hash-stable when moved.
|
||||||
@@ -700,6 +609,7 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
if not tl[key] then tl[key] = {} end
|
if not tl[key] then tl[key] = {} end
|
||||||
nt = nt + 1
|
nt = nt + 1
|
||||||
touched[nt] = key
|
touched[nt] = key
|
||||||
|
layer.TX[key], layer.TY[key] = tx, ty
|
||||||
end
|
end
|
||||||
k = k + 1
|
k = k + 1
|
||||||
tn[key] = k
|
tn[key] = k
|
||||||
@@ -713,14 +623,25 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
PE()
|
PE()
|
||||||
|
|
||||||
-- Place the layer (this is all a window move costs)
|
-- Place the layer (this is all a window move costs)
|
||||||
local frame = layer.Frame
|
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
|
if layer.X ~= lx0 or layer.Y ~= ly0 then
|
||||||
layer.X, layer.Y = lx0, ly0
|
layer.X, layer.Y = lx0, ly0
|
||||||
frame.Position = UDim2.fromOffset(lx0 / scale, ly0 / scale)
|
layer.Frame.Position = UDim2.fromOffset((lx0 - clip_x0) / scale, (ly0 - clip_y0) / scale)
|
||||||
end
|
end
|
||||||
z = z + 1
|
elseif layer.X ~= lx0 or layer.Y ~= ly0 then
|
||||||
if layer.Z ~= z then layer.Z = z; frame.ZIndex = z end
|
layer.X, layer.Y = lx0, ly0
|
||||||
if not layer.Visible then layer.Visible = true; frame.Visible = true end
|
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
|
-- Pass 3: redraw + upload changed tiles, hide unused ones
|
||||||
PB("ImGui raster+upload")
|
PB("ImGui raster+upload")
|
||||||
@@ -732,8 +653,7 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
for kk = 1, nt do
|
for kk = 1, nt do
|
||||||
local key = touched[kk]
|
local key = touched[kk]
|
||||||
local count = tn[key]
|
local count = tn[key]
|
||||||
local tx, ty = key % KEY_W, floor(key / KEY_W)
|
local tx, ty = layer.TX[key], layer.TY[key]
|
||||||
if tx < 0 then tx = tx + KEY_W; ty = ty - 1 end
|
|
||||||
local tile = GetTile(bd, layer, key, tx, ty)
|
local tile = GetTile(bd, layer, key, tx, ty)
|
||||||
if force or tile.H1 ~= th1[key] or tile.H2 ~= th2[key] then
|
if force or tile.H1 ~= th1[key] or tile.H2 ~= th2[key] then
|
||||||
tile.H1, tile.H2 = th1[key], th2[key]
|
tile.H1, tile.H2 = th1[key], th2[key]
|
||||||
@@ -800,14 +720,180 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
|||||||
end
|
end
|
||||||
PE()
|
PE()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@native
|
||||||
|
function ImGui_ImplRoblox_RenderDrawData(draw_data)
|
||||||
|
local bd = ImGui_ImplRoblox_GetBackendData()
|
||||||
|
PB("ImGui RenderDrawData")
|
||||||
|
|
||||||
|
PB("ImGui textures")
|
||||||
|
if draw_data.Textures ~= nil then
|
||||||
|
for _, tex in draw_data.Textures:iter() do
|
||||||
|
if tex.Status ~= ImTextureStatus.OK then
|
||||||
|
ImGui_ImplRoblox_UpdateTexture(tex)
|
||||||
|
bd.ForceRedraw = true -- texture contents changed: tiles sampling it must be redrawn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
PE()
|
||||||
|
|
||||||
|
-- Render rate cap: textures are always updated (imgui expects it), tiles only at bd.RenderRate Hz
|
||||||
|
-- Optional lower cap while scrolling / resizing / moving a window (SetBusyRenderRate, off by default)
|
||||||
|
local rate = bd.RenderRate
|
||||||
|
if bd.BusyRenderRate > 0 then
|
||||||
|
local g = ImGui.GetCurrentContext()
|
||||||
|
local busy = g.WheelingWindow ~= nil or g.MovingWindow ~= nil -- (io.MouseWheel is already cleared by EndFrame)
|
||||||
|
if not busy and g.ActiveId ~= 0 then
|
||||||
|
local w = g.ActiveIdWindow
|
||||||
|
local cur = g.MouseCursor -- ResizeAll..ResizeNWSE while a resize grip/border is held
|
||||||
|
busy = (cur >= ImGuiMouseCursor.ResizeAll and cur <= ImGuiMouseCursor.ResizeNWSE)
|
||||||
|
or (w ~= nil and (g.ActiveId == ImGui.GetWindowScrollbarID(w, ImGuiAxis.X) or g.ActiveId == ImGui.GetWindowScrollbarID(w, ImGuiAxis.Y)))
|
||||||
|
end
|
||||||
|
if busy and (rate <= 0 or bd.BusyRenderRate < rate) then rate = bd.BusyRenderRate end
|
||||||
|
end
|
||||||
|
if rate > 0 then
|
||||||
|
local now = os.clock()
|
||||||
|
if now + 0.002 < bd.NextRender then PE(); return end
|
||||||
|
bd.NextRender = math.max(bd.NextRender + 1 / rate, now)
|
||||||
|
end
|
||||||
|
|
||||||
|
local sw, sh = floor(draw_data.DisplaySize.x), floor(draw_data.DisplaySize.y)
|
||||||
|
if sw <= 0 or sh <= 0 then PE(); return end
|
||||||
|
local ox, oy = draw_data.DisplayPos.x, draw_data.DisplayPos.y
|
||||||
|
local lim_x0, lim_y0, lim_x1, lim_y1 = -CLIP_PAD, -CLIP_PAD, sw + CLIP_PAD, sh + CLIP_PAD
|
||||||
|
bd.FrameNo += 1
|
||||||
|
local frame_no = bd.FrameNo
|
||||||
|
local force = bd.ForceRedraw
|
||||||
|
bd.ForceRedraw = false
|
||||||
|
|
||||||
|
local n = 0
|
||||||
|
local z = 0
|
||||||
|
for _, draw_list in draw_data.CmdLists:iter() do
|
||||||
|
-- Pass 1: collect this list's primitives and their bounds
|
||||||
|
PB("ImGui collect")
|
||||||
|
local first = n + 1
|
||||||
|
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)
|
||||||
|
elseif pcmd.ElemCount > 0 then
|
||||||
|
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]
|
||||||
|
local vo = pcmd.VtxOffset
|
||||||
|
local i, last = pcmd.IdxOffset + 1, pcmd.IdxOffset + pcmd.ElemCount
|
||||||
|
while i <= last do
|
||||||
|
local ia, ic = idx[i], idx[i + 2]
|
||||||
|
local va, vb, vc = vtx[vo + ia], vtx[vo + idx[i + 1]], vtx[vo + ic]
|
||||||
|
local vd = false
|
||||||
|
local gk = false
|
||||||
|
local pa, pb, pc = va[1], vb[1], vc[1]
|
||||||
|
local bx0, by0, bx1, by1
|
||||||
|
if i + 5 <= last and idx[i + 3] == ia and idx[i + 4] == ic then
|
||||||
|
local d = vtx[vo + idx[i + 5]]
|
||||||
|
local pd = d[1]
|
||||||
|
-- axis aligned quad (TL, TR, BR, BL)
|
||||||
|
if pa.y == pb.y and pb.x == pc.x and pc.y == pd.y and pd.x == pa.x and pa.x < pb.x and pa.y < pd.y then
|
||||||
|
local col = va[3]
|
||||||
|
local ta, tb, tc, td = va[2], vb[2], vc[2], d[2]
|
||||||
|
if vb[3] == col and vc[3] == col and d[3] == col then
|
||||||
|
if tb.x == tc.x and tb.y == ta.y and td.x == ta.x and td.y == tc.y then vd = d end
|
||||||
|
elseif ta.x == tb.x and ta.x == tc.x and ta.x == td.x and ta.y == tb.y and ta.y == tc.y and ta.y == td.y then
|
||||||
|
-- constant uv, linear gradient: vertical (top/bottom pairs equal) or horizontal (left/right pairs equal)
|
||||||
|
if vb[3] == col and d[3] == vc[3] then vd = d; gk = 1
|
||||||
|
elseif d[3] == col and vb[3] == vc[3] then vd = d; gk = 2 end
|
||||||
|
end
|
||||||
|
if vd then
|
||||||
|
bx0, by0 = ceil(pa.x - ox - 0.5), ceil(pa.y - oy - 0.5)
|
||||||
|
bx1, by1 = ceil(pc.x - ox - 0.5), ceil(pc.y - oy - 0.5)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if vd then
|
||||||
|
i = i + 6
|
||||||
|
else
|
||||||
|
bx0, by0 = floor(min(pa.x, pb.x, pc.x) - ox), floor(min(pa.y, pb.y, pc.y) - oy)
|
||||||
|
bx1, by1 = ceil(max(pa.x, pb.x, pc.x) - ox) + 1, ceil(max(pa.y, pb.y, pc.y) - oy) + 1
|
||||||
|
i = i + 3
|
||||||
|
end
|
||||||
|
if bx0 < cx0 then bx0 = cx0 end
|
||||||
|
if by0 < cy0 then by0 = cy0 end
|
||||||
|
if bx1 > cx1 then bx1 = cx1 end
|
||||||
|
if by1 > cy1 then by1 = cy1 end
|
||||||
|
if bx0 < bx1 and by0 < by1 then
|
||||||
|
n = n + 1
|
||||||
|
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
|
||||||
|
pr_k[n] = kind
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
PE()
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
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
|
end
|
||||||
|
|
||||||
-- Hide layers not drawn this frame; free long unused ones (closed windows, old tooltips)
|
-- Hide layers not drawn this frame; free long unused ones (closed windows, old tooltips)
|
||||||
for draw_list, layer in pairs(bd.Layers) do
|
for draw_list, set in pairs(bd.Layers) do
|
||||||
if layer.LastUsed ~= frame_no then
|
if set.LastUsed ~= frame_no then
|
||||||
if layer.Visible then layer.Visible = false; layer.Frame.Visible = false end
|
for kind = 1, 3 do if set[kind] then HideLayer(set[kind]) end end
|
||||||
if frame_no - layer.LastUsed > LAYER_KEEP_FRAMES then
|
if frame_no - set.LastUsed > LAYER_KEEP_FRAMES then
|
||||||
DestroyLayer(layer)
|
DestroyLayerSet(set)
|
||||||
bd.Layers[draw_list] = nil
|
bd.Layers[draw_list] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -941,7 +1027,7 @@ function ImGui_ImplRoblox_NewFrame()
|
|||||||
local want = io.WantCaptureMouse
|
local want = io.WantCaptureMouse
|
||||||
if want ~= bd.TilesActive then
|
if want ~= bd.TilesActive then
|
||||||
bd.TilesActive = want
|
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
|
end
|
||||||
PE()
|
PE()
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7150,6 +7150,10 @@ function ImGui.Begin(name, open, flags)
|
|||||||
|
|
||||||
if bit32.band(flags, ImGuiWindowFlags.DockNodeHost) == 0 and not window.SkipRefresh then
|
if bit32.band(flags, ImGuiWindowFlags.DockNodeHost) == 0 and not window.SkipRefresh then
|
||||||
ImGui.PushClipRect(window.InnerClipRect.Min, window.InnerClipRect.Max, true)
|
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
|
end
|
||||||
|
|
||||||
window.WriteAccessed = false
|
window.WriteAccessed = false
|
||||||
|
|||||||
@@ -3158,6 +3158,7 @@ end
|
|||||||
|
|
||||||
-- TODO:
|
-- TODO:
|
||||||
function MT.ImDrawList:_ResetForNewFrame()
|
function MT.ImDrawList:_ResetForNewFrame()
|
||||||
|
self._ContentClipX0 = nil -- set again by Begin() (Roblox backend scroll reuse)
|
||||||
if self._Splitter._Count > 1 then
|
if self._Splitter._Count > 1 then
|
||||||
self._Splitter:Merge(self)
|
self._Splitter:Merge(self)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,21 +13,26 @@ local frame = 0
|
|||||||
|
|
||||||
local function composite(gui)
|
local function composite(gui)
|
||||||
local screen = buffer.create(W * H * 3)
|
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()
|
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
|
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
|
for _, c in ipairs(kids) do
|
||||||
if c.Visible ~= false then
|
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 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
|
if c.ClassName == "ImageLabel" then
|
||||||
local img = c.ImageContent.Object
|
local img = c.ImageContent.Object
|
||||||
local px, sz = img.Pixels, img.Size.X
|
local px, sz = img.Pixels, img.Size.X
|
||||||
for ty = 0, sz - 1 do
|
for ty = 0, sz - 1 do
|
||||||
local sy = y + ty
|
local sy = y + ty
|
||||||
if sy >= 0 and sy < H then
|
if sy >= ky0 and sy < ky1 then
|
||||||
for tx = 0, sz - 1 do
|
for tx = 0, sz - 1 do
|
||||||
local sx = x + tx
|
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 o = (ty * sz + tx) * 4
|
||||||
local a = buffer.readu8(px, o + 3)
|
local a = buffer.readu8(px, o + 3)
|
||||||
if a > 0 then
|
if a > 0 then
|
||||||
@@ -43,11 +48,11 @@ local function composite(gui)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
walk(c, x, y)
|
walk(c, x, y, cx0, cy0, cx1, cy1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
walk(gui, 0, 0)
|
walk(gui, 0, 0, 0, 0, W, H)
|
||||||
return screen
|
return screen
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user