<commit_before>/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2006-2011 <NAME> <<EMAIL>>

This file is part of Daemon source code.

Daemon source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Daemon source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Daemon source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
// tr_init.c -- functions that are not called every frame
#include "tr_local.h"

	glconfig_t  glConfig;
	glconfig2_t glConfig2;

	glstate_t   glState;

	float       displayAspect = 0.0f;

	static void GfxInfo_f( void );

	cvar_t      *r_glMajorVersion;
	cvar_t      *r_glMinorVersion;
	cvar_t      *r_glCoreProfile;
	cvar_t      *r_glDebugProfile;
	cvar_t      *r_glDebugMode;
	cvar_t      *r_glAllowSoftware;

	cvar_t      *r_flares;
	cvar_t      *r_flareSize;
	cvar_t      *r_flareFade;

	cvar_t      *r_verbose;
	cvar_t      *r_ignore;

	cvar_t      *r_znear;
	cvar_t      *r_zfar;

	cvar_t      *r_smp;
	cvar_t      *r_showSmp;
	cvar_t      *r_skipBackEnd;
	cvar_t      *r_skipLightBuffer;

	cvar_t      *r_measureOverdraw;

	cvar_t      *r_inGameVideo;
	cvar_t      *r_fastsky;
	cvar_t      *r_drawSun;

	cvar_t      *r_lodBias;
	cvar_t      *r_lodScale;
	cvar_t      *r_lodTest;

	cvar_t      *r_norefresh;
	cvar_t      *r_drawentities;
	cvar_t      *r_drawworld;
	cvar_t      *r_drawpolies;
	cvar_t      *r_speeds;
	cvar_t      *r_novis;
	cvar_t      *r_nocull;
	cvar_t      *r_facePlaneCull;
	cvar_t      *r_showcluster;
	cvar_t      *r_nocurves;
	cvar_t      *r_noLightScissors;
	cvar_t      *r_noLightVisCull;
	cvar_t      *r_noInteractionSort;
	cvar_t      *r_dynamicLight;
	cvar_t      *r_staticLight;
	cvar_t      *r_dynamicLightCastShadows;
	cvar_t      *r_precomputedLighting;
	cvar_t      *r_vertexLighting;
	cvar_t      *r_compressDiffuseMaps;
	cvar_t      *r_compressSpecularMaps;
	cvar_t      *r_compressNormalMaps;
	cvar_t      *r_exportTextures;
	cvar_t      *r_heatHaze;
	cvar_t      *r_heatHazeFix;
	cvar_t      *r_noMarksOnTrisurfs;
	cvar_t      *r_recompileShaders;
	cvar_t      *r_lazyShaders;

	cvar_t      *r_ext_compressed_textures;
	cvar_t      *r_ext_occlusion_query;
	cvar_t      *r_ext_texture_non_power_of_two;
	cvar_t      *r_ext_draw_buffers;
	cvar_t      *r_ext_vertex_array_object;
	cvar_t      *r_ext_half_float_pixel;
	cvar_t      *r_ext_texture_float;
	cvar_t      *r_ext_texture_rg;
	cvar_t      *r_ext_stencil_wrap;
	cvar_t      *r_ext_texture_filter_anisotropic;
	cvar_t      *r_ext_stencil_two_side;
	cvar_t      *r_ext_separate_stencil;
	cvar_t      *r_ext_depth_bounds_test;
	cvar_t      *r_ext_framebuffer_object;
	cvar_t      *r_ext_packed_depth_stencil;
	cvar_t      *r_ext_framebuffer_blit;
	cvar_t      *r_extx_framebuffer_mixed_formats;
	cvar_t      *r_ext_generate_mipmap;

	cvar_t      *r_ignoreGLErrors;
	cvar_t      *r_logFile;

	cvar_t      *r_stencilbits;
	cvar_t      *r_depthbits;
	cvar_t      *r_colorbits;
	cvar_t      *r_alphabits;
	cvar_t      *r_ext_multisample;
	cvar_t      *r_stereo;

	cvar_t      *r_drawBuffer;
	cvar_t      *r_shadows;
	cvar_t      *r_softShadows;
	cvar_t      *r_softShadowsPP;
	cvar_t      *r_shadowBlur;

	cvar_t      *r_shadowMapQuality;
	cvar_t      *r_shadowMapSizeUltra;
	cvar_t      *r_shadowMapSizeVeryHigh;
	cvar_t      *r_shadowMapSizeHigh;
	cvar_t      *r_shadowMapSizeMedium;
	cvar_t      *r_shadowMapSizeLow;

	cvar_t      *r_shadowMapSizeSunUltra;
	cvar_t      *r_shadowMapSizeSunVeryHigh;
	cvar_t      *r_shadowMapSizeSunHigh;
	cvar_t      *r_shadowMapSizeSunMedium;
	cvar_t      *r_shadowMapSizeSunLow;

	cvar_t      *r_shadowOffsetFactor;
	cvar_t      *r_shadowOffsetUnits;
	cvar_t      *r_shadowLodBias;
	cvar_t      *r_shadowLodScale;
	cvar_t      *r_noShadowPyramids;
	cvar_t      *r_cullShadowPyramidFaces;
	cvar_t      *r_cullShadowPyramidCurves;
	cvar_t      *r_cullShadowPyramidTriangles;
	cvar_t      *r_debugShadowMaps;
	cvar_t      *r_noShadowFrustums;
	cvar_t      *r_noLightFrustums;
	cvar_t      *r_shadowMapLinearFilter;
	cvar_t      *r_lightBleedReduction;
	cvar_t      *r_overDarkeningFactor;
	cvar_t      *r_shadowMapDepthScale;
	cvar_t      *r_parallelShadowSplits;
	cvar_t      *r_parallelShadowSplitWeight;

	cvar_t      *r_mode;
	cvar_t      *r_collapseStages;
	cvar_t      *r_nobind;
	cvar_t      *r_singleShader;
	cvar_t      *r_roundImagesDown;
	cvar_t      *r_colorMipLevels;
	cvar_t      *r_picmip;
	cvar_t      *r_finish;
	cvar_t      *r_clear;
	cvar_t      *r_swapInterval;
	cvar_t      *r_textureMode;
	cvar_t      *r_offsetFactor;
	cvar_t      *r_offsetUnits;
	cvar_t      *r_forceSpecular;
	cvar_t      *r_specularExponentMin;
	cvar_t      *r_specularExponentMax;
	cvar_t      *r_specularScale;
	cvar_t      *r_normalScale;
	cvar_t      *r_normalMapping;
	cvar_t      *r_wrapAroundLighting;
	cvar_t      *r_halfLambertLighting;
	cvar_t      *r_rimLighting;
	cvar_t      *r_rimExponent;
	cvar_t      *r_gamma;
	cvar_t      *r_lockpvs;
	cvar_t      *r_noportals;
	cvar_t      *r_portalOnly;
	cvar_t      *r_portalSky;

	cvar_t      *r_subdivisions;
	cvar_t      *r_stitchCurves;

	cvar_t      *r_fullscreen;

	cvar_t      *r_customwidth;
	cvar_t      *r_customheight;
	cvar_t      *r_customaspect;

	cvar_t      *r_debugSurface;
	cvar_t      *r_simpleMipMaps;

	cvar_t      *r_showImages;

	cvar_t      *r_forceFog;
	cvar_t      *r_wolfFog;
	cvar_t      *r_noFog;

	cvar_t      *r_forceAmbient;
	cvar_t      *r_ambientScale;
	cvar_t      *r_lightScale;
	cvar_t      *r_debugLight;
	cvar_t      *r_debugSort;
	cvar_t      *r_printShaders;

	cvar_t      *r_maxPolys;
	cvar_t      *r_maxPolyVerts;

	cvar_t      *r_showTris;
	cvar_t      *r_showSky;
	cvar_t      *r_showShadowVolumes;
	cvar_t      *r_showShadowLod;
	cvar_t      *r_showShadowMaps;
	cvar_t      *r_showSkeleton;
	cvar_t      *r_showEntityTransforms;
	cvar_t      *r_showLightTransforms;
	cvar_t      *r_showLightInteractions;
	cvar_t      *r_showLightScissors;
	cvar_t      *r_showLightBatches;
	cvar_t      *r_showLightGrid;
	cvar_t      *r_showOcclusionQueries;
	cvar_t      *r_showBatches;
	cvar_t      *r_showLightMaps;
	cvar_t      *r_showDeluxeMaps;
	cvar_t      *r_showAreaPortals;
	cvar_t      *r_showCubeProbes;
	cvar_t      *r_showBspNodes;
	cvar_t      *r_showParallelShadowSplits;
	cvar_t      *r_showDecalProjectors;

	cvar_t      *r_vboFaces;
	cvar_t      *r_vboCurves;
	cvar_t      *r_vboTriangles;
	cvar_t      *r_vboShadows;
	cvar_t      *r_vboLighting;
	cvar_t      *r_vboModels;
	cvar_t      *r_vboVertexSkinning;
	cvar_t      *r_vboDeformVertexes;

	cvar_t      *r_mergeLeafSurfaces;
	cvar_t      *r_parallaxMapping;
	cvar_t      *r_parallaxDepthScale;

	cvar_t      *r_dynamicBspOcclusionCulling;
	cvar_t      *r_dynamicEntityOcclusionCulling;
	cvar_t      *r_dynamicLightOcclusionCulling;
	cvar_t      *r_chcMaxPrevInvisNodesBatchSize;
	cvar_t      *r_chcMaxVisibleFrames;
	cvar_t      *r_chcVisibilityThreshold;
	cvar_t      *r_chcIgnoreLeaves;

	cvar_t      *r_reflectionMapping;
	cvar_t      *r_highQualityNormalMapping;
	cvar_t      *r_bloom;
	cvar_t      *r_bloomBlur;
	cvar_t      *r_bloomPasses;
	cvar_t      *r_FXAA;
	cvar_t      *r_cameraVignette;
	cvar_t      *r_cameraFilmGrain;
	cvar_t      *r_cameraFilmGrainScale;

	cvar_t      *r_evsmPostProcess;

	cvar_t      *r_fontScale;

