Windows GL Multisample Tutorial(ish)

Discuss programming topics for the various GPL'd game engine sources.
Post Reply
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Windows GL Multisample Tutorial(ish)

Post by Baker »

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 ..
Barnes
Posts: 232
Joined: Thu Dec 24, 2009 2:26 pm
Location: Russia, Moscow
Contact:

Re: Windows GL Multisample Tutorial(ish)

Post by Barnes »

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;
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Windows GL Multisample Tutorial(ish)

Post by Baker »

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 ..
Post Reply