blur perf warning

This commit is contained in:
Michael Freno
2025-12-03 14:08:39 -05:00
parent 2c7f8c5282
commit 5bc67ecb69
2 changed files with 72 additions and 11 deletions

View File

@@ -1,6 +1,9 @@
-- Lua 5.2+ compatibility for unpack -- Lua 5.2+ compatibility for unpack
local unpack = table.unpack or unpack local unpack = table.unpack or unpack
-- Warning cache to prevent duplicate warnings for the same element
local warningCache = {}
local Cache = { local Cache = {
canvases = {}, canvases = {},
quads = {}, quads = {},
@@ -10,6 +13,7 @@ local Cache = {
MAX_QUAD_SIZE = 20, MAX_QUAD_SIZE = 20,
MAX_BLURRED_CANVAS_CACHE = 50, -- Maximum cached blurred canvases MAX_BLURRED_CANVAS_CACHE = 50, -- Maximum cached blurred canvases
INTENSITY_THRESHOLD = 5, -- Skip blur below this intensity INTENSITY_THRESHOLD = 5, -- Skip blur below this intensity
LARGE_BLUR_THRESHOLD = 250 * 250, -- Warn if blur area exceeds this (250x250px)
} }
--- Round canvas size to nearest bucket for better reuse --- Round canvas size to nearest bucket for better reuse
@@ -158,7 +162,7 @@ function Cache.setBlurredCanvas(key, canvas)
for _ in pairs(Cache.blurredCanvases) do for _ in pairs(Cache.blurredCanvases) do
count = count + 1 count = count + 1
end end
if count >= Cache.MAX_BLURRED_CANVAS_CACHE then if count >= Cache.MAX_BLURRED_CANVAS_CACHE then
-- Remove oldest entry -- Remove oldest entry
local oldestKey = nil local oldestKey = nil
@@ -169,7 +173,7 @@ function Cache.setBlurredCanvas(key, canvas)
oldestKey = k oldestKey = k
end end
end end
if oldestKey then if oldestKey then
if Cache.blurredCanvases[oldestKey].canvas then if Cache.blurredCanvases[oldestKey].canvas then
Cache.blurredCanvases[oldestKey].canvas:release() Cache.blurredCanvases[oldestKey].canvas:release()
@@ -177,7 +181,7 @@ function Cache.setBlurredCanvas(key, canvas)
Cache.blurredCanvases[oldestKey] = nil Cache.blurredCanvases[oldestKey] = nil
end end
end end
Cache.blurredCanvases[key] = { Cache.blurredCanvases[key] = {
canvas = canvas, canvas = canvas,
lastUsed = os.time(), lastUsed = os.time(),
@@ -205,11 +209,12 @@ function Cache.clear()
entry.canvas:release() entry.canvas:release()
end end
end end
Cache.canvases = {} Cache.canvases = {}
Cache.quads = {} Cache.quads = {}
Cache.blurInstances = {} Cache.blurInstances = {}
Cache.blurredCanvases = {} Cache.blurredCanvases = {}
warningCache = {} -- Clear warning cache on cache clear
end end
-- ============================================================================ -- ============================================================================
@@ -287,14 +292,14 @@ function Cache.getBlurInstance(quality)
if taps % 2 == 0 then if taps % 2 == 0 then
taps = taps + 1 taps = taps + 1
end end
local shader = ShaderBuilder.build(taps, 1.0, "weighted", -1) local shader = ShaderBuilder.build(taps, 1.0, "weighted", -1)
Cache.blurInstances[quality] = { Cache.blurInstances[quality] = {
shader = shader, shader = shader,
taps = taps, taps = taps,
} }
end end
return Cache.blurInstances[quality] return Cache.blurInstances[quality]
end end
@@ -309,6 +314,50 @@ end
local Blur = {} local Blur = {}
Blur.__index = Blur Blur.__index = Blur
--- Check if we should warn about large blur area in immediate mode
---@param elementId string|nil Element ID for caching warnings
---@param width number Blur area width
---@param height number Blur area height
---@param blurType string "content" or "backdrop"
local function checkLargeBlurWarning(elementId, width, height, blurType)
-- Skip if no ErrorHandler available
if not Blur._ErrorHandler then
return
end
-- Skip if not in immediate mode
if not Blur._immediateModeOptimizations then
return
end
-- Calculate blur area
local area = width * height
-- Skip if area is below threshold
if area <= Cache.LARGE_BLUR_THRESHOLD then
return
end
-- Generate warning key (use elementId if available, otherwise use dimensions)
local warningKey = elementId or string.format("%dx%d:%s", width, height, blurType)
-- Skip if already warned for this element/area
if warningCache[warningKey] then
return
end
-- Mark as warned
warningCache[warningKey] = true
-- Issue warning
local message = string.format("Large %s blur area detected (%dx%d = %d pixels) in immediate mode", blurType, width, height, area)
local suggestion =
"Consider using retained mode for this component to avoid recreating blur effects every frame. Large blur operations are expensive and can cause performance issues in immediate mode."
Blur._ErrorHandler:warn("Blur", "PERF_003", message, suggestion)
end
--- Create a new blur effect instance --- Create a new blur effect instance
---@param props BlurProps? Blur configuration ---@param props BlurProps? Blur configuration
---@return Blur blur The new blur instance ---@return Blur blur The new blur instance
@@ -355,6 +404,9 @@ function Blur:applyToRegion(intensity, x, y, width, height, drawFunc)
return return
end end
-- Check for large blur area in immediate mode
checkLargeBlurWarning(nil, width, height, "content")
intensity = math.max(0, math.min(100, intensity)) intensity = math.max(0, math.min(100, intensity))
-- Intensity 0-100 maps to 0-5 passes -- Intensity 0-100 maps to 0-5 passes
@@ -508,10 +560,10 @@ function Blur:applyBackdropCached(intensity, x, y, width, height, backdropCanvas
if not Blur._immediateModeOptimizations or not elementId then if not Blur._immediateModeOptimizations or not elementId then
return self:applyBackdrop(intensity, x, y, width, height, backdropCanvas) return self:applyBackdrop(intensity, x, y, width, height, backdropCanvas)
end end
-- Generate cache key -- Generate cache key
local cacheKey = Cache.generateBlurCacheKey(elementId, x, y, width, height, intensity, self.quality, true) local cacheKey = Cache.generateBlurCacheKey(elementId, x, y, width, height, intensity, self.quality, true)
-- Check cache -- Check cache
local cachedCanvas = Cache.getBlurredCanvas(cacheKey) local cachedCanvas = Cache.getBlurredCanvas(cacheKey)
if cachedCanvas then if cachedCanvas then
@@ -520,17 +572,17 @@ function Blur:applyBackdropCached(intensity, x, y, width, height, backdropCanvas
local prevShader = love.graphics.getShader() local prevShader = love.graphics.getShader()
local prevColor = { love.graphics.getColor() } local prevColor = { love.graphics.getColor() }
local prevBlendMode = love.graphics.getBlendMode() local prevBlendMode = love.graphics.getBlendMode()
love.graphics.setCanvas(prevCanvas) love.graphics.setCanvas(prevCanvas)
love.graphics.setShader() love.graphics.setShader()
love.graphics.setBlendMode(prevBlendMode) love.graphics.setBlendMode(prevBlendMode)
love.graphics.draw(cachedCanvas, x, y) love.graphics.draw(cachedCanvas, x, y)
love.graphics.setShader(prevShader) love.graphics.setShader(prevShader)
love.graphics.setColor(unpack(prevColor)) love.graphics.setColor(unpack(prevColor))
return return
end end
-- Not cached, render and cache -- Not cached, render and cache
if not backdropCanvas then if not backdropCanvas then
if Blur._ErrorHandler then if Blur._ErrorHandler then
@@ -548,6 +600,9 @@ function Blur:applyBackdropCached(intensity, x, y, width, height, backdropCanvas
return return
end end
-- Check for large blur area in immediate mode
checkLargeBlurWarning(elementId, width, height, "backdrop")
intensity = math.max(0, math.min(100, intensity)) intensity = math.max(0, math.min(100, intensity))
local passes = math.ceil(intensity / 20) local passes = math.ceil(intensity / 20)

View File

@@ -291,6 +291,12 @@ local ErrorCodes = {
description = "Critical performance threshold exceeded", description = "Critical performance threshold exceeded",
suggestion = "Operation is causing frame drops. Consider optimizing or reducing frequency.", suggestion = "Operation is causing frame drops. Consider optimizing or reducing frequency.",
}, },
PERF_003 = {
code = "FLEXLOVE_PERF_003",
category = "PERF",
description = "Large blur area in immediate mode",
suggestion = "Consider using retained mode for this component to avoid recreating blur effects every frame.",
},
-- Memory Warnings (MEM_001 - MEM_099) -- Memory Warnings (MEM_001 - MEM_099)
MEM_001 = { MEM_001 = {