//	glBroken_t  glBroken = {};

	static void AssertCvarRange( cvar_t *cv, float minVal, float maxVal, qboolean shouldBeIntegral )
	{
		if ( shouldBeIntegral )
		{
			if ( ( int ) cv->value != cv->integer )
			{
				ri.Printf( PRINT_WARNING, "WARNING: cvar '%s' must be integral (%f)\n", cv->name, cv->value );
				ri.Cvar_Set( cv->name, va( "%d", cv->integer ) );
			}
		}

		if ( cv->value < minVal )
		{
			ri.Printf( PRINT_WARNING, "WARNING: cvar '%s' out of range (%f < %f)\n", cv->name, cv->value, minVal );
			ri.Cvar_Set( cv->name, va( "%f", minVal ) );
		}
		else if ( cv->value > maxVal )
		{
			ri.Printf( PRINT_WARNING, "WARNING: cvar '%s' out of range (%f > %f)\n", cv->name, cv->value, maxVal );
			ri.Cvar_Set( cv->name, va( "%f", maxVal ) );
		}
	}

	/*
	** InitOpenGL
	**
	** This function is responsible for initializing a valid OpenGL subsystem.  This
	** is done by calling GLimp_Init (which gives us a working OGL subsystem) then
	** setting variables, checking GL constants, and reporting the gfx system config
	** to the user.
	*/
	static qboolean InitOpenGL( void )
	{
		char renderer_buffer[ 1024 ];

		//
		// initialize OS specific portions of the renderer
		//
		// GLimp_Init directly or indirectly references the following cvars:
		//      - r_fullscreen
		//      - r_mode
		//      - r_(color|depth|stencil)bits
		//

		if ( glConfig.vidWidth == 0 )
		{
			GLint temp;

			if ( !GLimp_Init() )
			{
				return qfalse;
			}

			GL_CheckErrors();

			strcpy( renderer_buffer, glConfig.renderer_string );
			Q_strlwr( renderer_buffer );

			// OpenGL driver constants
			glGetIntegerv( GL_MAX_TEXTURE_SIZE, &temp );
			glConfig.maxTextureSize = temp;

			// stubbed or broken drivers may have reported 0...
			if ( glConfig.maxTextureSize <= 0 )
			{
				glConfig.maxTextureSize = 0;
			}

			// handle any OpenGL/GLSL brokenness here...
			// nothing at present

#if defined( GLSL_COMPILE_STARTUP_ONLY )
			GLSL_InitGPUShaders();
#endif
			glConfig.smpActive = qfalse;

			if ( r_smp->integer )
			{
				ri.Printf( PRINT_ALL, "Trying SMP acceleration...\n" );

				if ( GLimp_SpawnRenderThread( RB_RenderThread ) )
				{
					ri.Printf( PRINT_ALL, "...succeeded.\n" );
					glConfig.smpActive = qtrue;
				}
				else
				{
					ri.Printf( PRINT_ALL, "...failed.\n" );
				}
			}
		}

		GL_CheckErrors();

		// set default state
		GL_SetDefaultState();
		GL_CheckErrors();

		return qtrue;
	}

	/*
	==================
	GL_CheckErrors
	==================
	*/
	void GL_CheckErrors_( const char *fileName, int line )
	{
		int  err;
		char s[ 128 ];

		if ( glConfig.smpActive )
		{
			// we can't print onto the console while rendering in another thread
			return;
		}

		if ( r_ignoreGLErrors->integer )
		{
			return;
		}

		err = glGetError();

		if ( err == GL_NO_ERROR )
		{
			return;
		}

		switch ( err )
		{
			case GL_INVALID_ENUM:
				strcpy( s, "GL_INVALID_ENUM" );
				break;

			case GL_INVALID_VALUE:
				strcpy( s, "GL_INVALID_VALUE" );
				break;

			case GL_INVALID_OPERATION:
				strcpy( s, "GL_INVALID_OPERATION" );
				break;

			case GL_STACK_OVERFLOW:
				strcpy( s, "GL_STACK_OVERFLOW" );
				break;

			case GL_STACK_UNDERFLOW:
				strcpy( s, "GL_STACK_UNDERFLOW" );
				break;

			case GL_OUT_OF_MEMORY:
				strcpy( s, "GL_OUT_OF_MEMORY" );
				break;

			case GL_TABLE_TOO_LARGE:
				strcpy( s, "GL_TABLE_TOO_LARGE" );
				break;

			case GL_INVALID_FRAMEBUFFER_OPERATION_EXT:
				strcpy( s, "GL_INVALID_FRAMEBUFFER_OPERATION_EXT" );
				break;

			default:
				Com_sprintf( s, sizeof( s ), "0x%X", err );
				break;
		}

		ri.Error( ERR_FATAL, "caught OpenGL error: %s in file %s line %i", s, fileName, line );
	}

	/*
	** R_GetModeInfo
	*/
	typedef struct vidmode_s
	{
		const char *description;
		int        width, height;
		float      pixelAspect; // pixel width / height
	} vidmode_t;

	static const vidmode_t r_vidModes[] =
	{
		{ " 320x240",           320,  240, 1 },
		{ " 400x300",           400,  300, 1 },
		{ " 512x384",           512,  384, 1 },
		{ " 640x480",           640,  480, 1 },
		{ " 800x600",           800,  600, 1 },
		{ " 960x720",           960,  720, 1 },
		{ "1024x768",          1024,  768, 1 },
		{ "1152x864",          1152,  864, 1 },
		{ "1280x720  (16:9)",  1280,  720, 1 },
		{ "1280x768  (16:10)", 1280,  768, 1 },
		{ "1280x800  (16:10)", 1280,  800, 1 },
		{ "1280x1024",         1280, 1024, 1 },
		{ "1360x768  (16:9)",  1360,  768,  1 },
		{ "1440x900  (16:10)", 1440,  900, 1 },
		{ "1680x1050 (16:10)", 1680, 1050, 1 },
		{ "1600x1200",         1600, 1200, 1 },
		{ "1920x1080 (16:9)",  1920, 1080, 1 },
		{ "1920x1200 (16:10)", 1920, 1200, 1 },
		{ "2048x1536",         2048, 1536, 1 },
		{ "2560x1600 (16:10)", 2560, 1600, 1 },
	};
	static const int s_numVidModes = ARRAY_LEN( r_vidModes );

	qboolean R_GetModeInfo( int *width, int *height, float *windowAspect, int mode )
	{
		const vidmode_t *vm;

		if ( mode < -2 )
		{
			return qfalse;
		}

		if ( mode >= s_numVidModes )
		{
			return qfalse;
		}

		if( mode == -2)
		{
			// Must set width and height to display size before calling this function!
			*windowAspect = ( float ) *width / *height;
		}
		else if ( mode == -1 )
		{
			*width = r_customwidth->integer;
			*height = r_customheight->integer;
			*windowAspect = r_customaspect->value;
		}
		else
		{
			vm = &r_vidModes[ mode ];

			*width = vm->width;
			*height = vm->height;
			*windowAspect = ( float ) vm->width / ( vm->height * vm->pixelAspect );
		}

		return qtrue;
	}

	/*
	** R_ModeList_f
	*/
	static void R_ModeList_f( void )
	{
		int i;

		for ( i = 0; i < s_numVidModes; i++ )
		{
			ri.Printf( PRINT_ALL, "Mode %-2d: %s", i, r_vidModes[ i ].description );
		}
	}

	/*
	==============================================================================

	                                                SCREEN SHOTS

	screenshots get written in fs_homepath + fs_gamedir
	.. base/screenshots\*.*

	three commands: "screenshot", "screenshotJPEG" and "screenshotPNG"

	the format is etxreal-YYYY_MM_DD-HH_MM_SS-MS.tga/jpeg/png

	==============================================================================
	*/

	/*
	==================
	RB_ReadPixels

	Reads an image but takes care of alignment issues for reading RGB images.
	Prepends the specified number of (uninitialized) bytes to the buffer.

	The returned buffer must be freed with ri.Hunk_FreeTempMemory().
	==================
	*/
	static byte *RB_ReadPixels( int x, int y, int width, int height, size_t offset )
	{
		GLint packAlign;
		int   lineLen, paddedLineLen;
		byte  *buffer, *pixels;
		int   i;

		glGetIntegerv( GL_PACK_ALIGNMENT, &packAlign );

		lineLen = width * 3;
		paddedLineLen = PAD( lineLen, packAlign );

		// Allocate a few more bytes so that we can choose an alignment we like
		buffer = ( byte * ) ri.Hunk_AllocateTempMemory( offset + paddedLineLen * height + packAlign - 1 );

		pixels = ( byte * ) PADP( buffer + offset, packAlign );
		glReadPixels( x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels );

		// Drop alignment and line padding bytes
		for ( i = 0; i < height; ++i )
		{
			memmove( buffer + offset + i * lineLen, pixels + i * paddedLineLen, lineLen );
		}

		return buffer;
	}

	/*
	==================
	R_TakeScreenshot
	==================
	*/
	static void RB_TakeScreenshot( int x, int y, int width, int height, char *fileName )
	{
		byte *buffer;
		int  dataSize;
		byte *end, *p;

		// with 18 bytes for the TGA file header
		buffer = RB_ReadPixels( x, y, width, height, 18 );
		Com_Memset( buffer, 0, 18 );

		buffer[ 2 ] = 2; // uncompressed type
		buffer[ 12 ] = width & 255;
		buffer[ 13 ] = width >> 8;
		buffer[ 14 ] = height & 255;
		buffer[ 15 ] = height >> 8;
		buffer[ 16 ] = 24; // pixel size

		dataSize = 3 * width * height;

		// swap RGB to BGR
		end = buffer + 18 + dataSize;

		for ( p = buffer + 18; p < end; p += 3 )
		{
			byte temp = p[ 0 ];
			p[ 0 ] = p[ 2 ];
			p[ 2 ] = temp;
		}

		ri.FS_WriteFile( fileName, buffer, 18 + dataSize );

		ri.Hunk_FreeTempMemory( buffer );
	}

	/*
	==================
	R_TakeScreenshotJPEG
	==================
	*/
	static void RB_TakeScreenshotJPEG( int x, int y, int width, int height, char *fileName )
	{
		byte *buffer = RB_ReadPixels( x, y, width, height, 0 );

		SaveJPG( fileName, 90, width, height, buffer );
		ri.Hunk_FreeTempMemory( buffer );
	}

	/*
	==================
	R_TakeScreenshotPNG
	==================
	*/
	static void RB_TakeScreenshotPNG( int x, int y, int width, int height, char *fileName )
	{
		byte *buffer = RB_ReadPixels( x, y, width, height, 0 );

		SavePNG( fileName, buffer, width, height, 3, qfalse );
		ri.Hunk_FreeTempMemory( buffer );
	}

	/*
	==================
	RB_TakeScreenshotCmd
	==================
	*/
	const void     *RB_TakeScreenshotCmd( const void *data )
	{
		const screenshotCommand_t *cmd;

		cmd = ( const screenshotCommand_t * ) data;

		switch ( cmd->format )
		{
			case SSF_TGA:
				RB_TakeScreenshot( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
				break;

			case SSF_JPEG:
				RB_TakeScreenshotJPEG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
				break;

			case SSF_PNG:
				RB_TakeScreenshotPNG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
				break;
		}

		return ( const void * )( cmd + 1 );
	}

	/*
	==================
	R_TakeScreenshot
	==================
	*/
	void R_TakeScreenshot( const char *name, ssFormat_t format )
	{
		static char         fileName[ MAX_OSPATH ]; // bad things may happen if two screenshots per frame are taken.
		screenshotCommand_t *cmd;
		int                 lastNumber;

		cmd = ( screenshotCommand_t * ) R_GetCommandBuffer( sizeof( *cmd ) );

		if ( !cmd )
		{
			return;
		}

		if ( ri.Cmd_Argc() == 2 )
		{
			Com_sprintf( fileName, sizeof( fileName ), "screenshots/" PRODUCT_NAME_LOWER "-%s.%s", ri.Cmd_Argv( 1 ), name );
		}
		else
		{
			qtime_t t;

			ri.RealTime( &t );

			// scan for a free filename
			for ( lastNumber = 0; lastNumber <= 999; lastNumber++ )
			{
				Com_sprintf( fileName, sizeof( fileName ), "screenshots/" PRODUCT_NAME_LOWER "_%04d-%02d-%02d_%02d%02d%02d_%03d.%s",
				             1900 + t.tm_year, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, lastNumber, name );

				if ( !ri.FS_FileExists( fileName ) )
				{
					break; // file doesn't exist
				}
			}

			if ( lastNumber == 1000 )
			{
				ri.Printf( PRINT_ALL, "ScreenShot: Couldn't create a file\n" );
				return;
			}

			lastNumber++;
		}

		ri.Printf( PRINT_ALL, "Wrote %s\n", fileName );

		cmd->commandId = RC_SCREENSHOT;
		cmd->x = 0;
		cmd->y = 0;
		cmd->width = glConfig.vidWidth;
		cmd->height = glConfig.vidHeight;
		cmd->fileName = fileName;
		cmd->format = format;
	}

	/*
	==================
	R_ScreenShot_f

	screenshot
	screenshot [filename]
	==================
	*/
	static void R_ScreenShot_f( void )
	{
		R_TakeScreenshot( "tga", SSF_TGA );
	}

	static void R_ScreenShotJPEG_f( void )
	{
		R_TakeScreenshot( "jpg", SSF_JPEG );
	}

	static void R_ScreenShotPNG_f( void )
	{
		R_TakeScreenshot( "png", SSF_PNG );
	}

//============================================================================

	/*
	==================
	RB_TakeVideoFrameCmd
	==================
	*/
	const void     *RB_TakeVideoFrameCmd( const void *data )
	{
		const videoFrameCommand_t *cmd;
		GLint                     packAlign;
		int                       lineLen, captureLineLen;
		byte                      *pixels;
		int                       i;
		int                       outputSize;
		int                       j;
		int                       aviLineLen;

		cmd = ( const videoFrameCommand_t * ) data;

		// RB: it is possible to we still have a videoFrameCommand_t but we already stopped
		// video recording
		if ( ri.CL_VideoRecording() )
		{
			// take care of alignment issues for reading RGB images..

			glGetIntegerv( GL_PACK_ALIGNMENT, &packAlign );

			lineLen = cmd->width * 3;
			captureLineLen = PAD( lineLen, packAlign );

			pixels = ( byte * ) PADP( cmd->captureBuffer, packAlign );
			glReadPixels( 0, 0, cmd->width, cmd->height, GL_RGB, GL_UNSIGNED_BYTE, pixels );

			if ( cmd->motionJpeg )
			{
				// Drop alignment and line padding bytes
				for ( i = 0; i < cmd->height; ++i )
				{
					memmove( cmd->captureBuffer + i * lineLen, pixels + i * captureLineLen, lineLen );
				}

				outputSize = SaveJPGToBuffer( cmd->encodeBuffer, 3 * cmd->width * cmd->height, 90, cmd->width, cmd->height, cmd->captureBuffer );
				ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, outputSize );
			}
			else
			{
				aviLineLen = PAD( lineLen, AVI_LINE_PADDING );

				for ( i = 0; i < cmd->height; ++i )
				{
					for ( j = 0; j < lineLen; j += 3 )
					{
						cmd->encodeBuffer[ i * aviLineLen + j + 0 ] = pixels[ i * captureLineLen + j + 2 ];
						cmd->encodeBuffer[ i * aviLineLen + j + 1 ] = pixels[ i * captureLineLen + j + 1 ];
						cmd->encodeBuffer[ i * aviLineLen + j + 2 ] = pixels[ i * captureLineLen + j + 0 ];
					}

					while ( j < aviLineLen )
					{
						cmd->encodeBuffer[ i * aviLineLen + j++ ] = 0;
					}
				}

				ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, aviLineLen * cmd->height );
			}
		}

		return ( const void * )( cmd + 1 );
	}

