📄 win_glimp.c
字号:
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena 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.
Quake III Arena 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 Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
/*
** WIN_GLIMP.C
**
** This file contains ALL Win32 specific stuff having to do with the
** OpenGL refresh. When a port is being made the following functions
** must be implemented by the port:
**
** GLimp_EndFrame
** GLimp_Init
** GLimp_LogComment
** GLimp_Shutdown
**
** Note that the GLW_xxx functions are Windows specific GL-subsystem
** related functions that are relevant ONLY to win_glimp.c
*/
#include <assert.h>
#include "../renderer/tr_local.h"
#include "../qcommon/qcommon.h"
#include "resource.h"
#include "glw_win.h"
#include "win_local.h"
extern void WG_CheckHardwareGamma( void );
extern void WG_RestoreGamma( void );
typedef enum {
RSERR_OK,
RSERR_INVALID_FULLSCREEN,
RSERR_INVALID_MODE,
RSERR_UNKNOWN
} rserr_t;
#define TRY_PFD_SUCCESS 0
#define TRY_PFD_FAIL_SOFT 1
#define TRY_PFD_FAIL_HARD 2
#define WINDOW_CLASS_NAME "Quake 3: Arena"
static void GLW_InitExtensions( void );
static rserr_t GLW_SetMode( const char *drivername,
int mode,
int colorbits,
qboolean cdsFullscreen );
static qboolean s_classRegistered = qfalse;
//
// function declaration
//
void QGL_EnableLogging( qboolean enable );
qboolean QGL_Init( const char *dllname );
void QGL_Shutdown( void );
//
// variable declarations
//
glwstate_t glw_state;
cvar_t *r_allowSoftwareGL; // don't abort out if the pixelformat claims software
cvar_t *r_maskMinidriver; // allow a different dll name to be treated as if it were opengl32.dll
/*
** GLW_StartDriverAndSetMode
*/
static qboolean GLW_StartDriverAndSetMode( const char *drivername,
int mode,
int colorbits,
qboolean cdsFullscreen )
{
rserr_t err;
err = GLW_SetMode( drivername, r_mode->integer, colorbits, cdsFullscreen );
switch ( err )
{
case RSERR_INVALID_FULLSCREEN:
ri.Printf( PRINT_ALL, "...WARNING: fullscreen unavailable in this mode\n" );
return qfalse;
case RSERR_INVALID_MODE:
ri.Printf( PRINT_ALL, "...WARNING: could not set the given mode (%d)\n", mode );
return qfalse;
default:
break;
}
return qtrue;
}
/*
** ChoosePFD
**
** Helper function that replaces ChoosePixelFormat.
*/
#define MAX_PFDS 256
static int GLW_ChoosePFD( HDC hDC, PIXELFORMATDESCRIPTOR *pPFD )
{
PIXELFORMATDESCRIPTOR pfds[MAX_PFDS+1];
int maxPFD = 0;
int i;
int bestMatch = 0;
ri.Printf( PRINT_ALL, "...GLW_ChoosePFD( %d, %d, %d )\n", ( int ) pPFD->cColorBits, ( int ) pPFD->cDepthBits, ( int ) pPFD->cStencilBits );
// count number of PFDs
if ( glConfig.driverType > GLDRV_ICD )
{
maxPFD = qwglDescribePixelFormat( hDC, 1, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[0] );
}
else
{
maxPFD = DescribePixelFormat( hDC, 1, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[0] );
}
if ( maxPFD > MAX_PFDS )
{
ri.Printf( PRINT_WARNING, "...numPFDs > MAX_PFDS (%d > %d)\n", maxPFD, MAX_PFDS );
maxPFD = MAX_PFDS;
}
ri.Printf( PRINT_ALL, "...%d PFDs found\n", maxPFD - 1 );
// grab information
for ( i = 1; i <= maxPFD; i++ )
{
if ( glConfig.driverType > GLDRV_ICD )
{
qwglDescribePixelFormat( hDC, i, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[i] );
}
else
{
DescribePixelFormat( hDC, i, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[i] );
}
}
// look for a best match
for ( i = 1; i <= maxPFD; i++ )
{
//
// make sure this has hardware acceleration
//
if ( ( pfds[i].dwFlags & PFD_GENERIC_FORMAT ) != 0 )
{
if ( !r_allowSoftwareGL->integer )
{
if ( r_verbose->integer )
{
ri.Printf( PRINT_ALL, "...PFD %d rejected, software acceleration\n", i );
}
continue;
}
}
// verify pixel type
if ( pfds[i].iPixelType != PFD_TYPE_RGBA )
{
if ( r_verbose->integer )
{
ri.Printf( PRINT_ALL, "...PFD %d rejected, not RGBA\n", i );
}
continue;
}
// verify proper flags
if ( ( ( pfds[i].dwFlags & pPFD->dwFlags ) & pPFD->dwFlags ) != pPFD->dwFlags )
{
if ( r_verbose->integer )
{
ri.Printf( PRINT_ALL, "...PFD %d rejected, improper flags (%x instead of %x)\n", i, pfds[i].dwFlags, pPFD->dwFlags );
}
continue;
}
// verify enough bits
if ( pfds[i].cDepthBits < 15 )
{
continue;
}
if ( ( pfds[i].cStencilBits < 4 ) && ( pPFD->cStencilBits > 0 ) )
{
continue;
}
//
// selection criteria (in order of priority):
//
// PFD_STEREO
// colorBits
// depthBits
// stencilBits
//
if ( bestMatch )
{
// check stereo
if ( ( pfds[i].dwFlags & PFD_STEREO ) && ( !( pfds[bestMatch].dwFlags & PFD_STEREO ) ) && ( pPFD->dwFlags & PFD_STEREO ) )
{
bestMatch = i;
continue;
}
if ( !( pfds[i].dwFlags & PFD_STEREO ) && ( pfds[bestMatch].dwFlags & PFD_STEREO ) && ( pPFD->dwFlags & PFD_STEREO ) )
{
bestMatch = i;
continue;
}
// check color
if ( pfds[bestMatch].cColorBits != pPFD->cColorBits )
{
// prefer perfect match
if ( pfds[i].cColorBits == pPFD->cColorBits )
{
bestMatch = i;
continue;
}
// otherwise if this PFD has more bits than our best, use it
else if ( pfds[i].cColorBits > pfds[bestMatch].cColorBits )
{
bestMatch = i;
continue;
}
}
// check depth
if ( pfds[bestMatch].cDepthBits != pPFD->cDepthBits )
{
// prefer perfect match
if ( pfds[i].cDepthBits == pPFD->cDepthBits )
{
bestMatch = i;
continue;
}
// otherwise if this PFD has more bits than our best, use it
else if ( pfds[i].cDepthBits > pfds[bestMatch].cDepthBits )
{
bestMatch = i;
continue;
}
}
// check stencil
if ( pfds[bestMatch].cStencilBits != pPFD->cStencilBits )
{
// prefer perfect match
if ( pfds[i].cStencilBits == pPFD->cStencilBits )
{
bestMatch = i;
continue;
}
// otherwise if this PFD has more bits than our best, use it
else if ( ( pfds[i].cStencilBits > pfds[bestMatch].cStencilBits ) &&
( pPFD->cStencilBits > 0 ) )
{
bestMatch = i;
continue;
}
}
}
else
{
bestMatch = i;
}
}
if ( !bestMatch )
return 0;
if ( ( pfds[bestMatch].dwFlags & PFD_GENERIC_FORMAT ) != 0 )
{
if ( !r_allowSoftwareGL->integer )
{
ri.Printf( PRINT_ALL, "...no hardware acceleration found\n" );
return 0;
}
else
{
ri.Printf( PRINT_ALL, "...using software emulation\n" );
}
}
else if ( pfds[bestMatch].dwFlags & PFD_GENERIC_ACCELERATED )
{
ri.Printf( PRINT_ALL, "...MCD acceleration found\n" );
}
else
{
ri.Printf( PRINT_ALL, "...hardware acceleration found\n" );
}
*pPFD = pfds[bestMatch];
return bestMatch;
}
/*
** void GLW_CreatePFD
**
** Helper function zeros out then fills in a PFD
*/
static void GLW_CreatePFD( PIXELFORMATDESCRIPTOR *pPFD, int colorbits, int depthbits, int stencilbits, qboolean stereo )
{
PIXELFORMATDESCRIPTOR src =
{
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
24, // 24-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
};
src.cColorBits = colorbits;
src.cDepthBits = depthbits;
src.cStencilBits = stencilbits;
if ( stereo )
{
ri.Printf( PRINT_ALL, "...attempting to use stereo\n" );
src.dwFlags |= PFD_STEREO;
glConfig.stereoEnabled = qtrue;
}
else
{
glConfig.stereoEnabled = qfalse;
}
*pPFD = src;
}
/*
** GLW_MakeContext
*/
static int GLW_MakeContext( PIXELFORMATDESCRIPTOR *pPFD )
{
int pixelformat;
//
// don't putz around with pixelformat if it's already set (e.g. this is a soft
// reset of the graphics system)
//
if ( !glw_state.pixelFormatSet )
{
//
// choose, set, and describe our desired pixel format. If we're
// using a minidriver then we need to bypass the GDI functions,
// otherwise use the GDI functions.
//
if ( ( pixelformat = GLW_ChoosePFD( glw_state.hDC, pPFD ) ) == 0 )
{
ri.Printf( PRINT_ALL, "...GLW_ChoosePFD failed\n");
return TRY_PFD_FAIL_SOFT;
}
ri.Printf( PRINT_ALL, "...PIXELFORMAT %d selected\n", pixelformat );
if ( glConfig.driverType > GLDRV_ICD )
{
qwglDescribePixelFormat( glw_state.hDC, pixelformat, sizeof( *pPFD ), pPFD );
if ( qwglSetPixelFormat( glw_state.hDC, pixelformat, pPFD ) == FALSE )
{
ri.Printf ( PRINT_ALL, "...qwglSetPixelFormat failed\n");
return TRY_PFD_FAIL_SOFT;
}
}
else
{
DescribePixelFormat( glw_state.hDC, pixelformat, sizeof( *pPFD ), pPFD );
if ( SetPixelFormat( glw_state.hDC, pixelformat, pPFD ) == FALSE )
{
ri.Printf (PRINT_ALL, "...SetPixelFormat failed\n", glw_state.hDC );
return TRY_PFD_FAIL_SOFT;
}
}
glw_state.pixelFormatSet = qtrue;
}
//
// startup the OpenGL subsystem by creating a context and making it current
//
if ( !glw_state.hGLRC )
{
ri.Printf( PRINT_ALL, "...creating GL context: " );
if ( ( glw_state.hGLRC = qwglCreateContext( glw_state.hDC ) ) == 0 )
{
ri.Printf (PRINT_ALL, "failed\n");
return TRY_PFD_FAIL_HARD;
}
ri.Printf( PRINT_ALL, "succeeded\n" );
ri.Printf( PRINT_ALL, "...making context current: " );
if ( !qwglMakeCurrent( glw_state.hDC, glw_state.hGLRC ) )
{
qwglDeleteContext( glw_state.hGLRC );
glw_state.hGLRC = NULL;
ri.Printf (PRINT_ALL, "failed\n");
return TRY_PFD_FAIL_HARD;
}
ri.Printf( PRINT_ALL, "succeeded\n" );
}
return TRY_PFD_SUCCESS;
}
/*
** GLW_InitDriver
**
** - get a DC if one doesn't exist
** - create an HGLRC if one doesn't exist
*/
static qboolean GLW_InitDriver( const char *drivername, int colorbits )
{
int tpfd;
int depthbits, stencilbits;
static PIXELFORMATDESCRIPTOR pfd; // save between frames since 'tr' gets cleared
ri.Printf( PRINT_ALL, "Initializing OpenGL driver\n" );
//
// get a DC for our window if we don't already have one allocated
//
if ( glw_state.hDC == NULL )
{
ri.Printf( PRINT_ALL, "...getting DC: " );
if ( ( glw_state.hDC = GetDC( g_wv.hWnd ) ) == NULL )
{
ri.Printf( PRINT_ALL, "failed\n" );
return qfalse;
}
ri.Printf( PRINT_ALL, "succeeded\n" );
}
if ( colorbits == 0 )
{
colorbits = glw_state.desktopBitsPixel;
}
//
// implicitly assume Z-buffer depth == desktop color depth
//
if ( r_depthbits->integer == 0 ) {
if ( colorbits > 16 ) {
depthbits = 24;
} else {
depthbits = 16;
}
} else {
depthbits = r_depthbits->integer;
}
//
// do not allow stencil if Z-buffer depth likely won't contain it
//
stencilbits = r_stencilbits->integer;
if ( depthbits < 24 )
{
stencilbits = 0;
}
//
// make two attempts to set the PIXELFORMAT
//
//
// first attempt: r_colorbits, depthbits, and r_stencilbits
//
if ( !glw_state.pixelFormatSet )
{
GLW_CreatePFD( &pfd, colorbits, depthbits, stencilbits, r_stereo->integer );
if ( ( tpfd = GLW_MakeContext( &pfd ) ) != TRY_PFD_SUCCESS )
{
if ( tpfd == TRY_PFD_FAIL_HARD )
{
ri.Printf( PRINT_WARNING, "...failed hard\n" );
return qfalse;
}
//
// punt if we've already tried the desktop bit depth and no stencil bits
//
if ( ( r_colorbits->integer == glw_state.desktopBitsPixel ) &&
( stencilbits == 0 ) )
{
ReleaseDC( g_wv.hWnd, glw_state.hDC );
glw_state.hDC = NULL;
ri.Printf( PRINT_ALL, "...failed to find an appropriate PIXELFORMAT\n" );
return qfalse;
}
//
// second attempt: desktop's color bits and no stencil
//
if ( colorbits > glw_state.desktopBitsPixel )
{
colorbits = glw_state.desktopBitsPixel;
}
GLW_CreatePFD( &pfd, colorbits, depthbits, 0, r_stereo->integer );
if ( GLW_MakeContext( &pfd ) != TRY_PFD_SUCCESS )
{
if ( glw_state.hDC )
{
ReleaseDC( g_wv.hWnd, glw_state.hDC );
glw_state.hDC = NULL;
}
ri.Printf( PRINT_ALL, "...failed to find an appropriate PIXELFORMAT\n" );
return qfalse;
}
}
/*
** report if stereo is desired but unavailable
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -