diff --git a/source/SharpGL/Core/SharpGL.WPF/OpenGLControl.xaml.cs b/source/SharpGL/Core/SharpGL.WPF/OpenGLControl.xaml.cs
index c36cdde8..39a3ed36 100644
--- a/source/SharpGL/Core/SharpGL.WPF/OpenGLControl.xaml.cs
+++ b/source/SharpGL/Core/SharpGL.WPF/OpenGLControl.xaml.cs
@@ -3,6 +3,7 @@
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
@@ -17,6 +18,16 @@ namespace SharpGL.WPF
///
public partial class OpenGLControl : UserControl
{
+ // Fields to support the WritableBitmap method of rendering the image for display
+ byte[] m_imageBuffer;
+ WriteableBitmap m_writeableBitmap;
+ Int32Rect m_imageRect;
+ int m_imageStride;
+ double m_dpiX = 96;
+ double m_dpiY = 96;
+ PixelFormat m_format = PixelFormats.Bgra32;
+ int m_bytesPerPixel = 32 >> 3;
+
///
/// Initializes a new instance of the class.
///
@@ -105,6 +116,8 @@ private void UpdateOpenGLControl(int width, int height)
}
}
}
+ // Force re-creation of image buffer since size has changed
+ m_imageBuffer = null;
}
///
@@ -123,6 +136,9 @@ public override void OnApplyTemplate()
gl.Create(OpenGLVersion, RenderContextType, 1, 1, 32, null);
}
+ // Force re-set of dpi and format settings
+ m_dpiX = 0;
+
// Create our fast event args.
eventArgsFast = new OpenGLEventArgs(gl);
@@ -184,10 +200,7 @@ void timer_Tick(object sender, EventArgs e)
if (hBitmap != IntPtr.Zero)
{
- var newFormatedBitmapSource = GetFormatedBitmapSource(hBitmap);
-
- // Copy the pixels over.
- image.Source = newFormatedBitmapSource;
+ FillImageSource(provider.DIBSection.Bits, hBitmap);
}
}
break;
@@ -202,10 +215,7 @@ void timer_Tick(object sender, EventArgs e)
if (hBitmap != IntPtr.Zero)
{
- var newFormatedBitmapSource = GetFormatedBitmapSource(hBitmap);
-
- // Copy the pixels over.
- image.Source = newFormatedBitmapSource;
+ FillImageSource(provider.InternalDIBSection.Bits, hBitmap);
}
}
break;
@@ -395,5 +405,62 @@ public OpenGL OpenGL
{
get { return gl; }
}
+
+ /// Fill the ImageSource from the provided bits IntPtr, using the provided hBitMap IntPtr
+ /// if needed to determine key data from the bitmap source.
+ ///
+
+
+ /// An IntPtr to the bits for the bitmap image. Generally provided from
+ /// DIBSectionRenderContextProvider.DIBSection.Bits or from
+ /// FBORenderContextProvider.InternalDIBSection.Bits.
+ /// An IntPtr to the HBitmap for the image. Generally provided from
+ /// DIBSectionRenderContextProvider.DIBSection.HBitmap or from
+ /// FBORenderContextProvider.InternalDIBSection.HBitmap.
+ public void FillImageSource(IntPtr bits, IntPtr hBitmap)
+ {
+ // If DPI hasn't been set, use a call to HBitmapToBitmapSource to fill the info
+ // This should happen only ONCE (near the start of the application)
+ if (m_dpiX == 0)
+ {
+ var bitmapSource = BitmapConversion.HBitmapToBitmapSource(hBitmap);
+ m_dpiX = bitmapSource.DpiX;
+ m_dpiY = bitmapSource.DpiY;
+ m_format = bitmapSource.Format;
+ m_bytesPerPixel = gl.RenderContextProvider.BitDepth >> 3;
+ // FBO render context flips the image vertically, so transform to compensate
+ if (RenderContextType == SharpGL.RenderContextType.FBO)
+ {
+ image.RenderTransform = new ScaleTransform(1.0, -1.0);
+ image.RenderTransformOrigin = new Point(0.0, 0.5);
+ }
+ else
+ {
+ image.RenderTransform = Transform.Identity;
+ image.RenderTransformOrigin = new Point(0.0, 0.0);
+ }
+ }
+ // If the image buffer is null, create it
+ // This should happen when the size of the image changes
+ if (m_imageBuffer == null)
+ {
+ int width = gl.RenderContextProvider.Width;
+ int height = gl.RenderContextProvider.Height;
+
+ int imageBufferSize = width * height * m_bytesPerPixel;
+ m_imageBuffer = new byte[imageBufferSize];
+ m_writeableBitmap = new WriteableBitmap(width, height, m_dpiX, m_dpiY, m_format, null);
+ m_imageRect = new Int32Rect(0, 0, width, height);
+ m_imageStride = width * m_bytesPerPixel;
+ }
+
+ // Fill the image buffer from the bits and create the writeable bitmap
+ System.Runtime.InteropServices.Marshal.Copy(bits, m_imageBuffer, 0, m_imageBuffer.Length);
+ m_writeableBitmap.WritePixels(m_imageRect, m_imageBuffer, m_imageStride, 0);
+
+ image.Source = m_writeableBitmap;
+ }
+
+
}
}
diff --git a/source/SharpGL/Core/SharpGL/RenderContextProviders/FBORenderContextProvider.cs b/source/SharpGL/Core/SharpGL/RenderContextProviders/FBORenderContextProvider.cs
index e2e74c5b..99267f99 100644
--- a/source/SharpGL/Core/SharpGL/RenderContextProviders/FBORenderContextProvider.cs
+++ b/source/SharpGL/Core/SharpGL/RenderContextProviders/FBORenderContextProvider.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.InteropServices;
using System.Text;
using SharpGL.Version;
@@ -8,6 +9,9 @@ namespace SharpGL.RenderContextProviders
{
public class FBORenderContextProvider : HiddenWindowRenderContextProvider
{
+ [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
+ public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
+
///
/// Initializes a new instance of the class.
///
@@ -60,6 +64,14 @@ public override bool Create(OpenGLVersion openGLVersion, OpenGL gl, int width, i
gl.FramebufferRenderbufferEXT(OpenGL.GL_FRAMEBUFFER_EXT, OpenGL.GL_DEPTH_ATTACHMENT_EXT,
OpenGL.GL_RENDERBUFFER_EXT, depthRenderBufferID);
+
+ if(usePBO)
+ {
+ pboIds = new uint[PBO_COUNT];
+ int byte_count = width * height * 4;
+ CreatePBOs((uint)byte_count, pboIds);
+ }
+
dibSectionDeviceContext = Win32.CreateCompatibleDC(deviceContextHandle);
// Create the DIB section.
@@ -68,6 +80,17 @@ public override bool Create(OpenGLVersion openGLVersion, OpenGL gl, int width, i
return true;
}
+ private void CreatePBOs(uint byte_count, uint[] ids)
+ {
+ gl.GenBuffersARB(PBO_COUNT, pboIds);
+ for (int i = 0; i < PBO_COUNT; ++i)
+ {
+ gl.BindBufferARB(OpenGL.GL_PIXEL_PACK_BUFFER_ARB, ids[i]);
+ gl.BufferDataARB(OpenGL.GL_PIXEL_PACK_BUFFER_ARB, byte_count, (IntPtr)0, OpenGL.GL_STREAM_READ_ARB);
+ }
+ gl.BindBufferARB(OpenGL.GL_PIXEL_PACK_BUFFER_ARB, 0);
+ }
+
private void DestroyFramebuffers()
{
// Delete the render buffers.
@@ -80,6 +103,11 @@ private void DestroyFramebuffers()
colourRenderBufferID = 0;
depthRenderBufferID = 0;
frameBufferID = 0;
+
+ if (usePBO)
+ {
+ gl.DeleteBuffersARB(PBO_COUNT, pboIds);
+ }
}
public override void Destroy()
@@ -141,22 +169,53 @@ public override void SetDimensions(int width, int height)
OpenGL.GL_RENDERBUFFER_EXT, colourRenderBufferID);
gl.FramebufferRenderbufferEXT(OpenGL.GL_FRAMEBUFFER_EXT, OpenGL.GL_DEPTH_ATTACHMENT_EXT,
OpenGL.GL_RENDERBUFFER_EXT, depthRenderBufferID);
+
+ if (usePBO)
+ {
+ CreatePBOs((uint)(width * height * 4), pboIds);
+ }
}
public override void Blit(IntPtr hdc)
{
if (deviceContextHandle != IntPtr.Zero)
{
- // Set the read buffer.
- gl.ReadBuffer(OpenGL.GL_COLOR_ATTACHMENT0_EXT);
-
- // Read the pixels into the DIB section.
- gl.ReadPixels(0, 0, Width, Height, OpenGL.GL_BGRA,
- OpenGL.GL_UNSIGNED_BYTE, dibSection.Bits);
-
- // Blit the DC (containing the DIB section) to the target DC.
- Win32.BitBlt(hdc, 0, 0, Width, Height,
- dibSectionDeviceContext, 0, 0, Win32.SRCCOPY);
+ if(!usePBO)
+ {
+ // Set the read buffer.
+ gl.ReadBuffer(OpenGL.GL_COLOR_ATTACHMENT0_EXT);
+
+ // Read the pixels into the DIB section.
+ gl.ReadPixels(0, 0, Width, Height, OpenGL.GL_BGRA,
+ OpenGL.GL_UNSIGNED_BYTE, dibSection.Bits);
+
+ // Blit the DC (containing the DIB section) to the target DC.
+ Win32.BitBlt(hdc, 0, 0, Width, Height,
+ dibSectionDeviceContext, 0, 0, Win32.SRCCOPY);
+ }
+ else
+ {
+ uint pbo_count = (uint)PBO_COUNT;
+ current_pbo_index = (current_pbo_index + 1) % pbo_count;
+ uint nextIndex = (current_pbo_index + pbo_count - 1) % pbo_count;
+ // Set the read buffer.
+ gl.ReadBuffer(OpenGL.GL_COLOR_ATTACHMENT0_EXT);
+
+ gl.BindBufferARB(OpenGL.GL_PIXEL_PACK_BUFFER_ARB, pboIds[current_pbo_index]);
+ gl.ReadPixels(0, 0, Width, Height, OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE, null);
+
+ // map the PBO that contain framebuffer pixels before processing it
+ gl.BindBufferARB(OpenGL.GL_PIXEL_PACK_BUFFER_ARB, pboIds[nextIndex]);
+ IntPtr src = gl.MapBufferARB(OpenGL.GL_PIXEL_PACK_BUFFER_ARB, OpenGL.GL_READ_ONLY_ARB);
+
+ CopyMemory(dibSection.Bits, src, (uint)(Width*Height*4));
+
+ // Blit the DC (containing the DIB section) to the target DC.
+ Win32.BitBlt(hdc, 0, 0, Width, Height,
+ dibSectionDeviceContext, 0, 0, Win32.SRCCOPY);
+
+ gl.UnmapBufferARB(OpenGL.GL_PIXEL_PACK_BUFFER_ARB);
+ }
}
}
@@ -167,6 +226,12 @@ public override void Blit(IntPtr hdc)
protected DIBSection dibSection = new DIBSection();
protected OpenGL gl;
+ protected bool usePBO = false;
+ protected int PBO_COUNT = 2;
+ protected uint[] pboIds;
+ //protected
+ protected uint current_pbo_index = 0;
+
///
/// Gets the internal DIB section.
///