Multisampling

Jun 8, 2012 at 8:15 PM

I've been trying to get FSAA (aka multisampling) to work with SharpGL.  However, GL.Enable(OpenGL.GL_MULTISAMPLE) has no effect.

On looking at the SharpGL code, I can't see the call to wglChoosePixelFormatARB that would get a pixel format that actually supports FSAA.  Thus, does this mean that SharpGL does not support multisampling?

Help would be very much appreciated here, so thanks in advance :).  I've just written a full application using SharpGL, and if I can't get multisampling working then I'm going to have to switch to another framework => lots of work...

Jun 10, 2012 at 9:11 PM

Hi Dave,

It may help if I tell you exactly where I've got to.  I've altered the hidden window so that it performs two passes: a first pass to set a "basic" non-multisampling pixel format, and a second pass to attempt to set a multisampling pixel format.  My code is below, although it is not yet "production quality" - the constants need to be moved elsewhere, for instance.  I've also not passed all of my changes through to you here, since some are in other files - there aren't very many other changes, but I can pass them along if you want.  In addition, please note that I've disabled the second pass below (MSM HACK) as it isn't working yet. 

I've done the same double-pass thing in C++ before, but without the FBO approach you need to use for WPF here.  Thus, my suspicion is that the blitting is the issue.  (I've attempted to use gDEBugger on this, so as to see which buffers are OK, but I've not been able to get it to work with SharpGL on my machine.)  Of course, there is always the possibility that I've made a very basic mistake :).

Cheers,

Mark

P.S. Thanks very much for SharpGL - it does make OpenGL in WPF very easy :).

using System;
 
namespace SharpGL.RenderContextProviders
{
    public class HiddenWindowRenderContextProvider : RenderContextProvider
    {
        #region Constants
 
        private const int GL_TRUE = 1;
        private const int WGL_DRAW_TO_WINDOW_ARB = 0x2001;
        private const int WGL_SUPPORT_OPENGL_ARB = 0x2010;
        private const int WGL_ACCELERATION_ARB = 0x2003;
        private const int WGL_FULL_ACCELERATION_ARB = 0x2027;
        private const int WGL_ACCUM_BITS_ARB = 0x201D;
        private const int WGL_COLOR_BITS_ARB = 0x2014;
        private const int WGL_ALPHA_BITS_ARB = 0x201B;
        private const int WGL_DEPTH_BITS_ARB = 0x2022;
        private const int WGL_STENCIL_BITS_ARB = 0x2023;
        private const int WGL_DOUBLE_BUFFER_ARB = 0x2011;
        private const int WGL_SAMPLE_BUFFERS_ARB = 0x2041;
        private const int WGL_SAMPLES_ARB = 0x2042;
 
        #endregion Constants
 
        #region Fields
 
        private int fARBMultisampleFormat = 0;
 
        #endregion Fields
 
        #region Public Methods
 
        /// <summary>
        /// Initializes a new instance of the <see cref="HiddenWindowRenderContextProvider"/> class.
        /// </summary>
        public HiddenWindowRenderContextProvider()
        {
            //  We can layer GDI drawing on top of open gl drawing.
            GDIDrawingEnabled = true;
        }
 
