508 lines
13 KiB
Lua
508 lines
13 KiB
Lua
-- Profiling test comparing retained mode flag vs. default behavior in complex UI
|
|
-- This simulates creating a settings menu multiple times per frame to stress test
|
|
-- the performance difference between explicit mode="retained" and implicit retained mode
|
|
|
|
package.path = package.path .. ";../../?.lua;../../modules/?.lua"
|
|
|
|
local FlexLove = require("FlexLove")
|
|
local Color = require("modules.Color")
|
|
|
|
-- Mock resolution sets (simplified)
|
|
local resolution_sets = {
|
|
["16:9"] = {
|
|
{ 1920, 1080 },
|
|
{ 1600, 900 },
|
|
{ 1280, 720 },
|
|
},
|
|
["16:10"] = {
|
|
{ 1920, 1200 },
|
|
{ 1680, 1050 },
|
|
{ 1280, 800 },
|
|
},
|
|
}
|
|
|
|
-- Mock Settings object
|
|
local Settings = {
|
|
values = {
|
|
resolution = { width = 1920, height = 1080 },
|
|
fullscreen = false,
|
|
vsync = true,
|
|
msaa = 4,
|
|
resizable = true,
|
|
borderless = false,
|
|
masterVolume = 0.8,
|
|
musicVolume = 0.7,
|
|
sfxVolume = 0.9,
|
|
crtEffectStrength = 0.3,
|
|
},
|
|
get = function(self, key)
|
|
return self.values[key]
|
|
end,
|
|
set = function(self, key, value)
|
|
self.values[key] = value
|
|
end,
|
|
reset_to_defaults = function(self) end,
|
|
apply = function(self) end,
|
|
}
|
|
|
|
-- Helper function to round numbers
|
|
local function round(num, decimals)
|
|
local mult = 10 ^ (decimals or 0)
|
|
return math.floor(num * mult + 0.5) / mult
|
|
end
|
|
|
|
-- Simplified SettingsMenu implementation
|
|
local function create_settings_menu_with_mode_flag(use_mode_flag)
|
|
local GuiZIndexing = { MainMenuOverlay = 100 }
|
|
|
|
-- Backdrop
|
|
local backdrop_props = {
|
|
z = GuiZIndexing.MainMenuOverlay - 1,
|
|
width = "100%",
|
|
height = "100%",
|
|
backdropBlur = { radius = 10 },
|
|
backgroundColor = Color.new(1, 1, 1, 0.1),
|
|
}
|
|
if use_mode_flag then
|
|
backdrop_props.mode = "retained"
|
|
end
|
|
local backdrop = FlexLove.new(backdrop_props)
|
|
|
|
-- Main window
|
|
local window_props = {
|
|
z = GuiZIndexing.MainMenuOverlay,
|
|
x = "5%",
|
|
y = "5%",
|
|
width = "90%",
|
|
height = "90%",
|
|
themeComponent = "framev3",
|
|
positioning = "flex",
|
|
flexDirection = "vertical",
|
|
justifySelf = "center",
|
|
justifyContent = "flex-start",
|
|
alignItems = "center",
|
|
scaleCorners = 3,
|
|
padding = { horizontal = "5%", vertical = "3%" },
|
|
gap = 10,
|
|
}
|
|
if use_mode_flag then
|
|
window_props.mode = "retained"
|
|
end
|
|
local window = FlexLove.new(window_props)
|
|
|
|
-- Close button
|
|
FlexLove.new({
|
|
parent = window,
|
|
x = "2%",
|
|
y = "2%",
|
|
alignSelf = "flex-start",
|
|
themeComponent = "buttonv2",
|
|
width = "4vw",
|
|
height = "4vw",
|
|
text = "X",
|
|
textSize = "2xl",
|
|
textAlign = "center",
|
|
})
|
|
|
|
-- Title
|
|
FlexLove.new({
|
|
parent = window,
|
|
text = "Settings",
|
|
textAlign = "center",
|
|
textSize = "3xl",
|
|
width = "100%",
|
|
margin = { top = "-4%", bottom = "4%" },
|
|
})
|
|
|
|
-- Content container
|
|
local content = FlexLove.new({
|
|
parent = window,
|
|
width = "100%",
|
|
height = "100%",
|
|
positioning = "flex",
|
|
flexDirection = "vertical",
|
|
padding = { top = "4%" },
|
|
})
|
|
|
|
-- Display Settings Section
|
|
FlexLove.new({
|
|
parent = content,
|
|
text = "Display Settings",
|
|
textAlign = "start",
|
|
textSize = "xl",
|
|
width = "100%",
|
|
textColor = Color.new(0.8, 0.9, 1, 1),
|
|
})
|
|
|
|
-- Resolution control
|
|
local row1 = FlexLove.new({
|
|
parent = content,
|
|
width = "100%",
|
|
height = "5vh",
|
|
positioning = "flex",
|
|
flexDirection = "horizontal",
|
|
justifyContent = "space-between",
|
|
alignItems = "center",
|
|
gap = 10,
|
|
})
|
|
|
|
FlexLove.new({
|
|
parent = row1,
|
|
text = "Resolution",
|
|
textAlign = "start",
|
|
textSize = "md",
|
|
width = "30%",
|
|
})
|
|
|
|
local resolution = Settings:get("resolution")
|
|
FlexLove.new({
|
|
parent = row1,
|
|
text = resolution.width .. " x " .. resolution.height,
|
|
themeComponent = "buttonv2",
|
|
width = "30%",
|
|
textAlign = "center",
|
|
textSize = "lg",
|
|
})
|
|
|
|
-- Fullscreen toggle
|
|
local row2 = FlexLove.new({
|
|
parent = content,
|
|
width = "100%",
|
|
height = "5vh",
|
|
positioning = "flex",
|
|
flexDirection = "horizontal",
|
|
justifyContent = "space-between",
|
|
alignItems = "center",
|
|
gap = 10,
|
|
})
|
|
|
|
FlexLove.new({
|
|
parent = row2,
|
|
text = "Fullscreen",
|
|
textAlign = "start",
|
|
textSize = "md",
|
|
width = "60%",
|
|
})
|
|
|
|
local fullscreen = Settings:get("fullscreen")
|
|
FlexLove.new({
|
|
parent = row2,
|
|
text = fullscreen and "ON" or "OFF",
|
|
themeComponent = fullscreen and "buttonv1" or "buttonv2",
|
|
textAlign = "center",
|
|
width = "15vw",
|
|
height = "4vh",
|
|
textSize = "md",
|
|
})
|
|
|
|
-- VSync toggle
|
|
local row3 = FlexLove.new({
|
|
parent = content,
|
|
width = "100%",
|
|
height = "5vh",
|
|
positioning = "flex",
|
|
flexDirection = "horizontal",
|
|
justifyContent = "space-between",
|
|
alignItems = "center",
|
|
gap = 10,
|
|
})
|
|
|
|
FlexLove.new({
|
|
parent = row3,
|
|
text = "VSync",
|
|
textAlign = "start",
|
|
textSize = "md",
|
|
width = "60%",
|
|
})
|
|
|
|
local vsync = Settings:get("vsync")
|
|
FlexLove.new({
|
|
parent = row3,
|
|
text = vsync and "ON" or "OFF",
|
|
themeComponent = vsync and "buttonv1" or "buttonv2",
|
|
textAlign = "center",
|
|
width = "15vw",
|
|
height = "4vh",
|
|
textSize = "md",
|
|
})
|
|
|
|
-- MSAA control
|
|
local row4 = FlexLove.new({
|
|
parent = content,
|
|
width = "100%",
|
|
height = "5vh",
|
|
positioning = "flex",
|
|
flexDirection = "horizontal",
|
|
justifyContent = "space-between",
|
|
alignItems = "center",
|
|
gap = 10,
|
|
})
|
|
|
|
FlexLove.new({
|
|
parent = row4,
|
|
text = "MSAA",
|
|
textAlign = "start",
|
|
textSize = "md",
|
|
width = "30%",
|
|
})
|
|
|
|
local button_container = FlexLove.new({
|
|
parent = row4,
|
|
width = "60%",
|
|
height = "100%",
|
|
positioning = "flex",
|
|
flexDirection = "horizontal",
|
|
gap = 5,
|
|
})
|
|
|
|
local msaa_values = { 0, 1, 2, 4, 8, 16 }
|
|
for _, msaa_val in ipairs(msaa_values) do
|
|
local is_selected = Settings:get("msaa") == msaa_val
|
|
FlexLove.new({
|
|
parent = button_container,
|
|
themeComponent = is_selected and "buttonv1" or "buttonv2",
|
|
text = tostring(msaa_val),
|
|
textAlign = "center",
|
|
width = "8vw",
|
|
height = "100%",
|
|
textSize = "sm",
|
|
disabled = is_selected,
|
|
opacity = is_selected and 0.7 or 1.0,
|
|
})
|
|
end
|
|
|
|
-- Audio Settings Section
|
|
FlexLove.new({
|
|
parent = content,
|
|
text = "Audio Settings",
|
|
textAlign = "start",
|
|
textSize = "xl",
|
|
width = "100%",
|
|
textColor = Color.new(0.8, 0.9, 1, 1),
|
|
})
|
|
|
|
-- Master volume slider
|
|
local row5 = FlexLove.new({
|
|
parent = content,
|
|
width = "100%",
|
|
height = "5vh",
|
|
positioning = "flex",
|
|
flexDirection = "horizontal",
|
|
justifyContent = "space-between",
|
|
alignItems = "center",
|
|
gap = 10,
|
|
})
|
|
|
|
FlexLove.new({
|
|
parent = row5,
|
|
text = "Master Volume",
|
|
textAlign = "start",
|
|
textSize = "md",
|
|
width = "30%",
|
|
})
|
|
|
|
local slider_container = FlexLove.new({
|
|
parent = row5,
|
|
width = "50%",
|
|
height = "100%",
|
|
positioning = "flex",
|
|
flexDirection = "horizontal",
|
|
alignItems = "center",
|
|
gap = 5,
|
|
})
|
|
|
|
local value = Settings:get("masterVolume")
|
|
local normalized = value
|
|
|
|
local slider_track = FlexLove.new({
|
|
parent = slider_container,
|
|
width = "80%",
|
|
height = "75%",
|
|
positioning = "flex",
|
|
flexDirection = "horizontal",
|
|
themeComponent = "framev3",
|
|
})
|
|
|
|
FlexLove.new({
|
|
parent = slider_track,
|
|
width = (normalized * 100) .. "%",
|
|
height = "100%",
|
|
themeComponent = "buttonv1",
|
|
themeStateLock = true,
|
|
})
|
|
|
|
FlexLove.new({
|
|
parent = slider_container,
|
|
text = string.format("%d", value * 100),
|
|
textAlign = "center",
|
|
textSize = "md",
|
|
width = "15%",
|
|
})
|
|
|
|
-- Meta controls (bottom buttons)
|
|
local meta_container = FlexLove.new({
|
|
parent = window,
|
|
positioning = "absolute",
|
|
width = "100%",
|
|
height = "10%",
|
|
y = "90%",
|
|
x = "0%",
|
|
})
|
|
|
|
local button_bar = FlexLove.new({
|
|
parent = meta_container,
|
|
width = "100%",
|
|
positioning = "flex",
|
|
flexDirection = "horizontal",
|
|
justifyContent = "center",
|
|
alignItems = "center",
|
|
gap = 10,
|
|
})
|
|
|
|
FlexLove.new({
|
|
parent = button_bar,
|
|
themeComponent = "buttonv2",
|
|
text = "Reset",
|
|
textAlign = "center",
|
|
width = "15vw",
|
|
height = "6vh",
|
|
textSize = "lg",
|
|
})
|
|
|
|
return { backdrop = backdrop, window = window }
|
|
end
|
|
|
|
-- Profile configuration
|
|
local PROFILE_NAME = "Settings Menu Mode Comparison"
|
|
local ITERATIONS_PER_TEST = 100 -- Create the menu 100 times to measure difference
|
|
|
|
print("=" .. string.rep("=", 78))
|
|
print(string.format(" %s", PROFILE_NAME))
|
|
print("=" .. string.rep("=", 78))
|
|
print()
|
|
print("This profile compares performance when creating a complex settings menu")
|
|
print("with explicit mode='retained' flags vs. implicit retained mode (global).")
|
|
print()
|
|
print(string.format("Test configuration:"))
|
|
print(string.format(" - Iterations: %d menu creations per test", ITERATIONS_PER_TEST))
|
|
print(string.format(" - Elements per menu: ~45 (backdrop, window, buttons, sliders, etc.)"))
|
|
print(string.format(" - Total elements created: ~%d per test", ITERATIONS_PER_TEST * 45))
|
|
print()
|
|
|
|
-- Warm up
|
|
print("Warming up...")
|
|
FlexLove.init({ immediateMode = false, theme = "space" })
|
|
for i = 1, 10 do
|
|
create_settings_menu_with_mode_flag(false)
|
|
end
|
|
collectgarbage("collect")
|
|
|
|
-- Test 1: Without explicit mode flags (implicit retained via global setting)
|
|
print("Running Test 1: Without explicit mode='retained' flags...")
|
|
FlexLove.init({ immediateMode = false, theme = "space" })
|
|
collectgarbage("collect")
|
|
local mem_before_implicit = collectgarbage("count")
|
|
local time_before_implicit = os.clock()
|
|
|
|
for i = 1, ITERATIONS_PER_TEST do
|
|
local menu = create_settings_menu_with_mode_flag(false)
|
|
end
|
|
|
|
local time_after_implicit = os.clock()
|
|
collectgarbage("collect")
|
|
local mem_after_implicit = collectgarbage("count")
|
|
|
|
local time_implicit = time_after_implicit - time_before_implicit
|
|
local mem_implicit = mem_after_implicit - mem_before_implicit
|
|
|
|
print(string.format(" Time: %.4f seconds", time_implicit))
|
|
print(string.format(" Memory: %.2f KB", mem_implicit))
|
|
print(string.format(" Avg time per menu: %.4f ms", (time_implicit / ITERATIONS_PER_TEST) * 1000))
|
|
print()
|
|
|
|
-- Test 2: With explicit mode="retained" flags
|
|
print("Running Test 2: With explicit mode='retained' flags...")
|
|
FlexLove.init({ immediateMode = false, theme = "space" })
|
|
collectgarbage("collect")
|
|
local mem_before_explicit = collectgarbage("count")
|
|
local time_before_explicit = os.clock()
|
|
|
|
for i = 1, ITERATIONS_PER_TEST do
|
|
local menu = create_settings_menu_with_mode_flag(true)
|
|
end
|
|
|
|
local time_after_explicit = os.clock()
|
|
collectgarbage("collect")
|
|
local mem_after_explicit = collectgarbage("count")
|
|
|
|
local time_explicit = time_after_explicit - time_before_explicit
|
|
local mem_explicit = mem_after_explicit - mem_before_explicit
|
|
|
|
print(string.format(" Time: %.4f seconds", time_explicit))
|
|
print(string.format(" Memory: %.2f KB", mem_explicit))
|
|
print(string.format(" Avg time per menu: %.4f ms", (time_explicit / ITERATIONS_PER_TEST) * 1000))
|
|
print()
|
|
|
|
-- Calculate differences
|
|
print("=" .. string.rep("=", 78))
|
|
print("RESULTS COMPARISON")
|
|
print("=" .. string.rep("=", 78))
|
|
print()
|
|
|
|
local time_diff = time_explicit - time_implicit
|
|
local time_percent = (time_diff / time_implicit) * 100
|
|
local mem_diff = mem_explicit - mem_implicit
|
|
|
|
print(string.format("Time Difference:"))
|
|
print(string.format(" Without mode flag: %.4f seconds", time_implicit))
|
|
print(string.format(" With mode flag: %.4f seconds", time_explicit))
|
|
print(string.format(" Difference: %.4f seconds (%+.2f%%)", time_diff, time_percent))
|
|
print()
|
|
|
|
print(string.format("Memory Difference:"))
|
|
print(string.format(" Without mode flag: %.2f KB", mem_implicit))
|
|
print(string.format(" With mode flag: %.2f KB", mem_explicit))
|
|
print(string.format(" Difference: %+.2f KB", mem_diff))
|
|
print()
|
|
|
|
-- Interpretation
|
|
print("INTERPRETATION:")
|
|
print()
|
|
if math.abs(time_percent) < 5 then
|
|
print(" ✓ Performance is essentially identical (< 5% difference)")
|
|
print(" The explicit mode flag has negligible impact on performance.")
|
|
elseif time_percent > 0 then
|
|
print(string.format(" ⚠ Explicit mode flag is %.2f%% SLOWER", time_percent))
|
|
print(" This indicates overhead from mode checking/resolution.")
|
|
else
|
|
print(string.format(" ✓ Explicit mode flag is %.2f%% FASTER", -time_percent))
|
|
print(" This indicates potential optimization benefits.")
|
|
end
|
|
print()
|
|
|
|
if math.abs(mem_diff) < 50 then
|
|
print(" ✓ Memory usage is essentially identical (< 50 KB difference)")
|
|
elseif mem_diff > 0 then
|
|
print(string.format(" ⚠ Explicit mode flag uses %.2f KB MORE memory", mem_diff))
|
|
else
|
|
print(string.format(" ✓ Explicit mode flag uses %.2f KB LESS memory", -mem_diff))
|
|
end
|
|
print()
|
|
|
|
print("RECOMMENDATION:")
|
|
print()
|
|
if math.abs(time_percent) < 5 and math.abs(mem_diff) < 50 then
|
|
print(" The explicit mode='retained' flag provides clarity and explicitness")
|
|
print(" without any meaningful performance cost. It's recommended for:")
|
|
print(" - Code readability (makes intent explicit)")
|
|
print(" - Future-proofing (if global mode changes)")
|
|
print(" - Mixed-mode UIs (where some elements are immediate)")
|
|
else
|
|
print(" Consider the trade-offs based on your specific use case.")
|
|
end
|
|
print()
|
|
|
|
print("=" .. string.rep("=", 78))
|
|
print("Profile complete!")
|
|
print("=" .. string.rep("=", 78))
|