From: Jules B. <ju...@je...> - 2008-03-13 13:49:37
|
Hi, Thanks to Eric's help earlier, I managed to get wx running and run some simple programs in it. I have run into the following (unconnected) problems with the API: There doesn't seem to be a binding to wxImage's "SetOption" call. I need to this to save a JPEG at anything other than the default quality. I'm loading in a JPEG, cropping, and saving out: if you recompress that at low quality you really suffer from jpg artifacts. There doesn't seem to be a binding to the "wxImage-from-WXStream" constructor. In particular, it would be nice to have a version using a wxMemoryInputStream so that you can load a wxImage from a Ptr, such as if you get an image via a network protocol or embedded in a larger file. I have random segfaults when calling imageGetPixels. Seems to be with a probability of something like 25%. Here is the code I've written (which generates an openGL texture from a WX bitmap): textureFromBitmap bmp = do image <- imageCreateFromBitmap bmp pixels <- imageGetData image putStrLn $ "pixels : " ++ show pixels bytes <- peekArray 8 (castPtr pixels) putStrLn $ "bytes : " ++ show (bytes :: [Word8]) WX.Size w h <- imageGetSize image putStrLn $ "Image size : " ++ show (w,h) pixs <- imageGetPixels image putStrLn $ "number of pixs = " ++ show (length pixs) putStrLn $ "first pixel " ++ show (head pixs) [tex] <- genObjectNames 1 textureBinding Texture2D $= Just tex textureWrapMode Texture2D T $= (Repeated,Repeat) textureWrapMode Texture2D S $= (Repeated,Repeat) textureFilter Texture2D $= ((Nearest, Nothing), Linear') let wxToColor3 :: WX.Color -> Color3 GLubyte wxToColor3 c = Color3 (fromIntegral $ WX.colorRed c) (fromIntegral $ WX.colorGreen c) (fromIntegral $ WX.colorBlue c) padded | w `mod` 2 == 0 = pixs | otherwise = concatMap (\x -> x ++ [WX.colorRGB 0 0 0]) . chunksOf w $ pixs chunksOf n l = takeWhile (not.null) . map (take n) . iterate (drop n) $ l withArray (map wxToColor3 padded) $ \a -> texImage2D Nothing NoProxy 0 RGBA' (TextureSize2D (fromIntegral w) (fromIntegral h)) 0 (PixelData RGB UnsignedByte a) return tex It doesn't appear to be relevant where the bitmap comes from, I've seen the segfault with bitmaps coming from "bitmapCreateFromFile" as well as from "bitmapCreateEmpty". About 1 time in 4, this will segfault with the last thing printed being the "Image size" putStrLn, leading me to think that imageGetPixels is segfaulting. gdb doesn't tell me anything useful - presumably because my wx libraries are not compiled with debug info? Is there a wx configure option for that? I'll have a look and see what I can see. Thanks for providing such a useful binding, Jules Bean |
From: Eric K. <eri...@gm...> - 2008-03-13 15:50:28
|
Hi Jules, Is this the same bug as this one from the Tracker? http://sourceforge.net/tracker/index.php?func=detail&aid=1003006&group_id=73133&atid=536845 (was submitted by Paul Johnson, I think) Also, if you are so inclined, it would be extra-helpful if you could submit your demonstrator as a test case in the bugs/ directory (darcs send). It would be extra-extra-helpful if you could work out a way to reproduce this bug very consistently (for example, by repeatedly loading the image in or something) Thanks! -- Eric Kow <http://www.nltg.brighton.ac.uk/home/Eric.Kow> PGP Key ID: 08AC04F9 |
From: Jules B. <ju...@je...> - 2008-03-13 19:39:32
|
Eric Kow wrote: > Hi Jules, > > Is this the same bug as this one from the Tracker? > http://sourceforge.net/tracker/index.php?func=detail&aid=1003006&group_id=73133&atid=536845 > > (was submitted by Paul Johnson, I think) > > Also, if you are so inclined, it would be extra-helpful if you could > submit your demonstrator as a test case in the bugs/ directory (darcs > send). It would be extra-extra-helpful if you could work out a way to > reproduce this bug very consistently (for example, by repeatedly > loading the image in or something) Hi Eric, It does look like it could well be the same bug. Although seems surprising for there to be a hole in the memory? Perhaps it is being moved by GC... but GC doesn't move memory which was allocated by a C(++) library does it? I can reproduce the bug simply by creating images over and over and over again. Here is a fairly minimal testcase, at the bottom of this mail. It always seems to segfault for me, normally after about 5 iterations, sometimes as many as 22. It's interesting that there is a very long pause before the segfault. That is, one iteration of creating an image is normally a tiny tiny fraction of a second, but if it's going to segfault, there is a long pause. I tried running with -Sstderr to see if I could see an obvious link between garbage collections and crashes, but if there is one it isn't obvious to me! Happy to try more stuff to pin this down, let me know what I can do. Jules import qualified Graphics.UI.WX as WX import Graphics.UI.WXCore.Image import Graphics.UI.WXCore.Draw import Graphics.UI.WXCore.WxcClassesAL import Graphics.UI.WXCore.WxcClassesMZ import Foreign import Data.Word import Control.Monad main = do forM_ [0..500] (\i -> putStrLn ("Iteration : " ++ show i) >> createAndGetPixels) createAndGetPixels = do bmp <- bitmapCreateEmpty (WX.Size 256 256) 24 pixelsFromBitmap bmp pixelsFromBitmap bmp = do image <- imageCreateFromBitmap bmp pixels <- imageGetData image putStrLn $ "pixels : " ++ show pixels bytes <- peekArray 8 (castPtr pixels) putStrLn $ "bytes : " ++ show (bytes :: [Word8]) WX.Size w h <- imageGetSize image putStrLn $ "Image size : " ++ show (w,h) pixs <- imageGetPixels image putStrLn $ "number of pixs = " ++ show (length pixs) putStrLn $ "first pixel " ++ show (head pixs) |
From: Eric K. <eri...@gm...> - 2008-03-13 21:56:32
|
Hi Jules, Thanks for the test case. It segfaults right away for me on Linux (iteration 0). I don't even need the bitmap to make it do this; just image <- imageCreateSized (WX.Size 256 256) Also, I notice that it seems to happen Graphics.UI.WXCore.pixelBufferGetPixels (This is via the old fashioned putStrLn style of debugging) Do you confirm these? -- Eric Kow <http://www.nltg.brighton.ac.uk/home/Eric.Kow> PGP Key ID: 08AC04F9 |
From: Jules B. <ju...@je...> - 2008-03-13 22:05:37
|
Eric Kow wrote: > Hi Jules, > > Thanks for the test case. > > It segfaults right away for me on Linux (iteration 0). I don't even > need the bitmap to make it do this; just > image <- imageCreateSized (WX.Size 256 256) > > Also, I notice that it seems to happen Graphics.UI.WXCore.pixelBufferGetPixels > > (This is via the old fashioned putStrLn style of debugging) > > Do you confirm these? Yes. And what pixelBufferGetPixels does is peekCStringLen, which is essentially reading the entire buffer that image owns. Whereas the other bug report you pointed me to read the image's buffer more gradually, but still hit the same error. So somehow, sometimes, we're getting an invalid pointer. Whether it's a pointer to a buffer which is too short, or a buffer which has been moved, or.... Jules |
From: Eric K. <eri...@gm...> - 2008-03-13 22:17:13
|
> So somehow, sometimes, we're getting an invalid pointer. Whether it's a > pointer to a buffer which is too short, or a buffer which has been > moved, or.... Another observation is that this fails much more easily with bigger buffers. A sort of silly question: do you think you could cook up an equivalent demonstrator in C++ just using wxWidgets? -- Eric Kow <http://www.nltg.brighton.ac.uk/home/Eric.Kow> PGP Key ID: 08AC04F9 |
From: Jules B. <ju...@je...> - 2008-03-14 08:34:57
|
Eric Kow wrote: >> So somehow, sometimes, we're getting an invalid pointer. Whether it's a >> pointer to a buffer which is too short, or a buffer which has been >> moved, or.... > > Another observation is that this fails much more easily with bigger buffers. > > A sort of silly question: do you think you could cook up an equivalent > demonstrator in C++ just using wxWidgets? Yes. But it doesn't segfault :( I have reduced the haskell code to the following simplified version: import qualified Graphics.UI.WX as WX import Graphics.UI.WXCore.Image import Graphics.UI.WXCore.Draw import Graphics.UI.WXCore.WxcClassesAL import Graphics.UI.WXCore.WxcClassesMZ import Foreign import Data.Word import Control.Monad main = do forM_ [0..500] (\i -> putStrLn ("Iteration : " ++ show i) >> createAndGetPixels) createAndGetPixels = do image <- imageCreateSized (WX.Size 256 256) pixels <- imageGetData image putStrLn $ "pixels : " ++ show pixels -- putStr $ "Reading byte.." -- forM_ [0..256*256*3] $ \b -> do -- byte <- peekByteOff (castPtr pixels) b :: IO Word8 -- putStr (show b ++ "..") bytes <- peekArray (256*256*3) (castPtr pixels) putStrLn $ "bytes : " ++ show (length (bytes :: [Word8])) (The commented out code was me experimenting to see if there was a pattern in "which" byte causes the segfault. There isn't, that I can see) And here is my attempt to replicate this exact set of calls from C++: #include <iostream> #include "wx/wx.h" #include "wx/image.h" void createAndGetPixels(); int main() { for (int i=0; i<500; i++) { std::cout << "Iteration " << i << std::endl; createAndGetPixels(); } } void createAndGetPixels() { wxImage *im = new wxImage(256,256); unsigned char* pixels = im->GetData(); std::cout << "pixels : " << ((void*)pixels) << std::endl; void *bytes = malloc(256*256*3); memcpy(bytes,pixels,256*256*3); //free(bytes); //delete im; } And it doesn't seem to segfault, ever. If you leave the "free" and "delete" in then the nice deterministic C malloc gives you back the same block of memory each time! that's why I commented them out. Jules |
From: Eric Y. K. <eri...@gm...> - 2008-03-14 09:42:04
|
On Fri, Mar 14, 2008 at 08:34:48 +0000, Jules Bean wrote: > But it doesn't segfault :( :-/ Well, at least this means it might be something we can fix. > void createAndGetPixels() { > wxImage *im = new wxImage(256,256); > unsigned char* pixels = im->GetData(); > std::cout << "pixels : " << ((void*)pixels) << std::endl; > void *bytes = malloc(256*256*3); > memcpy(bytes,pixels,256*256*3); > //free(bytes); > //delete im; I modified the wxc wrapper so that it copies the contents of the array into a whole new one... and that stops the segfaulting. It doesn't explain very much (to me), though, and the whole introducing a memory leak thing may not be so much fun. EWXWEXPORT(void*, wxImage_GetData)(void* _obj) { int w = ((wxImage*)_obj)->GetWidth(); int h = ((wxImage*)_obj)->GetHeight(); void *bytes = malloc(h*w*3); void *pixels = ((wxImage*)_obj)->GetData(); memcpy(bytes,pixels,h*w*3); return (void*)bytes; } I have honestly no idea what's going on here. -- Eric Kow <http://www.nltg.brighton.ac.uk/home/Eric.Kow> PGP Key ID: 08AC04F9 |
From: Jules B. <ju...@je...> - 2008-03-14 09:51:59
|
Eric Y. Kow wrote: > I have honestly no idea what's going on here. I've worked it out. With a little help from malcolmw on #ghc. 1 image <- imageCreateSized (WX.Size 256 256) 2 pixels <- imageGetData image 3 bytes <- peekArray (256*256*3) (castPtr pixels) After line 2 is executed, there is no remaining reference to 'image' in the program. 'image' is dead, and can be garbage collected. (Although it may not be at any particular time). If 'image' gets GC'ed, because there is a ForeignPtr inside, that calls the C++ destructor. The wxImage C++ destructor believes it owns the data block, so it free()s it, and 'pixels' is left pointing to a free()ed block. This is why it is inconsistent. It depends if image happens to get GC'ed before the peekArray completes running. As to how we fix it? Well as a proof of concept, adding: putStrLn $ "Keep image alive with this reference " ++ (show image) as the last line of createAndGetPixels will prevent the GC and stop the segfault. The right fix tho? I'm not sure. It seems like 'imageGetData' is fundamentally broken (and thus anything that depends on it, like getPixels). Is there some way imageGetData could return a Ptr and simultaneously keep the 'image' alive for as long as the Ptr is alive? Should imageGetData be turned into a 'with-style' function Image a -> (Ptr () -> IO b) -> IO b which protects the image? Is there a workaround I can use in my code like touchPtr? A version of touchPtr which works on WXObjects? Jules |
From: Eric Y. K. <eri...@gm...> - 2008-03-14 10:06:07
|
> I've worked it out. With a little help from malcolmw on #ghc. Excellent! Thanks very much. I learned something new. > Should imageGetData be turned into a 'with-style' function > > Image a -> (Ptr () -> IO b) -> IO b > > which protects the image? Ultimately, this seems like the right thing to do, for all functions that return pointers to underlying bits of data (I am slightly scared that there may be loads of these lurking around). > Is there a workaround I can use in my code like touchPtr? A version of > touchPtr which works on WXObjects? Well, I added image `seq` return () to your demonstrator. Not sure if that really does what we want. I suppose we could augment the PixelBuffer type to include a pointer to the original image. -- Eric Kow <http://www.nltg.brighton.ac.uk/home/Eric.Kow> PGP Key ID: 08AC04F9 |
From: Eric Y. K. <eri...@gm...> - 2008-03-14 10:15:11
|
On Fri, Mar 14, 2008 at 10:05:55 +0000, Eric Y. Kow wrote: > I suppose we could augment the > PixelBuffer type to include a pointer to the original image. Arrrr, imprecise thinking. That's not what I meant to say. Anyway, I think you get my idea of trying to keep the image reference/thingy alive within the Haskelley bits (wxcore). -- Eric Kow <http://www.nltg.brighton.ac.uk/home/Eric.Kow> PGP Key ID: 08AC04F9 |
From: Jules B. <ju...@je...> - 2008-03-14 10:18:02
|
Eric Y. Kow wrote: >> Should imageGetData be turned into a 'with-style' function >> >> Image a -> (Ptr () -> IO b) -> IO b >> >> which protects the image? > > Ultimately, this seems like the right thing to do, for all functions > that return pointers to underlying bits of data (I am slightly scared > that there may be loads of these lurking around). The alternative is to use mallocForeignPtr or one of its friends, and return a ForeignPtr to a copy of the buffer. Clearly both alternatives could be supported. For most purposes the (Ptr () -> IO b) approach is probably neater, though. After all if you do need direct access to a Ptr, it's probably because you're calling another FFI funciton on it (in my case, texImage2d), which will itself "take a copy" of the memory, and then it's safe to destroy. So in my example withImageData (\d -> texImage2d d ...) would seem like a pretty elegant answer. > >> Is there a workaround I can use in my code like touchPtr? A version of >> touchPtr which works on WXObjects? > > Well, I added image `seq` return () to your demonstrator. Not sure if > that really does what we want. I suppose we could augment the > PixelBuffer type to include a pointer to the original image. I think that makes it safe as long as PixelBuffer is considered opaque. But as soon as you provide a way of extracting the Ptr () from the Pixel Buffer, then the same problem comes back to bite you. As it happens, in my particular program, I don't use the Ptr directly because I need to pad to even width anyway, in general. So I was using imageGetPixels and thus would be safe under that scheme. Jules |
From: Eric Y. K. <eri...@gm...> - 2008-03-14 10:24:36
|
> > Ultimately, this seems like the right thing to do, for all functions > > that return pointers to underlying bits of data (I am slightly scared > > that there may be loads of these lurking around). > For most purposes the (Ptr () -> IO b) approach is probably neater, > though. After all if you do need direct access to a Ptr, it's probably > because you're calling another FFI funciton on it (in my case, > texImage2d), which will itself "take a copy" of the memory, and then > it's safe to destroy. Here's my implementation of it. Does it look sane to you? Any comments from wxhaskell developers? withImageData :: Image a -> (Ptr () -> IO b) -> IO b withImageData image f = do pixels <- imageGetData image x <- f pixels x `seq` image `seq` return x The seqs are there out of pure superstition (i.e. Eric doesn't know how Haskell works); basically, I want to make sure we're holding on to image as long as we need to, so at least until we know what 'x' is. If this is indeed sane, we could stick it in wxcore and put a big warning on the Haddocks for imageGetData (without removing it just yet). -- Eric Kow <http://www.nltg.brighton.ac.uk/home/Eric.Kow> PGP Key ID: 08AC04F9 |
From: shelarcy <she...@gm...> - 2008-03-14 10:53:59
|
Hi, On Fri, 14 Mar 2008 19:24:12 +0900, Eric Y. Kow <eri...@gm...> wrote: > Here's my implementation of it. Does it look sane to you? Any comments > from wxhaskell developers? > > withImageData :: Image a -> (Ptr () -> IO b) -> IO b > withImageData image f = do > pixels <- imageGetData image > x <- f pixels > x `seq` image `seq` return x > > The seqs are there out of pure superstition (i.e. Eric doesn't know how > Haskell works); basically, I want to make sure we're holding on to image > as long as we need to, so at least until we know what 'x' is. "a seq b" doesn't guarantee that evaluate a before b. If we want to do that, we must use Control.Parallel's pseq. http://www.haskell.org/ghc/docs/6.8.2/html/libraries/parallel/Control-Parallel.html#v%3Apseq Or to use $! operator in monad, like this. return $! x return $! image return x Best Regards, -- shelarcy <shelarcy hotmail.co.jp> http://page.freett.com/shelarcy/ |
From: Jules B. <ju...@je...> - 2008-03-14 10:39:55
|
Eric Y. Kow wrote: > Here's my implementation of it. Does it look sane to you? Any comments > from wxhaskell developers? > > withImageData :: Image a -> (Ptr () -> IO b) -> IO b > withImageData image f = do > pixels <- imageGetData image > x <- f pixels > x `seq` image `seq` return x > > The seqs are there out of pure superstition (i.e. Eric doesn't know how > Haskell works); basically, I want to make sure we're holding on to image > as long as we need to, so at least until we know what 'x' is. The x `seq` is likely pointless. "x" is a pure value and unless someone's cheating with unsafePerformIO, merely forcing a pure value can't cause any Ptr access to happen. the image `seq` looks like a reasonable hack to keep image alive. The canonical way would be "touchForeignPtr image" or to use "withForeignPtr image", except that image isn't a ForeignPtr exactly. I don't know if there is a good way to access the ForeignPtr inside image because I haven't grokked enough of wx yet. See http://www.haskell.org/ghc/docs/latest/html/libraries/base/Foreign-ForeignPtr.html for these and related issues. > > If this is indeed sane, we could stick it in wxcore and put a big > warning on the Haddocks for imageGetData (without removing it just > yet). And fix imageGetPixels to use the new safe withImageData. Which would have made my bug go away. Jules |
From: Eric Y. K. <eri...@gm...> - 2008-03-16 17:05:27
|
Hi Jules, Just letting you know that I've pushed a patch into the darcs repository which I expect to fix this. Cheers, -- Eric Kow <http://www.nltg.brighton.ac.uk/home/Eric.Kow> PGP Key ID: 08AC04F9 |
From: Jules B. <ju...@je...> - 2008-03-17 18:52:19
|
Eric Y. Kow wrote: > Hi Jules, > > Just letting you know that I've pushed a patch into the darcs repository > which I expect to fix this. That's great news. I can confirm that the fix we discussed (image `seq` return foo) is enough to make the program I was actually working on work reliably. I will take the lack of answer to my other two questions (about bindings to getOption and the Stream-based-constructor) as "patches welcome" :) Jules |
From: Eric K. <eri...@gm...> - 2008-03-17 18:58:38
|
> I will take the lack of answer to my other two questions (about bindings > to getOption and the Stream-based-constructor) as "patches welcome" :) Ah yes, sorry about that. :-) Indeed, patches would be welcome. We can help you work out how everything fits together (basically, you will need to edit the wxc stuff, I think). Note that we're trying to get the release out by this weekend. I'm not sure how I'd feel about a hypothetical patch going in before then. On the one hand, it would be a new feature, which seems like a good idea to avoid just before a release, on the other hand it's just an add-on, which should be harmless. -- Eric Kow <http://www.nltg.brighton.ac.uk/home/Eric.Kow> PGP Key ID: 08AC04F9 |
From: Jules B. <ju...@je...> - 2008-03-17 19:15:04
|
Eric Kow wrote: >> I will take the lack of answer to my other two questions (about bindings >> to getOption and the Stream-based-constructor) as "patches welcome" :) > > Ah yes, sorry about that. :-) Indeed, patches would be welcome. We > can help you work out how everything fits together (basically, you > will need to edit the wxc stuff, I think). I'm pretty sure I can work it out. I delved pretty deep in order to track down this image bug. > Note that we're trying to get the release out by this weekend. I'm > not sure how I'd feel about a hypothetical patch going in before then. > On the one hand, it would be a new feature, which seems like a good > idea to avoid just before a release, on the other hand it's just an > add-on, which should be harmless. Don't expect them by the weekend, then :) Don't hold up a release. There will always be other releases. Jules |
From: Jules B. <ju...@je...> - 2008-03-14 11:00:59
|
shelarcy wrote: > Hi, > > On Fri, 14 Mar 2008 19:24:12 +0900, Eric Y. Kow <eri...@gm...> wrote: >> Here's my implementation of it. Does it look sane to you? Any comments >> from wxhaskell developers? >> >> withImageData :: Image a -> (Ptr () -> IO b) -> IO b >> withImageData image f = do >> pixels <- imageGetData image >> x <- f pixels >> x `seq` image `seq` return x >> >> The seqs are there out of pure superstition (i.e. Eric doesn't know how >> Haskell works); basically, I want to make sure we're holding on to image >> as long as we need to, so at least until we know what 'x' is. > > "a seq b" doesn't guarantee that evaluate a before b. True, but that is not relevant here. It does guarantee that both get evaluated. In fact, all we need to do here is keep image alive. Evaluating it is not the point. > If we want to do that, we must use Control.Parallel's pseq. > > http://www.haskell.org/ghc/docs/6.8.2/html/libraries/parallel/Control-Parallel.html#v%3Apseq > > Or to use $! operator in monad, like this. > > return $! x > return $! image > return x that just uses seq. return $! x is just x `seq` return x that would add nothign to this code. Jules |
From: shelarcy <she...@gm...> - 2008-03-14 11:18:31
|
On Fri, 14 Mar 2008 20:00:54 +0900, Jules Bean <ju...@je...> wrote: >> "a seq b" doesn't guarantee that evaluate a before b. > > True, but that is not relevant here. It does guarantee that both get > evaluated. Okay, I see. I confused that. >> return $! x >> return $! image >> return x > > that just uses seq. > > return $! x is just x `seq` return x > > that would add nothign to this code. Ah ... it's my mistake. It just reduce x and image to WHNF. Best Regards, -- shelarcy <shelarcy hotmail.co.jp> http://page.freett.com/shelarcy/ |