Forum

Windows GL Multisample Tutorial(ish)

Discuss programming topics for the various GPL'd game engine sources.

Moderator: InsideQC Admins

Windows GL Multisample Tutorial(ish)

Postby Baker » Tue May 21, 2013 4:30 am

Quick and dirty "mostly a tutorial" on implementing multisample in Windows/OpenGL.

Step 1: Use the "Starting Quake Dialog". Multisample requires restarting the OpenGL window (and doing a DestroyWindow too, to the best of my knowledge). This is annoying and causes the screen to flash. Since I do not want that, I have the "Starting Quake" window create a GL context and check for multisample there.

sys_win.c
Code: Select all
#if 1 //johnfitz -- 0 to supress the 'starting quake' dialog
   if (!isDedicated)
   {
      wplat.hwnd_dialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, NULL);

      if (wplat.hwnd_dialog)
      {
         RECT rect;
         GetWindowRect (wplat.hwnd_dialog, &rect);
         WIN_AdjustRectToCenterScreen (&rect);
         SetWindowPos
         (
            wplat.hwnd_dialog,
            NULL,
            rect.left,
            rect.top,
            0,
            0,
            SWP_NOZORDER | SWP_NOSIZE
         );
         ShowWindow (wplat.hwnd_dialog, SW_SHOWDEFAULT);
         UpdateWindow (wplat.hwnd_dialog);
         SetForegroundWindow (wplat.hwnd_dialog);
      }
   }
#endif


Step 2: Kill multiple bitsperpixel support in your engine and instead ALWAYS use the desktop resolution. This is going to be 32 bits per pixel for 99.999999% and it won't the 5 people that use 16 bits per pixel.

Grabbing desktop bpp =
Code: Select all
vmode_t VID_Local_GetDesktopProperties (void)
{
   DEVMODE   devmode;
   vmode_t desktop = {0};

   if (!EnumDisplaySettings (NULL, ENUM_CURRENT_SETTINGS, &devmode))
   {
      Sys_Error ("VID_UpdateDesktopProperties: EnumDisplaySettings failed\n");
      return desktop;
   }

   desktop.type      =   MODE_FULLSCREEN;
   desktop.width      =   devmode.dmPelsWidth;
   desktop.height      =   devmode.dmPelsHeight;
   desktop.bpp         =   devmode.dmBitsPerPel;
   
   return desktop;
}


Step 3: When you are closing the starting Quake dialog, take the time to get available pixel format info and check wgl extensions and poke around for multisample. If you find it, you'll get an "int" value of the pixel format you want, which you will use instead of querying the pfd.

Code: Select all
   if (wplat.hwnd_dialog)
   {
      if (vid_multisample.value)
      {
         // Poke into it for the PFD
         HDC   hdc         = GetDC(wplat.hwnd_dialog);
         int unused      = WIN_SetupPixelFormat (hdc);
         HGLRC wglHRC   = wglCreateContext( hdc );
         HDC wglHDC       = wglGetCurrentDC();
         int unused2      = wglMakeCurrent( hdc, wglHRC);
         int ask_samples   = (int)vid_multisample.value;

         if (ask_samples != 2 && ask_samples != 4 && ask_samples != 8)
         {
            Con_Warning ("Multisamples requested \"%i\" is invalid, trying 4\n", ask_samples);
            ask_samples = 4;
         }

         // Do it.  We already have desktop properties
         wplat.multisamples = WIN_InitMultisample (wplat.hInstance, wplat.hwnd_dialog, wplat.pfd, ask_samples, &wplat.forcePixelFormat);

         // Your mission is complete.  You may leave now ...      
         wglMakeCurrent(NULL, NULL);
         wglDeleteContext(wglHRC);
         ReleaseDC(wplat.hwnd_dialog, wglHDC);
         ReleaseDC(wplat.hwnd_dialog, hdc);

         if (wplat.multisamples)
            Con_Printf ("Multisample x %i Enabled (Requested %i, Received %i).\n", wplat.multisamples, ask_samples, wplat.multisamples);
         else Con_Warning ("Multisample: Requested but not available.\n");
      } else Con_Printf ("Note: Multisample not requested\n");

      // Post teardown
      DestroyWindow (wplat.hwnd_dialog);
      wplat.hwnd_dialog = NULL;
   }


4. My modified PFD function looks like this:

