英文:
Space Leak on Gloss render of Mutable Image
问题
I used the JuicyPixel
library to generate a rendered image and the gloss
library for a live preview.
The following Haskell code snippet appears to be causing a space leak:
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])
Some additional context:
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
-- ...
viewWindow :: RenderedImage -> IO ()
-- ...
renderer :: RenderedImage -> IO ()
-- ...
main :: IO ()
-- ...
The idea is to continually write to a MutableImage
in the background using the renderer
function, and then display the result in the viewportRenderer
function. However, viewportRenderer
is causing a space leak.
You've tried using IORef RenderedImage
and MVar RenderedImage
but didn't resolve the space leak. Profiling shows that createMutableImage
is responsible for most allocations.
You're wondering if modifying strictness or using a different data structure might fix the issue.
Please let me know if you need specific advice or further assistance with this code.
英文:
I used the JuicyPixel
library for generating a rendered image and the gloss
library for a live preview.
The piece of code below causes a Space Leak
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])
Some additional context:
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
(Repo containing the whole codebase)
The idea is to have a MutableImage
be continually written to by renderer
forked in the background and to have viewportRenderer
display the result every frame. Though as said, viewportRenderer
causes a space leak. I'm wondering if this can be fixed by modifying Strictness or if I should probably utilize another Data Structure for passing the image.
I wouldnt really call it "best practice" but I don't really need to deal with having race conditions here.
-
I've experimented on using
IORef RenderedImage
andMVar RenderedImage
though both of these didn't fix the space leak. -
It was also unable to be optimized away by passing
-O2
as a compile option. -
I tried it with just generating a new image every frame and it didn't create a space leak. Unless I generate an image from the
RenderedImage
. -
I've read the image in both
freezeImage
,unsafeFreezeImage
though both caused space leaks. (unsafeFreezeImage
was faster and produced the same results which is strange as the library docs mention that you shouldnt be able to use theMutableImage
after passing it)
Edit: I did some profiling which resulted in the following results.
With the profiler output being:
https://pastebin.com/3YMFpAem
The results are admittedly quite surprising, with createMutableImage
being responsible for most of the allocations.
答案1
得分: 4
已解决问题,显然是因为我连续使用的gloss-juicy函数每帧都缓存了图像。它的实现如下:
fromImageRGBA8 :: Image PixelRGBA8 -> Picture
fromImageRGBA8 (Image { imageWidth = w, imageHeight = h, imageData = id }) =
bitmapOfForeignPtr w h
(BitmapFormat TopToBottom PxRGBA)
ptr True
where (ptr, _, _) = unsafeToForeignPtr id
所以我只是复制了它,并将bitmapOfForeignPtr的Bool
参数设置为False
。
英文:
Solved the issue, apparently it's because the gloss-juicy function I used continually cached the image each frame. It is implemented as:
fromImageRGBA8 :: Image PixelRGBA8 -> Picture
fromImageRGBA8 (Image { imageWidth = w, imageHeight = h, imageData = id }) =
bitmapOfForeignPtr w h
(BitmapFormat TopToBottom PxRGBA)
ptr True
where (ptr, _, _) = unsafeToForeignPtr id
So I just copied it and set the Bool
argument to bitmapOfForeignPtr
to False
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论