//============================================================================

	/*
	** GL_SetDefaultState
	*/
	void GL_SetDefaultState( void )
	{
		int i;

		GLimp_LogComment( "--- GL_SetDefaultState ---\n" );

		GL_ClearDepth( 1.0f );

		if ( glConfig.stencilBits >= 4 )
		{
			GL_ClearStencil( 128 );
		}

		GL_FrontFace( GL_CCW );
		GL_CullFace( GL_FRONT );

		glState.faceCulling = CT_TWO_SIDED;
		glDisable( GL_CULL_FACE );

		GL_CheckErrors();

		glVertexAttrib4f( ATTR_INDEX_COLOR, 1, 1, 1, 1 );

		GL_CheckErrors();

		// initialize downstream texture units if we're running
		// in a multitexture environment
		if ( glConfig.driverType == GLDRV_OPENGL3 )
		{
			for ( i = 31; i >= 0; i-- )
			{
				GL_SelectTexture( i );
				GL_TextureMode( r_textureMode->string );
			}
		}
		else
		{
			if ( GLEW_ARB_multitexture )
			{
				for ( i = glConfig.maxActiveTextures - 1; i >= 0; i-- )
				{
					GL_SelectTexture( i );
					GL_TextureMode( r_textureMode->string );
				}
			}
		}

		GL_CheckErrors();

		GL_DepthFunc( GL_LEQUAL );

		// make sure our GL state vector is set correctly
		glState.glStateBits = GLS_DEPTHTEST_DISABLE | GLS_DEPTHMASK_TRUE;
		glState.vertexAttribsState = 0;
		glState.vertexAttribPointersSet = 0;

		GL_BindProgram( NULL );

		glBindBuffer( GL_ARRAY_BUFFER, 0 );
		glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
		glState.currentVBO = NULL;
		glState.currentIBO = NULL;

		GL_CheckErrors();

		// the vertex array is always enabled, but the color and texture
		// arrays are enabled and disabled around the compiled vertex array call
		glEnableVertexAttribArray( ATTR_INDEX_POSITION );

		/*
		   OpenGL 3.0 spec: E.1. PROFILES AND DEPRECATED FEATURES OF OPENGL 3.0 405
		   Calling VertexAttribPointer when no buffer object or no
		   vertex array object is bound will generate an INVALID OPERATION error,
		   as will calling any array drawing command when no vertex array object is
		   bound.
		 */

		if ( glConfig2.framebufferObjectAvailable )
		{
			glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
			glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 );
			glState.currentFBO = NULL;
		}

		GL_PolygonMode( GL_FRONT_AND_BACK, GL_FILL );
		GL_DepthMask( GL_TRUE );
		glDisable( GL_DEPTH_TEST );
		glEnable( GL_SCISSOR_TEST );
		glDisable( GL_BLEND );

		glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
		glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
		glClearDepth( 1.0 );

		glDrawBuffer( GL_BACK );
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );

		GL_CheckErrors();

		glState.stackIndex = 0;

		for ( i = 0; i < MAX_GLSTACK; i++ )
		{
			MatrixIdentity( glState.modelViewMatrix[ i ] );
			MatrixIdentity( glState.projectionMatrix[ i ] );
			MatrixIdentity( glState.modelViewProjectionMatrix[ i ] );
		}
	}

	/*
	================
	GfxInfo_f
	================
	*/
	void GfxInfo_f( void )
	{
		static const char fsstrings[][16] =
		{
			"windowed",
			"fullscreen"
		};

		ri.Printf( PRINT_ALL, "\nGL_VENDOR: %s\n", glConfig.vendor_string );
		ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glConfig.renderer_string );
		ri.Printf( PRINT_ALL, "GL_VERSION: %s\n", glConfig.version_string );
		ri.Printf( PRINT_DEVELOPER, "GL_EXTENSIONS: %s\n", glConfig.extensions_string );
		ri.Printf( PRINT_DEVELOPER, "GL_MAX_TEXTURE_SIZE: %d\n", glConfig.maxTextureSize );

		if ( glConfig.driverType != GLDRV_OPENGL3 )
		{
			ri.Printf( PRINT_DEVELOPER, "GL_MAX_TEXTURE_UNITS: %d\n", glConfig.maxActiveTextures );
		}

		ri.Printf( PRINT_ALL, "GL_SHADING_LANGUAGE_VERSION: %s\n", glConfig2.shadingLanguageVersionString );

		ri.Printf( PRINT_ALL, "GL_MAX_VERTEX_UNIFORM_COMPONENTS %d\n", glConfig2.maxVertexUniforms );
		ri.Printf( PRINT_DEVELOPER, "GL_MAX_VERTEX_ATTRIBS %d\n", glConfig2.maxVertexAttribs );

		if ( glConfig2.occlusionQueryAvailable )
		{
			ri.Printf( PRINT_DEVELOPER, "%d occlusion query bits\n", glConfig2.occlusionQueryBits );
		}

		if ( glConfig2.drawBuffersAvailable )
		{
			ri.Printf( PRINT_DEVELOPER, "GL_MAX_DRAW_BUFFERS: %d\n", glConfig2.maxDrawBuffers );
		}

		if ( glConfig2.textureAnisotropyAvailable )
		{
			ri.Printf( PRINT_DEVELOPER, "GL_TEXTURE_MAX_ANISOTROPY_EXT: %f\n", glConfig2.maxTextureAnisotropy );
		}

		if ( glConfig2.framebufferObjectAvailable )
		{
			ri.Printf( PRINT_DEVELOPER, "GL_MAX_RENDERBUFFER_SIZE_EXT: %d\n", glConfig2.maxRenderbufferSize );
			ri.Printf( PRINT_DEVELOPER, "GL_MAX_COLOR_ATTACHMENTS_EXT: %d\n", glConfig2.maxColorAttachments );
		}

		ri.Printf( PRINT_DEVELOPER, "\nPIXELFORMAT: color(%d-bits) Z(%d-bit) stencil(%d-bits)\n", glConfig.colorBits,
		           glConfig.depthBits, glConfig.stencilBits );
		ri.Printf( PRINT_DEVELOPER, "MODE: %d, %d x %d %s hz:", r_mode->integer, glConfig.vidWidth, glConfig.vidHeight,
		           fsstrings[ r_fullscreen->integer == 1 ] );

		if ( glConfig.displayFrequency )
		{
			ri.Printf( PRINT_DEVELOPER, "%d\n", glConfig.displayFrequency );
		}
		else
		{
			ri.Printf( PRINT_DEVELOPER, "N/A\n" );
		}

		ri.Printf( PRINT_DEVELOPER, "texturemode: %s\n", r_textureMode->string );
		ri.Printf( PRINT_DEVELOPER, "picmip: %d\n", r_picmip->integer );

		if ( glConfig.driverType == GLDRV_OPENGL3 )
		{
			int contextFlags, profile;

			ri.Printf( PRINT_ALL, S_COLOR_GREEN "Using OpenGL 3.x context\n" );

			// check if we have a core-profile
			glGetIntegerv( GL_CONTEXT_PROFILE_MASK, &profile );

			if ( profile == GL_CONTEXT_CORE_PROFILE_BIT )
			{
				ri.Printf( PRINT_DEVELOPER, S_COLOR_GREEN "Having a core profile\n" );
			}
			else
			{
				ri.Printf( PRINT_DEVELOPER, S_COLOR_RED "Having a compatibility profile\n" );
			}

			// check if context is forward compatible
			glGetIntegerv( GL_CONTEXT_FLAGS, &contextFlags );

			if ( contextFlags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT )
			{
				ri.Printf( PRINT_DEVELOPER, S_COLOR_GREEN "Context is forward compatible\n" );
			}
			else
			{
				ri.Printf( PRINT_DEVELOPER, S_COLOR_RED "Context is NOT forward compatible\n" );
			}
		}

		if ( glConfig.hardwareType == GLHW_ATI )
		{
			ri.Printf( PRINT_DEVELOPER, "HACK: ATI approximations\n" );
		}

		if ( glConfig.textureCompression != TC_NONE )
		{
			ri.Printf( PRINT_DEVELOPER, "Using S3TC (DXTC) texture compression\n" );
		}

		if ( glConfig.hardwareType == GLHW_ATI_DX10 )
		{
			ri.Printf( PRINT_DEVELOPER, "Using ATI DirectX 10 hardware features\n" );

			if ( glConfig.driverType == GLDRV_MESA )
			{
				ri.Printf( PRINT_DEVELOPER, "^3Not using GPU vertex skinning: known to be broken with Radeon HD and Mesa\n" );
				glConfig2.vboVertexSkinningAvailable = qfalse;
			}
		}

		if ( glConfig.hardwareType == GLHW_NV_DX10 )
		{
			ri.Printf( PRINT_DEVELOPER, "Using NVIDIA DirectX 10 hardware features\n" );
		}

		if ( glConfig2.vboVertexSkinningAvailable )
		{
			ri.Printf( PRINT_ALL, "Using GPU vertex skinning with max %i bones in a single pass\n", glConfig2.maxVertexSkinningBones );
		}<commit_msg>Allow GPU vertex skinning on Radeon/MESA.<commit_after>/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2006-2011 <NAME> <<EMAIL>>