        /// <summary>
        /// Creates the render context provider. Must also create the OpenGL extensions.
        /// </summary>
        /// <param name="gl">The OpenGL context.</param>
        /// <param name="widthLocal">The width.</param>
        /// <param name="heightLocal">The height.</param>
        /// <param name="bitDepthLocal">The bit depth.</param>
        /// <param name="parameter">The parameter</param>
        /// <returns></returns>
        public override bool Create(OpenGL gl, int widthLocal, int heightLocal, int bitDepthLocal, object parameter)
        {
            // Call the base.
            base.Create(gl, widthLocal, heightLocal, bitDepthLocal, parameter);
 
            // Create a new window class, as basic as possible.                
            fWindowHandle = CreateBasicWindowClass(widthLocal, heightLocal);
 
            // Get the window device context.
            fDeviceContextHandle = Win32.GetDC(fWindowHandle);
 
            // Get an initial basic pixel format descriptor.
            Win32.PIXELFORMATDESCRIPTOR basicPfd = GetBasicPixelFormatDescriptor(bitDepthLocal);
 
            // Match an appropriate pixel format.
            int basicPixelFormat;
            if (!gl.IsMultisamplingSupported)
            {
                if ((basicPixelFormat = Win32.ChoosePixelFormat(fDeviceContextHandle, basicPfd)) == 0)
                    return false;
            }
            else
            {
                basicPixelFormat = fARBMultisampleFormat;
            }
 
            // Set the pixel format
            if (Win32.SetPixelFormat(fDeviceContextHandle, basicPixelFormat, basicPfd) == 0)
            {
                return false;
            }
 
            // Now that we have a basic pixel format we can create a render context.
            fRenderContextHandle = Win32.wglCreateContext(fDeviceContextHandle);
 
            // Make the context current.
            MakeCurrent();
 
            // If this is the second (multisampling) call, then we are done.
            // MSM HACK: Always exit on the first pass right now, since setting a multisampling pixel format
            // does not work: it gives a strange flickering image.
  //          if (fMultisamplingIsSupported)
                return true;
 
            int[] pixelFormat1Array = new int[1];
            uint[] numFormats1Array = new uint[1];
            float[] floatAttributes = new float[] {0,0};
            var integerAttributes = GetMultisamplingIntegerAttributes(bitDepthLocal);
 
            // First try a pixel format for 4 samples.
            bool isValid = gl.ChoosePixelFormatARB(fDeviceContextHandle, integerAttributes, floatAttributes, 1, pixelFormat1Array, numFormats1Array);
            fARBMultisampleFormat = pixelFormat1Array[0];
            uint numFormats = numFormats1Array[0];
 
            // Have we succeeded for 4 samples?
            if (isValid && numFormats >= 1)
            {
                gl.IsMultisamplingSupported = true;
            }
            else
            {
                // Our pixel format with 4 samples failed: test for 2 samples.
                integerAttributes[19] = 2;
                isValid = gl.ChoosePixelFormatARB(fDeviceContextHandle, integerAttributes, floatAttributes, 1, pixelFormat1Array, numFormats1Array);
                fARBMultisampleFormat = pixelFormat1Array[0];
                numFormats = numFormats1Array[0];
                if (isValid && numFormats >= 1)
                {
                    gl.IsMultisamplingSupported = true;
                }
            }
 
            if (gl.IsMultisamplingSupported)
            {
                // Destroy the window we created earlier, and the associated rendering contexts.
                Win32.wglMakeCurrent(fDeviceContextHandle, IntPtr.Zero);				// Set The Current Active Rendering Context To Zero
				Win32.wglDeleteContext (fRenderContextHandle);							// Release The Rendering Context
				fRenderContextHandle = IntPtr.Zero;										// Zero The Rendering Context
                Win32.ReleaseDC(fWindowHandle, fDeviceContextHandle);					// Release The Device Context
			    fDeviceContextHandle = IntPtr.Zero;										// Zero The Device Context
                fWindowHandle = IntPtr.Zero;
 
                // Make the call again, with the multisampling pixel format.
                return Create(gl, widthLocal, heightLocal, bitDepthLocal, parameter);
            }
 
            // Return success.
            return true;
        }
 
        /// <summary>
        /// Get the integer attribute arguments to pass to ChoosePixelFormatARB for
        /// a multisampling pixel format.
        /// </summary>
        private static int[] GetMultisamplingIntegerAttributes(int bitDepthLocal)
        {
            int[] iAttributes = new int[]
                                    {
                                        WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
                                        WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
                                        WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
                                        WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
                                        WGL_ACCUM_BITS_ARB, 64,
                                        WGL_COLOR_BITS_ARB, 32,
                                        WGL_ALPHA_BITS_ARB, 0,
                                        WGL_DEPTH_BITS_ARB, 24,
                                        WGL_STENCIL_BITS_ARB, 8,
                                        WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
                                        WGL_SAMPLES_ARB, 4, // Check For 4x Multisampling
                                        0, 0
                                    };
            return iAttributes;
        }
 
        /// <summary>
        /// Get a non-multisampling pixel format descriptor
        /// </summary>
        private static Win32.PIXELFORMATDESCRIPTOR GetBasicPixelFormatDescriptor(int bitDepthLocal)
        {
            Win32.PIXELFORMATDESCRIPTOR pfd = new Win32.PIXELFORMATDESCRIPTOR();
            pfd.Init(); // To set nSize;
            pfd.nVersion = 1;
            pfd.dwFlags = Win32.PFD_DRAW_TO_WINDOW | Win32.PFD_SUPPORT_OPENGL | Win32.PFD_DOUBLEBUFFER;
            pfd.iPixelType = Win32.PFD_TYPE_RGBA;
 
            pfd.cAccumBits = 64;
            pfd.cColorBits = 32;
            pfd.cAlphaBits = 0;
            pfd.cDepthBits = 24;
            pfd.cStencilBits = 8;
 
			pfd.cRedBits = 0;
            pfd.cRedShift = 0;
            pfd.cGreenBits = 0;
            pfd.cGreenShift = 0;
            pfd.cBlueBits = 0;
            pfd.cBlueShift = 0;
			pfd.cAlphaShift = 0;
            pfd.cAccumRedBits = 0;
            pfd.cAccumGreenBits = 0;
            pfd.cAccumBlueBits = 0;
            pfd.cAccumAlphaBits = 0;
            pfd.cAuxBuffers = 0;
            pfd.iLayerType = Win32.PFD_MAIN_PLANE;
            pfd.bReserved = 0;
            pfd.dwLayerMask = 0;
            pfd.dwVisibleMask = 0;
            pfd.dwDamageMask = 0;
 
            return pfd;
        }
 
        /// <summary>
        /// Destroys the render context provider instance.
        /// </summary>
        public override void Destroy()
        {
            //	Release the device context.
            Win32.ReleaseDC(fWindowHandle, fDeviceContextHandle);
 
            //	Destroy the window.
            Win32.DestroyWindow(fWindowHandle);
 
            //	Call the base, which will delete the render context handle.
            base.Destroy();
        }
 
