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. ///