This file is part of Daemon source code.

Daemon source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Daemon source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Daemon source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
// tr_init.c -- functions that are not called every frame
#include "tr_local.h"

	glconfig_t  glConfig;
	glconfig2_t glConfig2;

	glstate_t   glState;

	float       displayAspect = 0.0f;

	static void GfxInfo_f( void );

	cvar_t      *r_glMajorVersion;
	cvar_t      *r_glMinorVersion;
	cvar_t      *r_glCoreProfile;
	cvar_t      *r_glDebugProfile;
	cvar_t      *r_glDebugMode;
	cvar_t      *r_glAllowSoftware;

	cvar_t      *r_flares;
	cvar_t      *r_flareSize;
	cvar_t      *r_flareFade;

	cvar_t      *r_verbose;
	cvar_t      *r_ignore;

	cvar_t      *r_znear;
	cvar_t      *r_zfar;

	cvar_t      *r_smp;
	cvar_t      *r_showSmp;
	cvar_t      *r_skipBackEnd;
	cvar_t      *r_skipLightBuffer;

	cvar_t      *r_measureOverdraw;

	cvar_t      *r_inGameVideo;
	cvar_t      *r_fastsky;
	cvar_t      *r_drawSun;

	cvar_t      *r_lodBias;
	cvar_t      *r_lodScale;
	cvar_t      *r_lodTest;

	cvar_t      *r_norefresh;
	cvar_t      *r_drawentities;
	cvar_t      *r_drawworld;
	cvar_t      *r_drawpolies;
	cvar_t      *r_speeds;
	cvar_t      *r_novis;
	cvar_t      *r_nocull;
	cvar_t      *r_facePlaneCull;
	cvar_t      *r_showcluster;
	cvar_t      *r_nocurves;
	cvar_t      *r_noLightScissors;
	cvar_t      *r_noLightVisCull;
	cvar_t      *r_noInteractionSort;
	cvar_t      *r_dynamicLight;
	cvar_t      *r_staticLight;
	cvar_t      *r_dynamicLightCastShadows;
	cvar_t      *r_precomputedLighting;
	cvar_t      *r_vertexLighting;
	cvar_t      *r_compressDiffuseMaps;
	cvar_t      *r_compressSpecularMaps;
	cvar_t      *r_compressNormalMaps;
	cvar_t      *r_exportTextures;
	cvar_t      *r_heatHaze;
	cvar_t      *r_heatHazeFix;
	cvar_t      *r_noMarksOnTrisurfs;
	cvar_t      *r_recompileShaders;
	cvar_t      *r_lazyShaders;

	cvar_t      *r_ext_compressed_textures;
	cvar_t      *r_ext_occlusion_query;
	cvar_t      *r_ext_texture_non_power_of_two;
	cvar_t      *r_ext_draw_buffers;
	cvar_t      *r_ext_vertex_array_object;
	cvar_t      *r_ext_half_float_pixel;
	cvar_t      *r_ext_texture_float;
	cvar_t      *r_ext_texture_rg;
	cvar_t      *r_ext_stencil_wrap;
	cvar_t      *r_ext_texture_filter_anisotropic;
	cvar_t      *r_ext_stencil_two_side;
	cvar_t      *r_ext_separate_stencil;
	cvar_t      *r_ext_depth_bounds_test;
	cvar_t      *r_ext_framebuffer_object;
	cvar_t      *r_ext_packed_depth_stencil;
	cvar_t      *r_ext_framebuffer_blit;
	cvar_t      *r_extx_framebuffer_mixed_formats;
	cvar_t      *r_ext_generate_mipmap;

	cvar_t      *r_ignoreGLErrors;
	cvar_t      *r_logFile;

	cvar_t      *r_stencilbits;
	cvar_t      *r_depthbits;
	cvar_t      *r_colorbits;
	cvar_t      *r_alphabits;
	cvar_t      *r_ext_multisample;
	cvar_t      *r_stereo;

	cvar_t      *r_drawBuffer;
	cvar_t      *r_shadows;
	cvar_t      *r_softShadows;
	cvar_t      *r_softShadowsPP;
	cvar_t      *r_shadowBlur;

	cvar_t      *r_shadowMapQuality;
	cvar_t      *r_shadowMapSizeUltra;
	cvar_t      *r_shadowMapSizeVeryHigh;
	cvar_t      *r_shadowMapSizeHigh;
	cvar_t      *r_shadowMapSizeMedium;
	cvar_t      *r_shadowMapSizeLow;

	cvar_t      *r_shadowMapSizeSunUltra;
	cvar_t      *r_shadowMapSizeSunVeryHigh;
	cvar_t      *r_shadowMapSizeSunHigh;
	cvar_t      *r_shadowMapSizeSunMedium;
	cvar_t      *r_shadowMapSizeSunLow;

	cvar_t      *r_shadowOffsetFactor;
	cvar_t      *r_shadowOffsetUnits;
	cvar_t      *r_shadowLodBias;
	cvar_t      *r_shadowLodScale;
	cvar_t      *r_noShadowPyramids;
	cvar_t      *r_cullShadowPyramidFaces;
	cvar_t      *r_cullShadowPyramidCurves;
	cvar_t      *r_cullShadowPyramidTriangles;
	cvar_t      *r_debugShadowMaps;
	cvar_t      *r_noShadowFrustums;
	cvar_t      *r_noLightFrustums;
	cvar_t      *r_shadowMapLinearFilter;
	cvar_t      *r_lightBleedReduction;
	cvar_t      *r_overDarkeningFactor;
	cvar_t      *r_shadowMapDepthScale;
	cvar_t      *r_parallelShadowSplits;
	cvar_t      *r_parallelShadowSplitWeight;

	cvar_t      *r_mode;
	cvar_t      *r_collapseStages;
	cvar_t      *r_nobind;
	cvar_t      *r_singleShader;
	cvar_t      *r_roundImagesDown;
	cvar_t      *r_colorMipLevels;
	cvar_t      *r_picmip;
	cvar_t      *r_finish;
	cvar_t      *r_clear;
	cvar_t      *r_swapInterval;
	cvar_t      *r_textureMode;
	cvar_t      *r_offsetFactor;
	cvar_t      *r_offsetUnits;
	cvar_t      *r_forceSpecular;
	cvar_t      *r_specularExponentMin;
	cvar_t      *r_specularExponentMax;
	cvar_t      *r_specularScale;
	cvar_t      *r_normalScale;
	cvar_t      *r_normalMapping;
	cvar_t      *r_wrapAroundLighting;
	cvar_t      *r_halfLambertLighting;
	cvar_t      *r_rimLighting;
	cvar_t      *r_rimExponent;
	cvar_t      *r_gamma;
	cvar_t      *r_lockpvs;
	cvar_t      *r_noportals;
	cvar_t      *r_portalOnly;
	cvar_t      *r_portalSky;

	cvar_t      *r_subdivisions;
	cvar_t      *r_stitchCurves;

	cvar_t      *r_fullscreen;

	cvar_t      *r_customwidth;
	cvar_t      *r_customheight;
	cvar_t      *r_customaspect;

	cvar_t      *r_debugSurface;
	cvar_t      *r_simpleMipMaps;

	cvar_t      *r_showImages;

	cvar_t      *r_forceFog;
	cvar_t      *r_wolfFog;
	cvar_t      *r_noFog;

	cvar_t      *r_forceAmbient;
	cvar_t      *r_ambientScale;
	cvar_t      *r_lightScale;
	cvar_t      *r_debugLight;
	cvar_t      *r_debugSort;
	cvar_t      *r_printShaders;

	cvar_t      *r_maxPolys;
	cvar_t      *r_maxPolyVerts;

	cvar_t      *r_showTris;
	cvar_t      *r_showSky;
	cvar_t      *r_showShadowVolumes;
	cvar_t      *r_showShadowLod;
	cvar_t      *r_showShadowMaps;
	cvar_t      *r_showSkeleton;
	cvar_t      *r_showEntityTransforms;
	cvar_t      *r_showLightTransforms;
	cvar_t      *r_showLightInteractions;
	cvar_t      *r_showLightScissors;
	cvar_t      *r_showLightBatches;
	cvar_t      *r_showLightGrid;
	cvar_t      *r_showOcclusionQueries;
	cvar_t      *r_showBatches;
	cvar_t      *r_showLightMaps;
	cvar_t      *r_showDeluxeMaps;
	cvar_t      *r_showAreaPortals;
	cvar_t      *r_showCubeProbes;
	cvar_t      *r_showBspNodes;
	cvar_t      *r_showParallelShadowSplits;
	cvar_t      *r_showDecalProjectors;

	cvar_t      *r_vboFaces;
	cvar_t      *r_vboCurves;
	cvar_t      *r_vboTriangles;
	cvar_t      *r_vboShadows;
	cvar_t      *r_vboLighting;
	cvar_t      *r_vboModels;
	cvar_t      *r_vboVertexSkinning;
	cvar_t      *r_vboDeformVertexes;

	cvar_t      *r_mergeLeafSurfaces;
	cvar_t      *r_parallaxMapping;
	cvar_t      *r_parallaxDepthScale;

	cvar_t      *r_dynamicBspOcclusionCulling;
	cvar_t      *r_dynamicEntityOcclusionCulling;
	cvar_t      *r_dynamicLightOcclusionCulling;
	cvar_t      *r_chcMaxPrevInvisNodesBatchSize;
	cvar_t      *r_chcMaxVisibleFrames;
	cvar_t      *r_chcVisibilityThreshold;
	cvar_t      *r_chcIgnoreLeaves;

	cvar_t      *r_reflectionMapping;
	cvar_t      *r_highQualityNormalMapping;
	cvar_t      *r_bloom;
	cvar_t      *r_bloomBlur;
	cvar_t      *r_bloomPasses;
	cvar_t      *r_FXAA;
	cvar_t      *r_cameraVignette;
	cvar_t      *r_cameraFilmGrain;
	cvar_t      *r_cameraFilmGrainScale;

	cvar_t      *r_evsmPostProcess;

	cvar_t      *r_fontScale;