        /// <summary>
        /// Sets the dimensions of the render context provider.
        /// </summary>
        /// <param name="widthLocal">Width.</param>
        /// <param name="heightLocal">Height.</param>
        public override void SetDimensions(int widthLocal, int heightLocal)
        {
            //  Call the base.
            base.SetDimensions(widthLocal, heightLocal);
 
            //	Set the window size.
            Win32.SetWindowPos(fWindowHandle, IntPtr.Zero, 0, 0, Width, Height,
                               Win32.SetWindowPosFlags.SWP_NOACTIVATE |
                               Win32.SetWindowPosFlags.SWP_NOCOPYBITS |
                               Win32.SetWindowPosFlags.SWP_NOMOVE |
                               Win32.SetWindowPosFlags.SWP_NOOWNERZORDER);
        }
 
        /// <summary>
        /// Blit the rendered data to the supplied device context.
        /// </summary>
        /// <param name="hdc">The HDC.</param>
        public override void Blit(IntPtr hdc)
        {
            if (fDeviceContextHandle != IntPtr.Zero || fWindowHandle != IntPtr.Zero)
            {
                //	Swap the buffers.
                Win32.SwapBuffers(fDeviceContextHandle);
 
                //  Get the HDC for the graphics object.
                Win32.BitBlt(hdc, 0, 0, Width, Height, fDeviceContextHandle, 0, 0, Win32.SRCCOPY);
            }
        }
 
        /// <summary>
        /// Makes the render context current.
        /// </summary>
        public override void MakeCurrent()
        {
            if (fRenderContextHandle != IntPtr.Zero)
                Win32.wglMakeCurrent(fDeviceContextHandle, fRenderContextHandle);
        }
 
        #endregion Public Methods
 
        #region Private Support
 
        /// <summary>
        /// Create a basic window for the purposes of setting a pixel format.
        /// </summary>
        /// <returns>The basic window created.</returns>
        private IntPtr CreateBasicWindowClass(int widthLocal, int heightLocal)
        {
            Win32.WNDCLASSEX wndClass = new Win32.WNDCLASSEX();
            wndClass.Init();
            wndClass.style = Win32.ClassStyles.HorizontalRedraw | Win32.ClassStyles.VerticalRedraw | Win32.ClassStyles.OwnDC;
            wndClass.lpfnWndProc = wndProcDelegate;
            wndClass.cbClsExtra = 0;
            wndClass.cbWndExtra = 0;
            wndClass.hInstance = IntPtr.Zero;
            wndClass.hIcon = IntPtr.Zero;
            wndClass.hCursor = IntPtr.Zero;
            wndClass.hbrBackground = IntPtr.Zero;
            wndClass.lpszMenuName = null;
            wndClass.lpszClassName = "SharpGLRenderWindow";
            wndClass.hIconSm = IntPtr.Zero;
            Win32.RegisterClassEx(ref wndClass);
 
            //	Create the window. Position and size it.
            return Win32.CreateWindowEx(0,
                                        "SharpGLRenderWindow",
                                        "",
                                        Win32.WindowStyles.WS_CLIPCHILDREN | Win32.WindowStyles.WS_CLIPSIBLINGS | Win32.WindowStyles.WS_POPUP,
                                        0, 0, widthLocal, heightLocal,
                                        IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
        }
 
        private static Win32.WndProc wndProcDelegate = new Win32.WndProc(WndProc);
 
        private static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
        {
            return Win32.DefWindowProc(hWnd, msg, wParam, lParam);
        }

       #endregion Private Support

         /// <summary>         /// The window handle.         /// </summary>         protected IntPtr fWindowHandle = IntPtr.Zero;             } }

 

Coordinator
Jun 11, 2012 at 8:35 AM

Hi Mark,

Sorry about the lack of feedback on this - I've just moved to another job in another country so things have been rather hectic - have you got a sample application or something that you can send over to me so I can take a look at the issue specifically? If SGL doesn't support this then it MUST - I'll make whatever changes are required to SGL to support this - you can send source to dwmkerr@gmail.com or submit it as a patch - I'm also going to raise a task for this so we can keep track of it, will post the task shortly.

Coordinator
Jun 11, 2012 at 8:37 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Coordinator
Jun 11, 2012 at 8:37 AM

The work item is:

http://sharpgl.codeplex.com/workitem/790

If you'd like you can upload the project as an attachment for it - from now on let's keep all issues related to multisampling in the work item discussion and we'll get it sorted :)

Jun 11, 2012 at 9:56 PM

Hi Dave,

Thanks a lot for getting back to me on this, and no need to apologise in the slightest - I'm a lot further along with using OpenGL in WPF than I would have been without you!  I'll upload my modified SharpGL project to the workitem - please note that I've made a couple of other modifications to it that you may not want however, such as altering the 3d fonts a bit so that I can use them from within a larger display list.  Also, none of my changes are robustly tested yet.

Cheers,

Mark