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

This commit is contained in:
4DBug
2026-10-08 11:10:53 -05:00
parent 71843392f1
commit ceb66d3067
7 changed files with 564 additions and 190 deletions
+1 -1
View File
@@ -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"], [33977, "imgui_impl_roblox_fonts.lua"], [40380, "imgui_internal.lua"], [43552, "imgui_tables.lua"], [47899, "imgui_tabs.lua"], [49066, "imgui_widgets.lua"], [57600, "imstb_rectpack.lua"], [58046, "imstb_textedit.lua"], [59287, "imstb_truetype.lua"], [61755, "imstd_minstdio.lua"]]
[[12, "imgui.lua"], [13795, "imgui_demo.lua"], [14728, "imgui_demo_2.lua"], [17803, "imgui_demo_apps.lua"], [19062, "imgui_demo_tools.lua"], [20388, "imgui_demo_widgets.lua"], [22312, "imgui_docking.lua"], [25440, "imgui_dragdrop.lua"], [25732, "imgui_draw.lua"], [30979, "imgui_h.lua"], [33153, "imgui_impl_roblox.lua"], [34094, "imgui_impl_roblox_fonts.lua"], [40497, "imgui_internal.lua"], [43669, "imgui_tables.lua"], [48016, "imgui_tabs.lua"], [49183, "imgui_widgets.lua"], [57717, "imstb_rectpack.lua"], [58163, "imstb_textedit.lua"], [59404, "imstb_truetype.lua"], [61872, "imstd_minstdio.lua"]]
+211 -94
View File
@@ -33568,36 +33568,28 @@ local function DrawTriangle(va, vb, vc, t, cx0, cy0, cx1, cy1, ox, oy)
end
end
-- Quads emitted by PrimRect/PrimRectUV/RenderText use indices (a, b, c, a, c, d) with a=TL, b=TR, c=BR, d=BL.
-- When such a quad is axis aligned with one colour it is drawn with the much cheaper DrawRect.
@native
local function TryDrawRect(va, vb, vc, vd, t, cx0, cy0, cx1, cy1, ox, oy)
local pa, pb, pc, pd = va[1], vb[1], vc[1], vd[1]
if not (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 return false end
local col = va[3]
if vb[3] ~= col or vc[3] ~= col or vd[3] ~= col then return false end
local ta, tb, tc, td = va[2], vb[2], vc[2], vd[2]
if not (tb.x == tc.x and tb.y == ta.y and td.x == ta.x and td.y == tc.y) then return false end
DrawRect(pa.x - ox, pa.y - oy, pc.x - ox, pc.y - oy, ta.x, ta.y, tc.x, tc.y, col, t, cx0, cy0, cx1, cy1)
return true
end
-- Tiles: the screen is covered by TILE x TILE EditableImages (one ImageLabel each). Every frame each primitive
-- (triangle, or axis aligned quad) is hashed into the tiles it touches; only tiles whose hash changed are
-- re-rasterized and uploaded. Static UI therefore costs ~nothing to draw, and the result is pixel-identical to a
-- full redraw (the hash covers everything that affects a tile's pixels: geometry, uv, colour, texture, clip, order).
-- Layers: every ImDrawList (one per top level window, popup, tooltip, plus the viewport bg/fg lists) gets its own
-- Frame, stacked by draw order. A layer is covered by TILE x TILE EditableImages (one ImageLabel each) placed
-- 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
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)
local CLIP_PAD = 2048 -- content up to this far off screen is kept, so dragging a window past the edge doesn't redraw it
local LAYER_KEEP_FRAMES = 120 -- unused layers are destroyed after this many rendered frames
-- Primitive arrays (reused between frames, no per-frame allocation)
local pr_a, pr_b, pr_c, pr_d, pr_t = {}, {}, {}, {}, {} -- vertices (pr_d == false -> triangle), texture
local pr_a, pr_b, pr_c, pr_d, pr_t, pr_g = {}, {}, {}, {}, {}, {} -- vertices (pr_d == false -> triangle), texture, gradient kind
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
@native
local function HashVertex(h1, h2, v)
local function HashVertex(h1, h2, v, ox, oy)
local p, u, c = v[1], v[2], v[3]
local x, y = p.x, p.y
local x, y = p.x - ox, p.y - oy
local uv = u.x * 8192 + u.y
h1 = (h1 * 48271 + x * 4096 + y) % P1
h1 = (h1 * 48271 + uv * 4096) % P1
@@ -33608,13 +33600,76 @@ local function HashVertex(h1, h2, v)
return h1, h2
end
local function GetTile(bd, index, tx, ty)
local tile = bd.Tiles[index]
-- Axis aligned quad with a linear colour gradient and constant uv (ColorPicker hue/SV/alpha bars, AddRectFilledMultiColor).
-- kind 1: colour varies along y (ca top, cb bottom), kind 2: along x (ca left, cb right). Same pixels as the two triangles.
@native
local function DrawRectGradient(x0, y0, x1, y1, ca, cb, kind, t, u, v, cx0, cy0, cx1, cy1)
local px0, py0 = max(ceil(x0 - 0.5), cx0), max(ceil(y0 - 0.5), cy0)
local px1, py1 = min(ceil(x1 - 0.5), cx1), min(ceil(y1 - 0.5), cy1)
if px0 >= px1 or py0 >= py1 then return end
local ra, ga, ba, aa = UnpackColor(ca)
local rb, gb, bb, ab = UnpackColor(cb)
if t then
local tr, tg, tb, ta = SampleTexture(t, u, v)
ra, ga, ba, aa = ra * tr / 255, ga * tg / 255, ba * tb / 255, aa * ta / 255
rb, gb, bb, ab = rb * tr / 255, gb * tg / 255, bb * tb / 255, ab * ta / 255
end
if kind == 1 then
local inv = 1 / (y1 - y0)
for y = py0, py1 - 1 do
local f = (y + 0.5 - y0) * inv
local nf = 1 - f
FillSpan(y, px0, px1, ra * nf + rb * f, ga * nf + gb * f, ba * nf + bb * f, aa * nf + ab * f)
end
else
-- Every row gets the same colours: when a row's destination equals the previous row's, copy its result
local inv = 1 / (x1 - x0)
local len = (px1 - px0) * 4
local opaque = aa >= 255 and ab >= 255
local prev_dst, prev_o = nil, 0
for y = py0, py1 - 1 do
local o = (y * buf_w + px0) * 4
local dst = (not opaque) and readstring(buf, o, len) or true
if prev_dst ~= nil and dst == prev_dst then
buffer.copy(buf, o, buf, prev_o, len)
else
local oo = o
for x = px0, px1 - 1 do
local f = (x + 0.5 - x0) * inv
local nf = 1 - f
BlendPixel(oo, ra * nf + rb * f, ga * nf + gb * f, ba * nf + bb * f, aa * nf + ab * f)
oo = oo + 4
end
prev_dst = dst
end
prev_o = o
end
end
end
local function GetLayer(bd, draw_list)
local layer = bd.Layers[draw_list]
if layer then return layer end
local frame = Instance.new("Frame")
frame.Name = "Layer"
frame.BackgroundTransparency = 1
frame.BorderSizePixel = 0
frame.Size = UDim2.fromOffset(0, 0)
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
return layer
end
local function GetTile(bd, layer, key, tx, ty)
local tile = layer.Tiles[key]
if tile then return tile end
local img = AssetService:CreateEditableImage({ Size = Vector2.new(TILE, TILE) })
assert(img, "imgui_impl_roblox: EditableImage memory budget exceeded")
local label = Instance.new("ImageLabel")
label.Name = "Tile" .. index
label.Name = "Tile" .. key
label.BackgroundTransparency = 1
label.BorderSizePixel = 0
local scale = bd.Scale
@@ -33624,15 +33679,20 @@ local function GetTile(bd, index, tx, ty)
label.ImageContent = Content.fromObject(img)
label.Visible = false
label.Active = bd.TilesActive
label.Parent = bd.Gui
label.Parent = layer.Frame
tile = { Image = img, Label = label, Buffer = buffer.create(TILE * TILE * 4), H1 = -1, H2 = -1, Visible = false }
bd.Tiles[index] = tile
layer.Tiles[key] = tile
return tile
end
local function DestroyLayer(layer)
for _, tile in pairs(layer.Tiles) do tile.Image:Destroy() end
layer.Frame:Destroy()
end
local function DestroyTiles(bd)
for _, tile in pairs(bd.Tiles) do tile.Label:Destroy(); tile.Image:Destroy() end
bd.Tiles = {}
for _, layer in pairs(bd.Layers) do DestroyLayer(layer) end
bd.Layers = {}
end
-- MicroProfiler labels, enabled with ImGui_ImplRoblox.SetProfiling(true)
@@ -33666,56 +33726,56 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
local sw, sh = floor(draw_data.DisplaySize.x), floor(draw_data.DisplaySize.y)
if sw <= 0 or sh <= 0 then PE(); return end
local cols, rows = ceil(sw / TILE), ceil(sh / TILE)
if cols ~= bd.TileCols or rows ~= bd.TileRows then
DestroyTiles(bd)
bd.TileCols, bd.TileRows = cols, rows
end
local ntiles = cols * rows
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
-- Per tile: hashes and the list of primitives touching it
local th1, th2, tn, tl = bd.TH1, bd.TH2, bd.TN, bd.TL
for t = 0, ntiles - 1 do
th1[t], th2[t], tn[t] = 0, 0, 0
if not tl[t] then tl[t] = {} end
end
-- Pass 1: collect primitives, hash them into tiles
PB("ImGui tile hash")
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), 0), max(floor(clip.y - oy), 0)
local cx1, cy1 = min(floor(clip.z - ox), sw), min(floor(clip.w - oy), sh)
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 ch1 = ((cx0 * 8191 + cy0) * 8191 + cx1) % P1
local ch2 = ((cy1 * 8191 + cx1) * 8191 + cy0 + tex_id) % P2
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) with one colour and a matching uv rect
-- 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
and tb.x == tc.x and tb.y == ta.y and td.x == ta.x and td.y == tc.y then
vd = d
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
@@ -33734,55 +33794,93 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
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] = va, vb, vc, vd, t
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
local h1, h2 = HashVertex(ch1, ch2, va)
h1, h2 = HashVertex(h1, h2, vb)
h1, h2 = HashVertex(h1, h2, vc)
if vd then h1, h2 = HashVertex(h1, h2, vd) end
for ty = floor(by0 / TILE), floor((by1 - 1) / TILE) do
for tx = floor(bx0 / TILE), floor((bx1 - 1) / TILE) do
local ti = ty * cols + tx
local k = tn[ti] + 1
tn[ti] = k
tl[ti][k] = n
th1[ti] = (th1[ti] * 48271 + h1) % P1
th2[ti] = (th2[ti] * 16807 + h2 + k) % P2
end
end
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
end
PE()
PE()
-- Pass 2: redraw + upload changed tiles
PB("ImGui raster+upload")
local force = bd.ForceRedraw
bd.ForceRedraw = false
for ty = 0, rows - 1 do
for tx = 0, cols - 1 do
local ti = ty * cols + tx
local count = tn[ti]
local tile = bd.Tiles[ti]
if count == 0 then
if tile and tile.Visible then
tile.Visible = false
tile.Label.Visible = false
tile.H1 = -1
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
else
tile = tile or GetTile(bd, ti, tx, ty)
if force or tile.H1 ~= th1[ti] or tile.H2 ~= th2[ti] then
tile.H1, tile.H2 = th1[ti], th2[ti]
local x0, y0 = tx * TILE, ty * TILE
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
buffer.fill(buf, 0, 0)
local list = tl[ti]
local list = tl[key]
local tox, toy = ox + x0, oy + y0
for k = 1, count do
local p = list[k]
@@ -33791,7 +33889,14 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
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]
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)
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
@@ -33805,13 +33910,25 @@ function ImGui_ImplRoblox_RenderDrawData(draw_data)
tile.Label.Visible = true
end
end
PE()
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)
bd.Layers[draw_list] = nil
end
end
end
-- drop references so old vertex tables can be collected
for k = n + 1, bd.LastPrimCount do pr_a[k], pr_b[k], pr_c[k], pr_d[k], pr_t[k] = nil, nil, nil, nil, nil end
bd.LastPrimCount = n
PE(); PE()
PE()
end
---------------------------------------------------------
@@ -33830,12 +33947,13 @@ function ImGui_ImplRoblox_Init(parent, render_scale)
gui.IgnoreGuiInset = true -- so GetMouseLocation() and the tiles share the same origin
gui.ResetOnSpawn = false
gui.DisplayOrder = 1000
gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling -- layers (one Frame per draw list) stack by ZIndex
gui.Parent = parent or Players.LocalPlayer:WaitForChild("PlayerGui")
local bd = {
Time = os.clock(), Gui = gui, Connections = {},
Textures = {}, TextureCount = 0, MouseX = -1, MouseY = -1,
Tiles = {}, TileCols = 0, TileRows = 0, Scale = render_scale or 1, TilesActive = false, RenderRate = 60, NextRender = 0, TH1 = {}, TH2 = {}, TN = {}, TL = {}, LastPrimCount = 0, ForceRedraw = true,
Layers = {}, FrameNo = 0, Scale = render_scale or 1, TilesActive = false, RenderRate = 60, NextRender = 0, LastPrimCount = 0, ForceRedraw = true,
}
io.BackendPlatformUserData = bd
@@ -33935,7 +34053,7 @@ function ImGui_ImplRoblox_NewFrame()
local want = io.WantCaptureMouse
if want ~= bd.TilesActive then
bd.TilesActive = want
for _, tile in pairs(bd.Tiles) do tile.Label.Active = want end
for _, layer in pairs(bd.Layers) do for _, tile in pairs(layer.Tiles) do tile.Label.Active = want end end
end
PE()
end
@@ -33954,7 +34072,6 @@ function ImGui_ImplRoblox_SetRenderScale(scale)
if scale == bd.Scale then return end
bd.Scale = scale
DestroyTiles(bd)
bd.TileCols, bd.TileRows = 0, 0
bd.ForceRedraw = true
end