Code: Select all
BOOL WIN_SetupPixelFormat (HDC hDC)
{
    static PIXELFORMATDESCRIPTOR pfd = {
   sizeof(PIXELFORMATDESCRIPTOR),   // size of this pfd
   1,                  // version number
   PFD_DRAW_TO_WINDOW |   // support window
   PFD_SUPPORT_OPENGL |   // support OpenGL
   PFD_DOUBLEBUFFER,      // double buffered
   PFD_TYPE_RGBA,         // RGBA type
   24,                  // 24-bit color depth
   0, 0, 0, 0, 0, 0,      // color bits ignored
   0,                  // no alpha buffer
   0,                  // shift bit ignored
   0,                  // no accumulation buffer
   0, 0, 0, 0,          // accum bits ignored
   32,                  // 32-bit z-buffer
   8,                  // 8-bit stencil buffer
   0,                  // no auxiliary buffer
   PFD_MAIN_PLANE,         // main layer
   0,                  // reserved
   0, 0, 0               // layer masks ignored
    };
    int pixelformat;
   PIXELFORMATDESCRIPTOR  test; //johnfitz

#ifdef SUPPORTS_MULTISAMPLE // Baker change
   if (!wplat.multisamples)
   {
#endif // Baker change + #ifdef SUPPORTS_MULTISAMPLE // Baker change
      if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 )
      {
         Sys_Error ("Video: ChoosePixelFormat failed");
         return FALSE;
      }
#ifdef SUPPORTS_MULTISAMPLE // Baker change
   } else pixelformat = wplat.forcePixelFormat; // Multisample overrride
#endif // Baker change + #ifdef SUPPORTS_MULTISAMPLE // Baker change

   DescribePixelFormat(hDC, pixelformat, sizeof(PIXELFORMATDESCRIPTOR), &test);

    if (SetPixelFormat(hDC, pixelformat, &pfd) == FALSE)
    {
        Sys_Error ("SetPixelFormat failed");
        return FALSE;
    }

#ifdef SUPPORTS_MULTISAMPLE // Baker change
   memcpy (&wplat.pfd, &pfd, sizeof(pfd) );
#endif // Baker change + #ifdef SUPPORTS_MULTISAMPLE // Baker change
    return TRUE;
}


5. The function to check for multisample availability, mostly from NeHe and modified some while looking at FTE's implementation.

Code: Select all
#include "vid_wglext.h"      //WGL extensions

int   arbMultisampleSupported   = false;
int      arbMultisampleFormat   = 0;

// WGLisExtensionSupported: This Is A Form Of The Extension For WGL
int WGLisExtensionSupported(const char *extension)
{
   const size_t extlen = strlen(extension);
   const char *supported = NULL;
   const char* p;

   // Try To Use wglGetExtensionStringARB On Current DC, If Possible
   PROC wglGetExtString = wglGetProcAddress("wglGetExtensionsStringARB");

   if (wglGetExtString)
      supported = ((char*(__stdcall*)(HDC))wglGetExtString)(wglGetCurrentDC());

   // If That Failed, Try Standard Opengl Extensions String
   if (supported == NULL)
      supported = (char*)glGetString(GL_EXTENSIONS);

   // If That Failed Too, Must Be No Extensions Supported
   if (supported == NULL)
      return false;

   // Begin Examination At Start Of String, Increment By 1 On False Match
   for (p = supported; ; p++)
   {
      // Advance p Up To The Next Possible Match
      p = strstr(p, extension);

      if (p == NULL)
         return false;                                             // No Match

      // Make Sure That Match Is At The Start Of The String Or That
      // The Previous Char Is A Space, Or Else We Could Accidentally
      // Match "wglFunkywglExtension" With "wglExtension"

      // Also, Make Sure That The Following Character Is Space Or NULL
      // Or Else "wglExtensionTwo" Might Match "wglExtension"
      if ((p==supported || p[-1]==' ') && (p[extlen]=='\0' || p[extlen]==' '))
         return true;                                             // Match
   }
}

// InitMultisample: Used To Query The Multisample Frequencies
int WIN_InitMultisample (HINSTANCE hInstance,HWND hWnd,PIXELFORMATDESCRIPTOR pfd, int ask_samples, int* pixelForceFormat)

    // See If The String Exists In WGL!
   if (!WGLisExtensionSupported("WGL_ARB_multisample"))
   {
      arbMultisampleSupported=false;
      return false;
   }

   {      
      // Get Our Pixel Format
      PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");   
      if (!wglChoosePixelFormatARB)
      {
         arbMultisampleSupported=false;
         return false;
      }


      {
         // Get Our Current Device Context
         HDC hDC = GetDC(hWnd);

         int      pixelFormat;
         int      valid;
         UINT   numFormats;
         float   fAttributes[] = {0,0};

         // These Attributes Are The Bits We Want To Test For In Our Sample
         // Everything Is Pretty Standard, The Only One We Want To
         // Really Focus On Is The SAMPLE BUFFERS ARB And WGL SAMPLES
         // These Two Are Going To Do The Main Testing For Whether Or Not
         // We Support Multisampling On This Hardware.
         int iAttributes[] =
         {
            WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,
            WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
            WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
            WGL_COLOR_BITS_ARB, 24 /*currentbpp? Nah */, // Baker: Mirror current bpp color depth?
            WGL_ALPHA_BITS_ARB,8,
            WGL_DEPTH_BITS_ARB,16,
            WGL_STENCIL_BITS_ARB,8, // Baker: Stencil bits
            WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
            WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
            WGL_SAMPLES_ARB, ask_samples /*multisample bits*/,
            0,0
         };

         
         while (ask_samples == 8 || ask_samples == 4 || ask_samples == 2)
         {
            iAttributes[19] = ask_samples;

            // First We Check To See If We Can Get A Pixel Format For 4 Samples
            valid = wglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelFormat,&numFormats);

            // If We Returned True, And Our Format Count Is Greater Than 1
            if (valid && numFormats >= 1)
            {            
               *pixelForceFormat = arbMultisampleFormat = pixelFormat;
               return (arbMultisampleSupported = ask_samples);
            }

            ask_samples >>= 1; // Divide by 2
         }
          
         // Return Fail
         return  (arbMultisampleSupported = 0);
      }
   }
}