//	glBroken_t  glBroken = {};

	static void AssertCvarRange( cvar_t *cv, float minVal, float maxVal, qboolean shouldBeIntegral )
	{
		if ( shouldBeIntegral )
		{
			if ( ( int ) cv->value != cv->integer )
			{
				ri.Printf( PRINT_WARNING, "WARNING: cvar '%s' must be integral (%f)\n", cv->name, cv->value );
				ri.Cvar_Set( cv->name, va( "%d", cv->integer ) );
			}
		}

		if ( cv->value < minVal )
		{
			ri.Printf( PRINT_WARNING, "WARNING: cvar '%s' out of range (%f < %f)\n", cv->name, cv->value, minVal );
			ri.Cvar_Set( cv->name, va( "%f", minVal ) );
		}
		else if ( cv->value > maxVal )
		{
			ri.Printf( PRINT_WARNING, "WARNING: cvar '%s' out of range (%f > %f)\n", cv->name, cv->value, maxVal );
			ri.Cvar_Set( cv->name, va( "%f", maxVal ) );
		}
	}

	/*
	** InitOpenGL
	**
	** This function is responsible for initializing a valid OpenGL subsystem.  This
	** is done by calling GLimp_Init (which gives us a working OGL subsystem) then
	** setting variables, checking GL constants, and reporting the gfx system config
	** to the user.
	*/
	static qboolean InitOpenGL( void )
	{
		char renderer_buffer[ 1024 ];

		//
		// initialize OS specific portions of the renderer
		//
		// GLimp_Init directly or indirectly references the following cvars:
		//      - r_fullscreen
		//      - r_mode
		//      - r_(color|depth|stencil)bits
		//

		if ( glConfig.vidWidth == 0 )
		{
			GLint temp;

			if ( !GLimp_Init() )
			{
				return qfalse;
			}

			GL_CheckErrors();

			strcpy( renderer_buffer, glConfig.renderer_string );
			Q_strlwr( renderer_buffer );

			// OpenGL driver constants
			glGetIntegerv( GL_MAX_TEXTURE_SIZE, &temp );
			glConfig.maxTextureSize = temp;

			// stubbed or broken drivers may have reported 0...
			if ( glConfig.maxTextureSize <= 0 )
			{
				glConfig.maxTextureSize = 0;
			}

			// handle any OpenGL/GLSL brokenness here...
			// nothing at present

#if defined( GLSL_COMPILE_STARTUP_ONLY )
			GLSL_InitGPUShaders();
#endif
			glConfig.smpActive = qfalse;

			if ( r_smp->integer )
			{
				ri.Printf( PRINT_ALL, "Trying SMP acceleration...\n" );

				if ( GLimp_SpawnRenderThread( RB_RenderThread ) )
				{
					ri.Printf( PRINT_ALL, "...succeeded.\n" );
					glConfig.smpActive = qtrue;
				}
				else
				{
					ri.Printf( PRINT_ALL, "...failed.\n" );
				}
			}
		}

		GL_CheckErrors();

		// set default state
		GL_SetDefaultState();
		GL_CheckErrors();

		return qtrue;
	}

	/*
	==================
	GL_CheckErrors
	==================
	*/
	void GL_CheckErrors_( const char *fileName, int line )
	{
		int  err;
		char s[ 128 ];

		if ( glConfig.smpActive )
		{
			// we can't print onto the console while rendering in another thread
			return;
		}

		if ( r_ignoreGLErrors->integer )
		{
			return;
		}

		err = glGetError();

		if ( err == GL_NO_ERROR )
		{
			return;
		}

		switch ( err )
		{
			case GL_INVALID_ENUM:
				strcpy( s, "GL_INVALID_ENUM" );
				break;

			case GL_INVALID_VALUE:
				strcpy( s, "GL_INVALID_VALUE" );
				break;

			case GL_INVALID_OPERATION:
				strcpy( s, "GL_INVALID_OPERATION" );
				break;

			case GL_STACK_OVERFLOW:
				strcpy( s, "GL_STACK_OVERFLOW" );
				break;

			case GL_STACK_UNDERFLOW:
				strcpy( s, "GL_STACK_UNDERFLOW" );
				break;

			case GL_OUT_OF_MEMORY:
				strcpy( s, "GL_OUT_OF_MEMORY" );
				break;

			case GL_TABLE_TOO_LARGE:
				strcpy( s, "GL_TABLE_TOO_LARGE" );
				break;

			case GL_INVALID_FRAMEBUFFER_OPERATION_EXT:
				strcpy( s, "GL_INVALID_FRAMEBUFFER_OPERATION_EXT" );
				break;

			default:
				Com_sprintf( s, sizeof( s ), "0x%X", err );
				break;
		}

		ri.Error( ERR_FATAL, "caught OpenGL error: %s in file %s line %i", s, fileName, line );
	}

	/*
	** R_GetModeInfo
	*/
	typedef struct vidmode_s
	{
		const char *description;
		int        width, height;
		float      pixelAspect; // pixel width / height
	} vidmode_t;

	static const vidmode_t r_vidModes[] =
	{
		{ " 320x240",           320,  240, 1 },
		{ " 400x300",           400,  300, 1 },
		{ " 512x384",           512,  384, 1 },
		{ " 640x480",           640,  480, 1 },
		{ " 800x600",           800,  600, 1 },
		{ " 960x720",           960,  720, 1 },
		{ "1024x768",          1024,  768, 1 },
		{ "1152x864",          1152,  864, 1 },
		{ "1280x720  (16:9)",  1280,  720, 1 },
		{ "1280x768  (16:10)", 1280,  768, 1 },
		{ "1280x800  (16:10)", 1280,  800, 1 },
		{ "1280x1024",         1280, 1024, 1 },
		{ "1360x768  (16:9)",  1360,  768,  1 },
		{ "1440x900  (16:10)", 1440,  900, 1 },
		{ "1680x1050 (16:10)", 1680, 1050, 1 },
		{ "1600x1200",         1600, 1200, 1 },
		{ "1920x1080 (16:9)",  1920, 1080, 1 },
		{ "1920x1200 (16:10)", 1920, 1200, 1 },
		{ "2048x1536",         2048, 1536, 1 },
		{ "2560x1600 (16:10)", 2560, 1600, 1 },
	};
	static const int s_numVidModes = ARRAY_LEN( r_vidModes );

	qboolean R_GetModeInfo( int *width, int *height, float *windowAspect, int mode )
	{
		const vidmode_t *vm;

		if ( mode < -2 )
		{
			return qfalse;
		}

		if ( mode >= s_numVidModes )
		{
			return qfalse;
		}

		if( mode == -2)
		{
			// Must set width and height to display size before calling this function!
			*windowAspect = ( float ) *width / *height;
		}
		else if ( mode == -1 )
		{
			*width = r_customwidth->integer;
			*height = r_customheight->integer;
			*windowAspect = r_customaspect->value;
		}
		else
		{
			vm = &r_vidModes[ mode ];

			*width = vm->width;
			*height = vm->height;
			*windowAspect = ( float ) vm->width / ( vm->height * vm->pixelAspect );
		}

		return qtrue;
	}

	/*
	** R_ModeList_f
	*/
	static void R_ModeList_f( void )
	{
		int i;

		for ( i = 0; i < s_numVidModes; i++ )
		{
			ri.Printf( PRINT_ALL, "Mode %-2d: %s", i, r_vidModes[ i ].description );
		}
	}

	/*
	==============================================================================

	                                                SCREEN SHOTS

	screenshots get written in fs_homepath + fs_gamedir
	.. base/screenshots\*.*

	three commands: "screenshot", "screenshotJPEG" and "screenshotPNG"

	the format is etxreal-YYYY_MM_DD-HH_MM_SS-MS.tga/jpeg/png

	==============================================================================
	*/

	/*
	==================
	RB_ReadPixels

	Reads an image but takes care of alignment issues for reading RGB images.
	Prepends the specified number of (uninitialized) bytes to the buffer.

	The returned buffer must be freed with ri.Hunk_FreeTempMemory().
	==================
	*/
	static byte *RB_ReadPixels( int x, int y, int width, int height, size_t offset )
	{
		GLint packAlign;
		int   lineLen, paddedLineLen;
		byte  *buffer, *pixels;
		int   i;

		glGetIntegerv( GL_PACK_ALIGNMENT, &packAlign );

		lineLen = width * 3;
		paddedLineLen = PAD( lineLen, packAlign );

		// Allocate a few more bytes so that we can choose an alignment we like
		buffer = ( byte * ) ri.Hunk_AllocateTempMemory( offset + paddedLineLen * height + packAlign - 1 );

		pixels = ( byte * ) PADP( buffer + offset, packAlign );
		glReadPixels( x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels );

		// Drop alignment and line padding bytes
		for ( i = 0; i < height; ++i )
		{
			memmove( buffer + offset + i * lineLen, pixels + i * paddedLineLen, lineLen );
		}

		return buffer;
	}

	/*
	==================
	R_TakeScreenshot
	==================
	*/
	static void RB_TakeScreenshot( int x, int y, int width, int height, char *fileName )
	{
		byte *buffer;
		int  dataSize;
		byte *end, *p;

		// with 18 bytes for the TGA file header
		buffer = RB_ReadPixels( x, y, width, height, 18 );
		Com_Memset( buffer, 0, 18 );

		buffer[ 2 ] = 2; // uncompressed type
		buffer[ 12 ] = width & 255;
		buffer[ 13 ] = width >> 8;
		buffer[ 14 ] = height & 255;
		buffer[ 15 ] = height >> 8;
		buffer[ 16 ] = 24; // pixel size

		dataSize = 3 * width * height;

		// swap RGB to BGR
		end = buffer + 18 + dataSize;

		for ( p = buffer + 18; p < end; p += 3 )
		{
			byte temp = p[ 0 ];
			p[ 0 ] = p[ 2 ];
			p[ 2 ] = temp;
		}

		ri.FS_WriteFile( fileName, buffer, 18 + dataSize );

		ri.Hunk_FreeTempMemory( buffer );
	}

	/*
	==================
	R_TakeScreenshotJPEG
	==================
	*/
	static void RB_TakeScreenshotJPEG( int x, int y, int width, int height, char *fileName )
	{
		byte *buffer = RB_ReadPixels( x, y, width, height, 0 );

		SaveJPG( fileName, 90, width, height, buffer );
		ri.Hunk_FreeTempMemory( buffer );
	}

	/*
	==================
	R_TakeScreenshotPNG
	==================
	*/
	static void RB_TakeScreenshotPNG( int x, int y, int width, int height, char *fileName )
	{
		byte *buffer = RB_ReadPixels( x, y, width, height, 0 );

		SavePNG( fileName, buffer, width, height, 3, qfalse );
		ri.Hunk_FreeTempMemory( buffer );
	}

	/*
	==================
	RB_TakeScreenshotCmd
	==================
	*/
	const void     *RB_TakeScreenshotCmd( const void *data )
	{
		const screenshotCommand_t *cmd;

		cmd = ( const screenshotCommand_t * ) data;

		switch ( cmd->format )
		{
			case SSF_TGA:
				RB_TakeScreenshot( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
				break;

			case SSF_JPEG:
				RB_TakeScreenshotJPEG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
				break;

			case SSF_PNG:
				RB_TakeScreenshotPNG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
				break;
		}

		return ( const void * )( cmd + 1 );
	}

	/*
	==================
	R_TakeScreenshot
	==================
	*/
	void R_TakeScreenshot( const char *name, ssFormat_t format )
	{
		static char         fileName[ MAX_OSPATH ]; // bad things may happen if two screenshots per frame are taken.
		screenshotCommand_t *cmd;
		int                 lastNumber;

		cmd = ( screenshotCommand_t * ) R_GetCommandBuffer( sizeof( *cmd ) );

		if ( !cmd )
		{
			return;
		}

		if ( ri.Cmd_Argc() == 2 )
		{
			Com_sprintf( fileName, sizeof( fileName ), "screenshots/" PRODUCT_NAME_LOWER "-%s.%s", ri.Cmd_Argv( 1 ), name );
		}
		else
		{
			qtime_t t;

			ri.RealTime( &t );

			// scan for a free filename
			for ( lastNumber = 0; lastNumber <= 999; lastNumber++ )
			{
				Com_sprintf( fileName, sizeof( fileName ), "screenshots/" PRODUCT_NAME_LOWER "_%04d-%02d-%02d_%02d%02d%02d_%03d.%s",
				             1900 + t.tm_year, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, lastNumber, name );

				if ( !ri.FS_FileExists( fileName ) )
				{
					break; // file doesn't exist
				}
			}

			if ( lastNumber == 1000 )
			{
				ri.Printf( PRINT_ALL, "ScreenShot: Couldn't create a file\n" );
				return;
			}

			lastNumber++;
		}

		ri.Printf( PRINT_ALL, "Wrote %s\n", fileName );

		cmd->commandId = RC_SCREENSHOT;
		cmd->x = 0;
		cmd->y = 0;
		cmd->width = glConfig.vidWidth;
		cmd->height = glConfig.vidHeight;
		cmd->fileName = fileName;
		cmd->format = format;
	}

	/*
	==================
	R_ScreenShot_f

	screenshot
	screenshot [filename]
	==================
	*/
	static void R_ScreenShot_f( void )
	{
		R_TakeScreenshot( "tga", SSF_TGA );
	}

	static void R_ScreenShotJPEG_f( void )
	{
		R_TakeScreenshot( "jpg", SSF_JPEG );
	}

	static void R_ScreenShotPNG_f( void )
	{
		R_TakeScreenshot( "png", SSF_PNG );
	}

//============================================================================

	/*
	==================
	RB_TakeVideoFrameCmd
	==================
	*/
	const void     *RB_TakeVideoFrameCmd( const void *data )
	{
		const videoFrameCommand_t *cmd;
		GLint                     packAlign;
		int                       lineLen, captureLineLen;
		byte                      *pixels;
		int                       i;
		int                       outputSize;
		int                       j;
		int                       aviLineLen;

		cmd = ( const videoFrameCommand_t * ) data;

		// RB: it is possible to we still have a videoFrameCommand_t but we already stopped
		// video recording
		if ( ri.CL_VideoRecording() )
		{
			// take care of alignment issues for reading RGB images..

			glGetIntegerv( GL_PACK_ALIGNMENT, &packAlign );

			lineLen = cmd->width * 3;
			captureLineLen = PAD( lineLen, packAlign );

			pixels = ( byte * ) PADP( cmd->captureBuffer, packAlign );
			glReadPixels( 0, 0, cmd->width, cmd->height, GL_RGB, GL_UNSIGNED_BYTE, pixels );

			if ( cmd->motionJpeg )
			{
				// Drop alignment and line padding bytes
				for ( i = 0; i < cmd->height; ++i )
				{
					memmove( cmd->captureBuffer + i * lineLen, pixels + i * captureLineLen, lineLen );
				}

				outputSize = SaveJPGToBuffer( cmd->encodeBuffer, 3 * cmd->width * cmd->height, 90, cmd->width, cmd->height, cmd->captureBuffer );
				ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, outputSize );
			}
			else
			{
				aviLineLen = PAD( lineLen, AVI_LINE_PADDING );

				for ( i = 0; i < cmd->height; ++i )
				{
					for ( j = 0; j < lineLen; j += 3 )
					{
						cmd->encodeBuffer[ i * aviLineLen + j + 0 ] = pixels[ i * captureLineLen + j + 2 ];
						cmd->encodeBuffer[ i * aviLineLen + j + 1 ] = pixels[ i * captureLineLen + j + 1 ];
						cmd->encodeBuffer[ i * aviLineLen + j + 2 ] = pixels[ i * captureLineLen + j + 0 ];
					}

					while ( j < aviLineLen )
					{
						cmd->encodeBuffer[ i * aviLineLen + j++ ] = 0;
					}
				}

				ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, aviLineLen * cmd->height );
			}
		}

		return ( const void * )( cmd + 1 );
	}

