Утечка пространства при глянцевом рендеринге изменяемого изображения
Я использовалJuicyPixel
библиотека для генерации визуализированного изображения иgloss
библиотека для предварительного просмотра в реальном времени.
Приведенный ниже фрагмент кода вызывает утечку пространства
viewportRenderer :: Viewport Picture
viewportRenderer = do
eventText <- Color [rgb|#FFFFFF|] . scale 0.1 0.1 . Text . show . _lastEvent <$> get
viewportLoc' <- gets _viewportLoc
viewportScale' <- gets _viewportScale
image <- gets _renderedImage >>= unsafeFreezeImage
let viewport =
Color [rgb|#323232|] $
unV2 translate viewportLoc' $
join scale viewportScale' $
fromImageRGBA8 image
return (Pictures [viewport, eventText])
Некоторый дополнительный контекст:
type RenderedImage = MutableImage RealWorld PixelRGBA8
data ViewportState = ViewPortState
{ _lastEvent :: Event
, _viewportScale :: !Float
, _viewportOrigin :: V2 Float
, _viewportLoc :: V2 Float
, _repeatActions :: [(Event -> Bool, StateT ViewportState IO ())]
-- ^ Actions that get repeated until (Event -> Bool) returns True
, _renderedImage :: !RenderedImage
}
makeLenses ''ViewportState
type Viewport a = StateT ViewportState IO a
-- Uses lazy StateT
initialViewPortState :: RenderedImage -> ViewportState
initialViewPortState image =
ViewPortState
{ _lastEvent = EventResize (0, 0) -- Sentinel value
, _viewportScale = 1
, _viewportOrigin = 0
, _viewportLoc = 0
, _repeatActions = []
, _renderedImage = image
}
viewWindow :: RenderedImage -> IO ()
viewWindow !image = do
playIO
(InWindow "Reticule-Minor viewport" (400, 300) (100, 100))
[rgb|#0B0B0B|]
60
(initialViewPortState image)
(fmap fst . runStateT viewportRenderer)
(\event -> fmap snd . runStateT (eventHandler event))
(\t -> fmap snd . runStateT (timeHandler t))
renderer :: RenderedImage -> IO ()
renderer image = do
forM_ [0..399] \x -> forM_ [0..299] \y -> do
let r = floor @Float $ (fromIntegral x / 399) * 255
g = floor @Float $ (fromIntegral y / 299) * 255
writePixel image x y (PixelRGBA8 r g 255 255)
threadDelay 10
main :: IO ()
main = do
image <- createMutableImage 400 300 (PixelRGBA8 255 255 255 0) >>= newIORef
_ <- forkIO $ renderer image
V.viewWindow image
https://github.com/Perigord-Kleisli/reticule-minor (репозиторий, содержащий всю кодовую базу)
Идея состоит в том, чтобы постоянно писатьrenderer
разветвляется в фоновом режиме и отображает результат в каждом кадре. Хотя, как сказано,viewportRenderer
вызывает утечку пространства. Мне интересно, можно ли это исправить, изменив строгость, или мне, вероятно, следует использовать другую структуру данных для передачи изображения.
Я бы не назвал это «лучшей практикой», но мне действительно не нужно иметь дело с условиями гонки.
Я экспериментировал с использованием
IORef RenderedImage
иMVar RenderedImage
хотя оба они не устранили утечку пространства.Его также невозможно было оптимизировать путем передачи
-O2
как вариант компиляции.Я попробовал это, просто генерируя новое изображение в каждом кадре, и это не привело к утечке пространства. Если я не создаю изображение из
RenderedImage
.Я прочитал изображение в обоих
freezeImage
, хотя оба вызвали утечку пространства. (unsafeFreezeImage
был быстрее и дал те же результаты, что странно, поскольку в документации библиотеки упоминается, что вы не должны иметь возможность использоватьMutableImage
после прохождения)
Изменить: я провел некоторое профилирование, которое привело к следующим результатам.
Выходные данные профилировщика:https://pastebin.com/3YMFpAem
Результаты, по общему признанию, весьма неожиданны, посколькуcreateMutableImage
несет ответственность за большую часть ассигнований.
1 ответ
Проблема решена, по-видимому, это связано с тем, что функция Gloss-Jicy , которую я использовал, постоянно кэшировала изображение в каждом кадре. Это реализовано как:
fromImageRGBA8 :: Image PixelRGBA8 -> Picture
fromImageRGBA8 (Image { imageWidth = w, imageHeight = h, imageData = id }) =
bitmapOfForeignPtr w h
(BitmapFormat TopToBottom PxRGBA)
ptr True
where (ptr, _, _) = unsafeToForeignPtr id
Поэтому я просто скопировал его и установил
Bool
аргумент для
bitmapOfForeignPtr
кFalse
.