dwmkerr / sharpgl

Use OpenGL in .NET applications. SharpGL wraps all modern OpenGL features and offers a powerful scene graph to aid development.
MIT License
755 stars 299 forks source link

Alternative to using CreateBitmapSourceFromHBitmap for rendering in WPF #121

Open ftlPhysicsGuy opened 8 years ago

ftlPhysicsGuy commented 8 years ago

Topic: With the help of a friend, I've found an alternative to using CreateBitmapSourceFromHBitmap when implementing SharpGL in a WPF application!

Working through a couple of problems/solutions has lead me to use an alternative method for creating a bitmap source from a RenderContextProvider (DIB Section or FBO) without always calling System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(...) (via a call to HBitmapToBitmapSource(IntPtr hBitmap) in BitmapConversion.cs):

Issue 1: the GC.Collect() call made with every call to HBitmapToBitmapSource slows down rendering and can be significant when memory usage increases.

Solution 1: I found that by removing the GC.Collect call, I could add a catch for any OutOfMemoryException and perform a GC.Collect() (and set bitSrc = null) at that point. However, I then had to be careful to check for null bitSrc returns and I never actually hit a memory issue to cause the problem -- possibly because CreateBitmapSourceFromHBitmap was doing garbage collection internally (see Issue 2 below).

Issue 2: When memory usage got fairly high (around 1.2 GB used - which could happen with my particular application), EVERY CALL to CreateBitmapSourceFromHBitmap would cause a garbage collection, often taking 900+ ms to complete. I validated this by looking at GC.CollectionCount(0) before and after the call to CreateBitmapSourceFromHBitmap and using a System.Diagnosis.Stopwatch to verify that the processing time was being taken up by that specific call.

Solution 2: Here, I had to get away from the call to CreateBitmapSourceFromHBitmap. A friend of mine found an alternative and provided an initial implementation (BIG thanks to him!). After playing around with it, I've come up with my current method for rendering to the image in OpenGLControl.xaml.cs. Essentially, I use System.Windows.Media.Imaging.WriteableBitmap as the image.Source and refill it (vice re-creating it) on every tick. I call System.Runtime.InteropServices.Marshal.Copy(...) to copy from a bits pointer to an image buffer and use that to write to the WriteableBitmap. This then becomes the source of the image. You have to set up the buffer and WriteableBitmap when the size of the display changes, and you have to use image.RenderTransform and image.RenderTransformOrigin to flip the image vertically for an FBORenderContextProvider, but the whole process seems as fast (if not faster) than the current method.

I still have to use ONE call too HBitmapToBitmapSource (if we believe the dpiX, dpiY, and/or PixelFormat of the bitmap will ever be different from 96, 96 and PixelFormats.Bgra32 respectively) but it's only once and it causes no issues.

Here's my entire solution to modifying the OpenGLControl.xaml.cs file:

I also have a test application that creates a spinning line (like a radar screen) and provides a "Run Test" button (which creates 16,000,000 objects to seriously increase memory usage). If you use the old OpenGLControl, the spinner slows to about one tick per second after running the test. With the new OpenGLControl I'm using, there's no noticeable change after the test (unless you resize -- then there's a slight delay before it gets back to normal since it has to re-create the WriteableBitmap).

Finally, note that with this modification, there is no longer a need to modify the BitmapConversion code.

If it would be helpful, I could post my modified OpenGLControl.xaml.cs file.

Thanks.

-Jason

Thormidable commented 8 years ago

Hi Jason, If you could post your modified OpenGLControl.xaml.cs file, that would be great. We are currently working on reducing the SharpGL.WPF overheads in our application and were planning on using WriteableBitmap to reduce the work done. It would save us a fair bit of work if you would be good enough to upload your version of this class.

Thank you

ftlPhysicsGuy commented 8 years ago

Hi, Thormidable....

I'm attaching the modified file here. Hope it helps!

-Jason

OpenGLControl.xaml.cs.zip

Thormidable commented 8 years ago

Hi Jason, Thank you, I've seen a substantial improvement in frame rate already

xueqingsun commented 7 years ago

@Thormidable Was this code change adopted by SharpGL and included in a new release? I tried to remove GC.Collect() in BitmapConversion.cs, it worked, the frame rate was improved greatly.

Thormidable commented 7 years ago

@xueqingsun it is in @ewngs repo. Grab that, it's got all the updates from this repo, plus a few more fixes. https://github.com/ewngs/sharpgl/commits/master

xueqingsun commented 7 years ago

Many thanks to @Thormidable and @ewngs, The performance improved greatly. On my machine, the FPS is 20 and previously it was 8 to 10. ewngs's code change worked.

Instead the change of ewngs, merely remove GC.Collect() will work as well as the ewngs's change. Normally we don't call GC to collect. So why is GC.Collect() there?

xueqingsun commented 7 years ago

By the way, the way of using stopwatch in OpenGLControl won't give a correct FPS. It will give a much higher FPS than the real FPS.

ftlPhysicsGuy commented 7 years ago

Just to note: in my situation, the app would get into a state where the call to

CreateBitmapSourceFromHBitmap was internally causing its own garbage collection every time it was called. I could remove the manual GC, but the issue persisted.

-Jason

On Wed, Jan 18, 2017 at 7:27 PM Xueqing Sun notifications@github.com wrote:

Many thanks to @Thormidable https://github.com/Thormidable and @ewngs https://github.com/ewngs, The performance improved greatly. On my machine, the FPS is 20 and previously it was 8 to 10. ewngs's code change worked.

Instead the change of ewngs, merely remove GC.Collect() will work as well as the ewngs's change. Normally we don't call GC to collect. So why is GC.Collect() there?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/dwmkerr/sharpgl/issues/121#issuecomment-273655482, or mute the thread https://github.com/notifications/unsubscribe-auth/AHeoTN1riXSBiZb5rZ3XxBV9Gs-_ssgoks5rTrv-gaJpZM4H8Zsn .