//============================================================================

	/*
	** GL_SetDefaultState
	*/
	void GL_SetDefaultState( void )
	{
		int i;

		GLimp_LogComment( "--- GL_SetDefaultState ---\n" );

		GL_ClearDepth( 1.0f );

		if ( glConfig.stencilBits >= 4 )
		{
			GL_ClearStencil( 128 );
		}

		GL_FrontFace( GL_CCW );
		GL_CullFace( GL_FRONT );

		glState.faceCulling = CT_TWO_SIDED;
		glDisable( GL_CULL_FACE );

		GL_CheckErrors();

		glVertexAttrib4f( ATTR_INDEX_COLOR, 1, 1, 1, 1 );

		GL_CheckErrors();

		// initialize downstream texture units if we're running
		// in a multitexture environment
		if ( glConfig.driverType == GLDRV_OPENGL3 )
		{
			for ( i = 31; i >= 0; i-- )
			{
				GL_SelectTexture( i );
				GL_TextureMode( r_textureMode->string );
			}
		}
		else
		{
			if ( GLEW_ARB_multitexture )
			{
				for ( i = glConfig.maxActiveTextures - 1; i >= 0; i-- )
				{
					GL_SelectTexture( i );
					GL_TextureMode( r_textureMode->string );
				}
			}
		}

		GL_CheckErrors();

		GL_DepthFunc( GL_LEQUAL );

		// make sure our GL state vector is set correctly
		glState.glStateBits = GLS_DEPTHTEST_DISABLE | GLS_DEPTHMASK_TRUE;
		glState.vertexAttribsState = 0;
		glState.vertexAttribPointersSet = 0;

		GL_BindProgram( NULL );

		glBindBuffer( GL_ARRAY_BUFFER, 0 );
		glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
		glState.currentVBO = NULL;
		glState.currentIBO = NULL;

		GL_CheckErrors();

		// the vertex array is always enabled, but the color and texture
		// arrays are enabled and disabled around the compiled vertex array call
		glEnableVertexAttribArray( ATTR_INDEX_POSITION );

		/*
		   OpenGL 3.0 spec: E.1. PROFILES AND DEPRECATED FEATURES OF OPENGL 3.0 405
		   Calling VertexAttribPointer when no buffer object or no
		   vertex array object is bound will generate an INVALID OPERATION error,
		   as will calling any array drawing command when no vertex array object is
		   bound.
		 */

		if ( glConfig2.framebufferObjectAvailable )
		{
			glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
			glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 );
			glState.currentFBO = NULL;
		}

		GL_PolygonMode( GL_FRONT_AND_BACK, GL_FILL );
		GL_DepthMask( GL_TRUE );
		glDisable( GL_DEPTH_TEST );
		glEnable( GL_SCISSOR_TEST );
		glDisable( GL_BLEND );

		glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
		glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
		glClearDepth( 1.0 );

		glDrawBuffer( GL_BACK );
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );

		GL_CheckErrors();

		glState.stackIndex = 0;

		for ( i = 0; i < MAX_GLSTACK; i++ )
		{
			MatrixIdentity( glState.modelViewMatrix[ i ] );
			MatrixIdentity( glState.projectionMatrix[ i ] );
			MatrixIdentity( glState.modelViewProjectionMatrix[ i ] );
		}
	}

	/*
	================
	GfxInfo_f
	================
	*/
	void GfxInfo_f( void )
	{
		static const char fsstrings[][16] =
		{
			"windowed",
			"fullscreen"
		};

		ri.Printf( PRINT_ALL, "\nGL_VENDOR: %s\n", glConfig.vendor_string );
		ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glConfig.renderer_string );
		ri.Printf( PRINT_ALL, "GL_VERSION: %s\n", glConfig.version_string );
		ri.Printf( PRINT_DEVELOPER, "GL_EXTENSIONS: %s\n", glConfig.extensions_string );
		ri.Printf( PRINT_DEVELOPER, "GL_MAX_TEXTURE_SIZE: %d\n", glConfig.maxTextureSize );

		if ( glConfig.driverType != GLDRV_OPENGL3 )
		{
			ri.Printf( PRINT_DEVELOPER, "GL_MAX_TEXTURE_UNITS: %d\n", glConfig.maxActiveTextures );
		}

		ri.Printf( PRINT_ALL, "GL_SHADING_LANGUAGE_VERSION: %s\n", glConfig2.shadingLanguageVersionString );

		ri.Printf( PRINT_ALL, "GL_MAX_VERTEX_UNIFORM_COMPONENTS %d\n", glConfig2.maxVertexUniforms );
		ri.Printf( PRINT_DEVELOPER, "GL_MAX_VERTEX_ATTRIBS %d\n", glConfig2.maxVertexAttribs );

		if ( glConfig2.occlusionQueryAvailable )
		{
			ri.Printf( PRINT_DEVELOPER, "%d occlusion query bits\n", glConfig2.occlusionQueryBits );
		}

		if ( glConfig2.drawBuffersAvailable )
		{
			ri.Printf( PRINT_DEVELOPER, "GL_MAX_DRAW_BUFFERS: %d\n", glConfig2.maxDrawBuffers );
		}

		if ( glConfig2.textureAnisotropyAvailable )
		{
			ri.Printf( PRINT_DEVELOPER, "GL_TEXTURE_MAX_ANISOTROPY_EXT: %f\n", glConfig2.maxTextureAnisotropy );
		}

		if ( glConfig2.framebufferObjectAvailable )
		{
			ri.Printf( PRINT_DEVELOPER, "GL_MAX_RENDERBUFFER_SIZE_EXT: %d\n", glConfig2.maxRenderbufferSize );
			ri.Printf( PRINT_DEVELOPER, "GL_MAX_COLOR_ATTACHMENTS_EXT: %d\n", glConfig2.maxColorAttachments );
		}

		ri.Printf( PRINT_DEVELOPER, "\nPIXELFORMAT: color(%d-bits) Z(%d-bit) stencil(%d-bits)\n", glConfig.colorBits,
		           glConfig.depthBits, glConfig.stencilBits );
		ri.Printf( PRINT_DEVELOPER, "MODE: %d, %d x %d %s hz:", r_mode->integer, glConfig.vidWidth, glConfig.vidHeight,
		           fsstrings[ r_fullscreen->integer == 1 ] );

		if ( glConfig.displayFrequency )
		{
			ri.Printf( PRINT_DEVELOPER, "%d\n", glConfig.displayFrequency );
		}
		else
		{
			ri.Printf( PRINT_DEVELOPER, "N/A\n" );
		}

		ri.Printf( PRINT_DEVELOPER, "texturemode: %s\n", r_textureMode->string );
		ri.Printf( PRINT_DEVELOPER, "picmip: %d\n", r_picmip->integer );

		if ( glConfig.driverType == GLDRV_OPENGL3 )
		{
			int contextFlags, profile;

			ri.Printf( PRINT_ALL, S_COLOR_GREEN "Using OpenGL 3.x context\n" );

			// check if we have a core-profile
			glGetIntegerv( GL_CONTEXT_PROFILE_MASK, &profile );

			if ( profile == GL_CONTEXT_CORE_PROFILE_BIT )
			{
				ri.Printf( PRINT_DEVELOPER, S_COLOR_GREEN "Having a core profile\n" );
			}
			else
			{
				ri.Printf( PRINT_DEVELOPER, S_COLOR_RED "Having a compatibility profile\n" );
			}

			// check if context is forward compatible
			glGetIntegerv( GL_CONTEXT_FLAGS, &contextFlags );

			if ( contextFlags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT )
			{
				ri.Printf( PRINT_DEVELOPER, S_COLOR_GREEN "Context is forward compatible\n" );
			}
			else
			{
				ri.Printf( PRINT_DEVELOPER, S_COLOR_RED "Context is NOT forward compatible\n" );
			}
		}

		if ( glConfig.hardwareType == GLHW_ATI )
		{
			ri.Printf( PRINT_DEVELOPER, "HACK: ATI approximations\n" );
		}

		if ( glConfig.textureCompression != TC_NONE )
		{
			ri.Printf( PRINT_DEVELOPER, "Using S3TC (DXTC) texture compression\n" );
		}

		if ( glConfig.hardwareType == GLHW_ATI_DX10 )
		{
			ri.Printf( PRINT_DEVELOPER, "Using ATI DirectX 10 hardware features\n" );
		}

		if ( glConfig.hardwareType == GLHW_NV_DX10 )
		{
			ri.Printf( PRINT_DEVELOPER, "Using NVIDIA DirectX 10 hardware features\n" );
		}

		if ( glConfig2.vboVertexSkinningAvailable )
		{
			ri.Printf( PRINT_ALL, "Using GPU vertex skinning with max %i bones in a single pass\n", glConfig2.maxVertexSkinningBones );
		}
{'fraction_non_alphanumeric': 0.13463435633441553, 'fraction_numerical': 0.01998170436404728, 'mean_word_length': 3.037074583635047, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 177, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6321749', 'n_tokens_mistral': 24456, 'n_tokens_neox': 21410, 'n_words': 4578}