Multisample gets rid of the "jaggies" but with some performance cost. Barnes pointed out in another thread there are better and more refined versions to do this that emerged, these look like they require shaders and for now the moment I'm sticking with the OpenGL 1.x level feature-set.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Windows GL Multisample Tutorial(ish)

Postby Barnes » Tue May 21, 2013 5:45 am

Very short code my multisample block too large.
You can easy add nvidia CSAA modes (gf 8x +)

Nvidia Coverange AA

Samples # of Color/Z/Stencil # of Coverage Samples
8x 4 8
8xQ (Quality) 8 8
16x 4 16
16xQ (Quality) 8 16

Code: Select all
      gl_state.wgl_nv_multisample_coverage = false;
      if (strstr(glw_state.wglExtsString, "WGL_NV_multisample_coverage")) {
      
      if(r_arbSamples->value < 2){
      Com_Printf(""S_COLOR_YELLOW"...ignoring WGL_NV_multisample_coverage\n");
      gl_state.wgl_nv_multisample_coverage = false;
      gl_state.wgl_nv_multisample_coverage_aviable = true;
      }else{
      Com_Printf("...using WGL_NV_multisample_coverage\n");
      gl_state.wgl_nv_multisample_coverage = true;
      gl_state.wgl_nv_multisample_coverage_aviable = true;
      
      if(r_arbSamples->value >=16){ // clamp regular msaa 16x value to csaa 16q
      Cvar_SetValue("r_arbSamples", 8);
      Cvar_SetValue("r_nvSamplesCoverange", 16);
      }


Code: Select all
   iAttributes[0] = WGL_DOUBLE_BUFFER_ARB;
      iAttributes[1] = TRUE;
      
      if(gl_state.wgl_nv_multisample_coverage){
      
      iAttributes[2] = WGL_COLOR_SAMPLES_NV;
      iAttributes[3] = (int)r_arbSamples->value;
      
      }else{
      
      iAttributes[2] = WGL_COLOR_BITS_ARB;
      iAttributes[3] = 32;
      }

      iAttributes[4] = WGL_DEPTH_BITS_ARB;
      iAttributes[5] = 24;

      iAttributes[6] = WGL_ALPHA_BITS_ARB;
      iAttributes[7] = 8;

      iAttributes[8] = WGL_STENCIL_BITS_ARB;
      iAttributes[9] = 8;
      
      iAttributes[10] = arbMultisampleSupported ? WGL_SAMPLE_BUFFERS_ARB : 0;
      iAttributes[11] = arbMultisampleSupported ? TRUE : 0;
      
      if(gl_state.wgl_nv_multisample_coverage){
                     
      iAttributes[12] = arbMultisampleSupported ? WGL_COVERAGE_SAMPLES_NV : 0;
      iAttributes[13] = arbMultisampleSupported ? (int)r_nvSamplesCoverange->value : 0;
      }
      else
      {
      iAttributes[12] = arbMultisampleSupported ? WGL_SAMPLES_ARB : 0;
      iAttributes[13] = arbMultisampleSupported ? (int)r_arbSamples->value : 0;
      }
      iAttributes[14] = 0;
      iAttributes[15] = 0;
User avatar
Barnes
 
Posts: 226
Joined: Thu Dec 24, 2009 2:26 pm
Location: Russia, Moscow

Re: Windows GL Multisample Tutorial(ish)

Postby Baker » Tue May 21, 2013 6:46 am

Barnes wrote:Very short code my multisample block too large.
You can easy add nvidia CSAA modes (gf 8x +)


Ah! Very nice. Unsurprisingly, my ATI card doesn't have that NVidia wgl extension so I can't test at least not on this machine.

But very nice code.

/My code, yeah I revised it to be small. I don't trust large code = too hard to supervise. I try to get code to be compact and "modular" (i.e. I can identify it, add it or remove it easily.)
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am


Return to Engine Programming

Who is online

Users browsing this forum: No registered users and 1 guest