/******************************Module*Header*******************************\ * Module Name: wcreate.c * * wgl Context creation routines * * Created: 08-27-1996 * Author: Drew Bliss [drewb] * * Copyright (c) 1996 Microsoft Corporation \**************************************************************************/ #include "precomp.h" #pragma hdrstop #include #include #include #include #include "metasup.h" #include "wgldef.h" // List of loaded GL drivers for the process. // A driver is loaded only once per process. Once it is loaded, // it will not be freed until the process quits. static PGLDRIVER pGLDriverList = (PGLDRIVER) NULL; /******************************Public*Routine******************************\ * iAllocLRC * * Allocates a LRC and a handle. Initializes the LDC to have the default * attributes. Returns the handle index. On error returns INVALID_INDEX. * * History: * Tue Oct 26 10:25:26 1993 -by- Hock San Lee [hockl] * Wrote it. \**************************************************************************/ static LRC lrcDefault = { 0, // dhrc 0, // hrc 0, // iPixelFormat LRC_IDENTIFIER, // ident INVALID_THREAD_ID, // tidCurrent NULL, // pGLDriver GLWID_ERROR, NULL, NULL, NULL, // gwidCurrent GLWID_ERROR, NULL, NULL, NULL, // gwidCreate #ifdef GL_METAFILE 0, // uiGlsCaptureContext 0, // uiGlsPlaybackContext FALSE, // fCapturing 0, 0, 0, 0, 0, // Metafile scaling constants 0, 0, 0, 0.0f, 0.0f, #endif NULL, // GLubyte *pszExtensions #ifdef GL_METAFILE {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // XFORM xformMeta NULL, // LPRECTL prclGlsBounds #endif NULL, 0, // DDraw texture formats }; static ULONG iAllocLRC(int iPixelFormat) { ULONG irc = INVALID_INDEX; PLRC plrc; // Allocate a local RC. plrc = (PLRC) ALLOC(sizeof(LRC)); if (plrc == (PLRC) NULL) { DBGERROR("Alloc failed\n"); return(irc); } // Initialize the local RC. *plrc = lrcDefault; plrc->iPixelFormat = iPixelFormat; // Allocate a local handle. irc = iAllocHandle(LO_RC, 0, (PVOID) plrc); if (irc == INVALID_INDEX) { vFreeLRC(plrc); return(irc); } return(irc); } /******************************Public*Routine******************************\ * vFreeLRC * * Free a local side RC. * * History: * Tue Oct 26 10:25:26 1993 -by- Hock San Lee [hockl] * Copied from gdi client. \**************************************************************************/ VOID vFreeLRC(PLRC plrc) { // The driver will not be unloaded here. It is loaded for the process forever. // Some assertions. ASSERTOPENGL(plrc->ident == LRC_IDENTIFIER, "vFreeLRC: Bad plrc\n"); ASSERTOPENGL(plrc->dhrc == (DHGLRC) 0, "vFreeLRC: Driver RC is not freed!\n"); ASSERTOPENGL(plrc->tidCurrent == INVALID_THREAD_ID, "vFreeLRC: RC is current!\n"); ASSERTOPENGL(plrc->gwidCurrent.iType == GLWID_ERROR, "vFreeLRC: Current surface is not NULL!\n"); #ifdef GL_METAFILE ASSERTOPENGL(plrc->uiGlsCaptureContext == 0, "vFreeLRC: GLS capture context not freed"); ASSERTOPENGL(plrc->uiGlsPlaybackContext == 0, "vFreeLRC: GLS playback context not freed"); ASSERTOPENGL(plrc->fCapturing == FALSE, "vFreeLRC: GLS still capturing"); #endif // Smash the identifier. plrc->ident = 0; // Free the memory. if (plrc->pszExtensions) FREE(plrc->pszExtensions); if (plrc->pddsdTexFormats != NULL) { FREE(plrc->pddsdTexFormats); } FREE(plrc); } /******************************Public*Routine******************************\ * vCleanupAllLRC * * Process cleanup -- make sure all HGLRCs are deleted. This is done by * scanning the local handle table for all currently allocated objects * of type LO_RC and deleting them. * * Called *ONLY* during DLL process detach. * * History: * 24-Jul-1995 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ VOID vCleanupAllLRC() { UINT ii; if ( pLocalTable ) { ENTERCRITICALSECTION(&semLocal); // Scan handle table for handles of type LO_RC. Make sure to always // read the commit value since we need to periodically release the // semaphore. for (ii = 0; ii < *((volatile ULONG *)&cLheCommitted); ii++) { if ( pLocalTable[ii].iType == LO_RC ) { if ( !wglDeleteContext((HGLRC) ULongToPtr(LHANDLE(ii))) ) { WARNING1("bCleanupAllLRC: failed to remove hrc = 0x%lx\n", LHANDLE(ii)); } } } LEAVECRITICALSECTION(&semLocal); } } /******************************Public*Routine******************************\ * * GetDrvRegInfo * * Looks up driver registry information by name. * An old-style ICD registry entry has a REG_SZ value under the given name. * A new-style ICD registry entry has a key of the given name with * various values. * * This routine checks first for a key and then will optionally * try the value. If a key is not found then extended driver information * is filled out with the defaults. * * History: * Tue Apr 01 17:33:12 1997 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ typedef struct _GLDRVINFO { DWORD dwFlags; TCHAR tszDllName[MAX_GLDRIVER_NAME+1]; DWORD dwVersion; DWORD dwDriverVersion; } GLDRVINFO; #ifdef _WIN95_ #define STR_OPENGL_DRIVER_LIST (PCSTR)"Software\\Microsoft\\Windows\\CurrentVersion\\OpenGLDrivers" #else #define STR_OPENGL_DRIVER_LIST (PCWSTR)L"Software\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers" #endif BOOL GetDrvRegInfo(PTCHAR ptszName, GLDRVINFO *pgdi) { HKEY hkDriverList = NULL; HKEY hkDriverInfo; DWORD dwDataType; DWORD cjSize; BOOL bRet; bRet = FALSE; // Open the registry key for the list of OpenGL drivers. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, STR_OPENGL_DRIVER_LIST, 0, KEY_READ, &hkDriverList) != ERROR_SUCCESS) { WARNING("RegOpenKeyEx failed\n"); return bRet; } // Force a terminator on the DLL name so that we can check for // valid DLL name data. pgdi->tszDllName[MAX_GLDRIVER_NAME] = 0; cjSize = sizeof(TCHAR) * MAX_GLDRIVER_NAME; // Attempt to open a key under the driver's name. if (RegOpenKeyEx(hkDriverList, ptszName, 0, KEY_READ, &hkDriverInfo) == ERROR_SUCCESS) { // New-style driver entry. Fetch information from values. bRet = TRUE; if (RegQueryValueEx(hkDriverInfo, __TEXT("DLL"), NULL, &dwDataType, (LPBYTE)pgdi->tszDllName, &cjSize) != ERROR_SUCCESS || dwDataType != REG_SZ) { WARNING("Invalid DLL value in ICD key\n"); bRet = FALSE; } cjSize = sizeof(DWORD); if (bRet && (RegQueryValueEx(hkDriverInfo, __TEXT("Flags"), NULL, &dwDataType, (LPBYTE)&pgdi->dwFlags, &cjSize) != ERROR_SUCCESS || dwDataType != REG_DWORD)) { WARNING("Invalid Flags value in ICD key\n"); bRet = FALSE; } if (bRet && (RegQueryValueEx(hkDriverInfo, __TEXT("Version"), NULL, &dwDataType, (LPBYTE)&pgdi->dwVersion, &cjSize) != ERROR_SUCCESS || dwDataType != REG_DWORD)) { WARNING("Invalid Version value in ICD key\n"); bRet = FALSE; } if (bRet && (RegQueryValueEx(hkDriverInfo, __TEXT("DriverVersion"), NULL, &dwDataType, (LPBYTE)&pgdi->dwDriverVersion, &cjSize) != ERROR_SUCCESS || dwDataType != REG_DWORD)) { WARNING("Invalid DriverVersion value in ICD key\n"); bRet = FALSE; } // Mark as having full information. pgdi->dwFlags |= GLDRIVER_FULL_REGISTRY; RegCloseKey(hkDriverInfo); } else { // Attempt to fetch value under driver's name. if (RegQueryValueEx(hkDriverList, ptszName, NULL, &dwDataType, (LPBYTE)pgdi->tszDllName, &cjSize) != ERROR_SUCCESS || dwDataType != REG_SZ) { WARNING1("RegQueryValueEx failed, %d\n", GetLastError()); } else { // We found old-style information which only provides the // DLL name. Fill in the rest with defaults. // // Version and DriverVersion are not set here under the // assumption that the display driver set them in the // OPENGL_GETINFO escape since the old-style path requires // the escape to occur before getting here. pgdi->dwFlags = 0; bRet = TRUE; } } RegCloseKey(hkDriverList); // Validate the driver name. It must have some characters and // it must be terminated. if (bRet && (pgdi->tszDllName[0] == 0 || pgdi->tszDllName[MAX_GLDRIVER_NAME] != 0)) { WARNING("Invalid DLL name information for ICD\n"); bRet = FALSE; } #ifdef _WIN95_ // Force client-side buffer calls for Win95. pgdi->dwFlags |= GLDRIVER_CLIENT_BUFFER_CALLS; #endif return bRet; } /******************************Public*Routine******************************\ * bGetDriverInfo * * The HDC is used to determine the display driver name. This name in turn * is used as a subkey to search the registry for a corresponding OpenGL * driver name. * * The OpenGL driver name is returned in the buffer pointed to by pwszDriver. * If the name is not found or does not fit in the buffer, an error is * returned. * * Returns: * TRUE if sucessful. * FALSE if the driver name does not fit in the buffer or if an error occurs. * * History: * 16-Jan-1994 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ BOOL bGetDriverInfo(HDC hdc, GLDRVINFO *pgdi) { GLDRVNAME dn; GLDRVNAMERET dnRet; // Get display driver name. dn.oglget.ulSubEsc = OPENGL_GETINFO_DRVNAME; if ( ExtEscape(hdc, OPENGL_GETINFO, sizeof(GLDRVNAME), (LPCSTR) &dn, sizeof(GLDRVNAMERET), (LPSTR) &dnRet) <= 0 ) { WARNING("ExtEscape(OPENGL_GETINFO, " "OPENGL_GETINFO_DRVNAME) failed\n"); return FALSE; } pgdi->dwVersion = dnRet.ulVersion; pgdi->dwDriverVersion = dnRet.ulDriverVersion; if (GetDrvRegInfo((PTCHAR)dnRet.awch, pgdi)) { // Verify that the client-side driver version information // matches the information returned from the display driver. // Is this too restrictive? Old scheme used // DrvValidateVersion to allow the client-side DLL to validate // the display driver's version however it felt like. // In the new scheme DrvValidateVersion is mostly useless because // of the below code. return pgdi->dwVersion == dnRet.ulVersion && pgdi->dwDriverVersion == dnRet.ulDriverVersion; } else { return FALSE; } } /*****************************Private*Routine******************************\ * * wglCbSetCurrentValue * * Sets a thread-local value for a client-side driver * * History: * Wed Dec 21 15:10:40 1994 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ void APIENTRY wglCbSetCurrentValue(VOID *pv) { GLTEB_SET_CLTDRIVERSLOT(pv); } /*****************************Private*Routine******************************\ * * wglCbGetCurrentValue * * Gets a thread-local value for a client-side driver * * History: * Wed Dec 21 15:11:32 1994 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ PVOID APIENTRY wglCbGetCurrentValue(void) { return GLTEB_CLTDRIVERSLOT(); } /******************************Public*Routine******************************\ * * wglCbGetDhglrc * * Translates an HGLRC to a DHGLRC for a client-side driver * * History: * Mon Jan 16 17:03:38 1995 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ DHGLRC APIENTRY wglCbGetDhglrc(HGLRC hrc) { PLRC plrc; ULONG irc; PLHE plheRC; irc = MASKINDEX(hrc); plheRC = pLocalTable + irc; if ((irc >= cLheCommitted) || (!MATCHUNIQ(plheRC, hrc)) || ((plheRC->iType != LO_RC)) ) { DBGLEVEL1(LEVEL_ERROR, "wglCbGetDhglrc: invalid hrc 0x%lx\n", hrc); SetLastError(ERROR_INVALID_HANDLE); return 0; } plrc = (PLRC)plheRC->pv; ASSERTOPENGL(plrc->ident == LRC_IDENTIFIER, "wglCbGetDhglrc: Bad plrc\n"); return plrc->dhrc; } /******************************Public*Routine******************************\ * * wglCbGetDdHandle * * Callback to allow ICDs to extract kernel-mode handles for DDraw surfaces * * History: * Tue Feb 25 17:14:29 1997 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ HANDLE APIENTRY wglCbGetDdHandle(LPDIRECTDRAWSURFACE pdds) { return (HANDLE)(((LPDDRAWI_DDRAWSURFACE_INT)pdds)->lpLcl)->hDDSurface; } // wgl's default callback procedures #define CALLBACK_PROC_COUNT 4 static PROC __wglCallbackProcs[CALLBACK_PROC_COUNT] = { (PROC)wglCbSetCurrentValue, (PROC)wglCbGetCurrentValue, (PROC)wglCbGetDhglrc, (PROC)wglCbGetDdHandle }; static char *pszDriverEntryPoints[] = { "DrvCreateContext", "DrvDeleteContext", "DrvSetContext", "DrvReleaseContext", "DrvCopyContext", "DrvCreateLayerContext", "DrvShareLists", "DrvGetProcAddress", "DrvDescribeLayerPlane", "DrvSetLayerPaletteEntries", "DrvGetLayerPaletteEntries", "DrvRealizeLayerPalette", "DrvSwapLayerBuffers", "DrvCreateDirectDrawContext", "DrvEnumTextureFormats", "DrvBindDirectDrawTexture", "DrvSwapMultipleBuffers", "DrvDescribePixelFormat", "DrvSetPixelFormat", "DrvSwapBuffers" }; #define DRIVER_ENTRY_POINTS (sizeof(pszDriverEntryPoints)/sizeof(char *)) /******************************Public*Routine******************************\ * pgldrvLoadInstalledDriver * * Loads the opengl driver for the given device. Once the driver is loaded, * it will not be freed until the process goes away! It is loaded only once * for each process that references it. * * Returns the GLDRIVER structure if the driver is loaded. * Returns NULL if no driver is found or an error occurs. * * History: * Tue Oct 26 10:25:26 1993 -by- Hock San Lee [hockl] * Rewrote it. \**************************************************************************/ PGLDRIVER APIENTRY pgldrvLoadInstalledDriver(HDC hdc) { GLDRVINFO gdi; PGLDRIVER pGLDriverNext; PGLDRIVER pGLDriver = (PGLDRIVER) NULL; // needed by clean up PGLDRIVER pGLDriverRet = (PGLDRIVER) NULL; // return value, assume error PFN_DRVVALIDATEVERSION pfnDrvValidateVersion = (PFN_DRVVALIDATEVERSION) NULL; PFN_DRVSETCALLBACKPROCS pfnDrvSetCallbackProcs; DWORD dwEscape; int i; PROC *pproc; GLGENwindow *pwnd; GLWINDOWID gwid; DBGENTRY("pgldrvLoadInstalledDriver\n"); // Try to grab the cached pgldrv from the GLGENwindow if it exists. // This only works for DCs that have a window with a device pixel format. WindowIdFromHdc(hdc, &gwid); pwnd = pwndGetFromID(&gwid); if (pwnd) { ULONG ulFlags; ulFlags = pwnd->ulFlags; pGLDriverRet = (PGLDRIVER) pwnd->pvDriver; pwndRelease(pwnd); if ( ulFlags & GLGENWIN_DRIVERSET ) { return pGLDriverRet; } } // Do a quick check and see if this driver even understands OpenGL dwEscape = OPENGL_GETINFO; if (ExtEscape(hdc, QUERYESCSUPPORT, sizeof(dwEscape), (LPCSTR)&dwEscape, 0, NULL) <= 0) { // Don't output a message since this code path is traversed often // for the pixel format routines. #ifdef CHECK_DEFAULT_ICD // The display driver doesn't support a specific ICD. Check // for a default ICD. It must have full registry information. if (!GetDrvRegInfo(__TEXT("Default"), &gdi) || (gdi.dwFlags & GLDRIVER_FULL_REGISTRY) == 0) { return NULL; } #else return NULL; #endif } // Determine driver info from hdc else if ( !bGetDriverInfo(hdc, &gdi) ) { WARNING("bGetDriverInfo failed\n"); return NULL; } // Load the driver only once per process. ENTERCRITICALSECTION(&semLocal); // Look for the OpenGL driver in the previously loaded driver list. for (pGLDriverNext = pGLDriverList; pGLDriverNext != (PGLDRIVER) NULL; pGLDriverNext = pGLDriverNext->pGLDriver) { PTCHAR ptszDllName1 = pGLDriverNext->tszDllName; PTCHAR ptszDllName2 = gdi.tszDllName; while (*ptszDllName1 == *ptszDllName2) { // If we find one, return that driver. if (*ptszDllName1 == 0) { DBGINFO("pgldrvLoadInstalledDriver: " "return previously loaded driver\n"); pGLDriverRet = pGLDriverNext; // found one goto pgldrvLoadInstalledDriver_crit_exit; } ptszDllName1++; ptszDllName2++; } } // Load the driver for the first time. // Allocate the driver data. pGLDriver = (PGLDRIVER) ALLOC(sizeof(GLDRIVER)); if (pGLDriver == (PGLDRIVER) NULL) { WARNING("Alloc failed\n"); goto pgldrvLoadInstalledDriver_crit_exit; // error } // Load the driver. pGLDriver->hModule = LoadLibrary(gdi.tszDllName); if (pGLDriver->hModule == (HINSTANCE) NULL) { WARNING("pgldrvLoadInstalledDriver: LoadLibrary failed\n"); goto pgldrvLoadInstalledDriver_crit_exit; // error } // Copy the driver info. memcpy ( pGLDriver->tszDllName, gdi.tszDllName, (MAX_GLDRIVER_NAME + 1) * sizeof(TCHAR) ); pGLDriver->dwFlags = gdi.dwFlags; // Get the proc addresses. // DrvGetProcAddress is optional. It must be provided if a driver supports // extensions. pfnDrvValidateVersion = (PFN_DRVVALIDATEVERSION) GetProcAddress(pGLDriver->hModule, "DrvValidateVersion"); pfnDrvSetCallbackProcs = (PFN_DRVSETCALLBACKPROCS) GetProcAddress(pGLDriver->hModule, "DrvSetCallbackProcs"); pproc = (PROC *)&pGLDriver->pfnDrvCreateContext; for (i = 0; i < DRIVER_ENTRY_POINTS; i++) { *pproc++ = GetProcAddress(pGLDriver->hModule, pszDriverEntryPoints[i]); } if ((pGLDriver->pfnDrvCreateContext == NULL && pGLDriver->pfnDrvCreateLayerContext == NULL) || pGLDriver->pfnDrvDeleteContext == NULL || pGLDriver->pfnDrvSetContext == NULL || pGLDriver->pfnDrvReleaseContext == NULL || ((gdi.dwFlags & GLDRIVER_CLIENT_BUFFER_CALLS) && (pGLDriver->pfnDrvDescribePixelFormat == NULL || pGLDriver->pfnDrvSetPixelFormat == NULL || pGLDriver->pfnDrvSwapBuffers == NULL)) || pfnDrvValidateVersion == NULL) { WARNING("pgldrvLoadInstalledDriver: GetProcAddress failed\n"); goto pgldrvLoadInstalledDriver_crit_exit; // error } // Validate the driver. //!!!XXX -- Need to define a manifest constant for the ulVersion number // in this release. Where should it go? if ( gdi.dwVersion != 2 || !pfnDrvValidateVersion(gdi.dwDriverVersion) ) { WARNING2("pgldrvLoadInstalledDriver: bad driver version " "(0x%lx, 0x%lx)\n", gdi.dwVersion, gdi.dwDriverVersion); goto pgldrvLoadInstalledDriver_crit_exit; // error } // Everything is golden. // Add it to the driver list. pGLDriver->pGLDriver = pGLDriverList; pGLDriverList = pGLDriver; pGLDriverRet = pGLDriver; // set return value DBGINFO("pgldrvLoadInstalledDriver: Loaded an OpenGL driver\n"); // Set the callback procs for the driver if the driver supports doing so if (pfnDrvSetCallbackProcs != NULL) { pfnDrvSetCallbackProcs(CALLBACK_PROC_COUNT, __wglCallbackProcs); } // Error clean up in the critical section. pgldrvLoadInstalledDriver_crit_exit: if (pGLDriverRet == (PGLDRIVER) NULL) { if (pGLDriver != (PGLDRIVER) NULL) { if (pGLDriver->hModule != (HINSTANCE) NULL) if (!FreeLibrary(pGLDriver->hModule)) RIP("FreeLibrary failed\n"); FREE(pGLDriver); } } LEAVECRITICALSECTION(&semLocal); return(pGLDriverRet); } /******************************Public*Routine******************************\ * * CreateAnyContext * * Base worker function for creating all kinds of contexts * * History: * Mon Aug 26 14:41:31 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ HGLRC CreateAnyContext(GLSURF *pgsurf) { PLHE plheRC; ULONG irc; HGLRC hrc; PLRC plrc; #ifndef _WIN95_ // _OPENGL_NT_ // On NT, client-side drivers can use special fast TEB access macros // which rely on glContext being at a fixed offset into the // TEB. Assert that the offset is where we think it is // to catch any TEB changes which could break client-side // drivers // This assert is here in wglCreateContext to ensure that it // is checked very early in OpenGL operation ASSERTOPENGL(FIELD_OFFSET(TEB, glContext) == TeglContext, "TEB.glContext at wrong offset\n"); ASSERTOPENGL(FIELD_OFFSET(TEB, glDispatchTable) == TeglDispatchTable, "TEB.glDispatchTable at wrong offset\n"); ASSERTOPENGL(FIELD_OFFSET(TEB, glReserved1) == TeglReserved1, "TEB.glReserved1 at wrong offset\n"); #if !defined(_WIN64) ASSERTOPENGL(FIELD_OFFSET(TEB, glReserved1)+(18 * sizeof(ULONG_PTR)) == TeglPaTeb, "TEB.glPaTeb at wrong offset\n"); #endif ASSERTOPENGL(FIELD_OFFSET(TEB, glReserved2) == TeglReserved2, "TEB.glReserved2 at wrong offset\n"); ASSERTOPENGL(FIELD_OFFSET(TEB, glSectionInfo) == TeglSectionInfo, "TEB.glSectionInfo at wrong offset\n"); ASSERTOPENGL(FIELD_OFFSET(TEB, glSection) == TeglSection, "TEB.glSection at wrong offset\n"); ASSERTOPENGL(FIELD_OFFSET(TEB, glTable) == TeglTable, "TEB.glTable at wrong offset\n"); ASSERTOPENGL(FIELD_OFFSET(TEB, glCurrentRC) == TeglCurrentRC, "TEB.glCurrentRC at wrong offset\n"); #endif // Create the local RC. ENTERCRITICALSECTION(&semLocal); irc = iAllocLRC(pgsurf->ipfd); if (irc == INVALID_INDEX || cLockHandle((ULONG_PTR)(hrc = (HGLRC) ULongToPtr(LHANDLE(irc)))) <= 0) { // cLockHandle should never fail or we will need to free the handle. ASSERTOPENGL(irc == INVALID_INDEX, "cLockHandle should not fail!\n"); LEAVECRITICALSECTION(&semLocal); return((HGLRC) 0); } LEAVECRITICALSECTION(&semLocal); plheRC = &pLocalTable[irc]; plrc = (PLRC) plheRC->pv; // Remember the creation DC. This needs to be done early because // it is referenced in some code paths. plrc->gwidCreate.hdc = pgsurf->hdc; if (pgsurf->dwFlags & GLSURF_HDC) { plrc->gwidCreate.hwnd = pgsurf->hwnd; if (plrc->gwidCreate.hwnd == NULL) { plrc->gwidCreate.iType = GLWID_HDC; } else { plrc->gwidCreate.iType = GLWID_HWND; } plrc->gwidCreate.pdds = NULL; } else { plrc->gwidCreate.iType = GLWID_DDRAW; plrc->gwidCreate.pdds = pgsurf->dd.gddsFront.pdds; plrc->gwidCreate.hwnd = NULL; } if (!(pgsurf->pfd.dwFlags & PFD_GENERIC_FORMAT) && !(pgsurf->pfd.dwFlags & PFD_GENERIC_ACCELERATED)) { // If it is a device format, load the installable OpenGL driver. // Find and load the OpenGL driver referenced by this DC. if (!(plrc->pGLDriver = pgldrvLoadInstalledDriver(pgsurf->hdc))) goto wglCreateContext_error; // Create a driver context. // If the surface is a DirectDraw surface use the DirectDraw // entry point if (pgsurf->dwFlags & GLSURF_DIRECTDRAW) { if (plrc->pGLDriver->pfnDrvCreateDirectDrawContext == NULL) { SetLastError(ERROR_INVALID_FUNCTION); goto wglCreateContext_error; } plrc->dhrc = plrc->pGLDriver->pfnDrvCreateDirectDrawContext( pgsurf->hdc, pgsurf->dd.gddsFront.pdds, pgsurf->ipfd); if (plrc->dhrc == 0) { WARNING("wglCreateContext: " "pfnDrvCreateDirectDrawContext failed\n"); goto wglCreateContext_error; } } // If the driver supports layers then create a context for the // given layer. Otherwise reject all layers except for the // main plane and call the layer-less create else if (plrc->pGLDriver->pfnDrvCreateLayerContext != NULL) { if (!(plrc->dhrc = plrc->pGLDriver->pfnDrvCreateLayerContext(pgsurf->hdc, pgsurf->iLayer))) { WARNING("wglCreateContext: pfnDrvCreateLayerContext failed\n"); goto wglCreateContext_error; } } else if (pgsurf->iLayer != 0) { WARNING("wglCreateContext: " "Layer given for driver without layer support\n"); SetLastError(ERROR_INVALID_FUNCTION); goto wglCreateContext_error; } else if (!(plrc->dhrc = plrc->pGLDriver->pfnDrvCreateContext(pgsurf->hdc))) { WARNING("wglCreateContext: pfnDrvCreateContext failed\n"); goto wglCreateContext_error; } } else { GLCLTPROCTABLE *pgcpt; GLEXTPROCTABLE *pgept; __GLcontext *gc; // Unless supported by MCD, the generic implementation doesn't // support layers if ((pgsurf->iLayer != 0) && !(pgsurf->pfd.dwFlags & PFD_GENERIC_ACCELERATED)) { WARNING("wglCreateContext: Layer given to generic\n"); goto wglCreateContext_error; } #ifdef GL_METAFILE // Create a metafile context if necessary if (pgsurf->dwFlags & GLSURF_METAFILE) { if (!CreateMetaRc(pgsurf->hdc, plrc)) { WARNING("wglCreateContext: CreateMetaRc failed\n"); goto wglCreateContext_error; } } #endif // If it is a generic format, call the generic OpenGL server. // Create a server RC. plheRC->hgre = (ULONG_PTR) __wglCreateContext(&plrc->gwidCreate, pgsurf); if (plheRC->hgre == 0) goto wglCreateContext_error; // Set up the default dispatch tables for display list playback gc = (__GLcontext *)plheRC->hgre; if (gc->modes.colorIndexMode) pgcpt = &glCltCIProcTable; else pgcpt = &glCltRGBAProcTable; pgept = &glExtProcTable; memcpy(&gc->savedCltProcTable.glDispatchTable, &pgcpt->glDispatchTable, pgcpt->cEntries*sizeof(PROC)); memcpy(&gc->savedExtProcTable.glDispatchTable, &pgept->glDispatchTable, pgept->cEntries*sizeof(PROC)); } DBGLEVEL3(LEVEL_INFO, "wglCreateContext: plrc = 0x%lx, pGLDriver = 0x%lx, hgre = 0x%lx\n", plrc, plrc->pGLDriver, plheRC->hgre); // Success, return the result. plrc->hrc = hrc; vUnlockHandle((ULONG_PTR)hrc); return hrc; wglCreateContext_error: // Fail, clean up and return 0. #ifdef GL_METAFILE // Clean up metafile context if necessary if (plrc->uiGlsCaptureContext != 0) { DeleteMetaRc(plrc); } #endif DBGERROR("wglCreateContext failed\n"); ASSERTOPENGL(plrc->dhrc == (DHGLRC) 0, "wglCreateContext: dhrc != 0\n"); vFreeLRC(plrc); vFreeHandle(irc); // it unlocks handle too return NULL; } /******************************Public*Routine******************************\ * * CreateMetafileSurf * * Fills out a GLSURF for a metafile DC * * History: * Tue Aug 27 11:41:35 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ #ifdef GL_METAFILE void CreateMetafileSurf(HDC hdc, int iLayer, GLSURF *pgsurf) { pgsurf->dwFlags = GLSURF_HDC | GLSURF_METAFILE; pgsurf->iLayer = iLayer; // Metafile surfaces don't have a real pixel format pgsurf->ipfd = 0; // Create a fake format of 24-bit DIB with BGR memset(&pgsurf->pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); pgsurf->pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pgsurf->pfd.nVersion = 1; pgsurf->pfd.dwFlags = PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL | PFD_GENERIC_FORMAT; pgsurf->pfd.iPixelType = PFD_TYPE_RGBA; pgsurf->pfd.cColorBits = 24; pgsurf->pfd.cStencilBits = 8; pgsurf->pfd.cRedBits = 8; pgsurf->pfd.cRedShift = 16; pgsurf->pfd.cGreenBits = 8; pgsurf->pfd.cGreenShift = 8; pgsurf->pfd.cBlueBits = 8; pgsurf->pfd.cBlueShift = 0; pgsurf->pfd.cDepthBits = 16; pgsurf->pfd.iLayerType = PFD_MAIN_PLANE; pgsurf->hdc = hdc; } #endif /******************************Public*Routine******************************\ * wglSurfacePixelFormat * * wglDescribePixelFormat doesn't describe the format of the surface we want * to render into. Some fields need to be fixed up if the surface is RGB, * BGR, or BITFIELDS. * * Expects a Describe'd pixel format as input * \**************************************************************************/ VOID APIENTRY wglSurfacePixelFormat(HDC hdc, PIXELFORMATDESCRIPTOR *ppfd) { HBITMAP hbm; BITMAP bm; ULONG cBitmapColorBits; hbm = CreateCompatibleBitmap(hdc, 1, 1); if ( hbm ) { if ( GetObject(hbm, sizeof(bm), &bm) ) { cBitmapColorBits = bm.bmPlanes * bm.bmBitsPixel; #if DBG // If dynamic color depth caused depth mismatch one of two // things will happen: 1) bitmap creation will fail because // we failed to fill in color format, or 2) drawing will // be incorrect. We will not crash. if (cBitmapColorBits != ppfd->cColorBits) WARNING("pixel format/surface color depth mismatch\n"); #endif if ( cBitmapColorBits >= 16 ) __wglGetBitfieldColorFormat(hdc, cBitmapColorBits, ppfd, TRUE); } else { WARNING("wglSurfacePixelFormat: GetObject failed\n"); } DeleteObject(hbm); } else { WARNING("wglSurfacePixelFormat: Unable to create cbm\n"); } } /******************************Public*Routine******************************\ * bLayerPixelFormat * * Fake up a pixel format using the layer descriptor format. * * We use this to describe the layer plane in a format that the generic * context can understand. * * Expects a Describe'd pixel format as input for the flags * \**************************************************************************/ BOOL FASTCALL bLayerPixelFormat(HDC hdc, PIXELFORMATDESCRIPTOR *ppfd, int ipfd, LONG iLayer) { LAYERPLANEDESCRIPTOR lpd; if (!wglDescribeLayerPlane(hdc, ipfd, iLayer, sizeof(lpd), &lpd)) return FALSE; ppfd->nSize = sizeof(PIXELFORMATDESCRIPTOR); ppfd->nVersion = 1; ppfd->dwFlags = (ppfd->dwFlags & (PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED)) | (lpd.dwFlags & ~(LPD_SHARE_DEPTH | LPD_SHARE_STENCIL | LPD_SHARE_ACCUM | LPD_TRANSPARENT)); ppfd->iPixelType = lpd.iPixelType; ppfd->cColorBits = lpd.cColorBits; ppfd->cRedBits = lpd.cRedBits ; ppfd->cRedShift = lpd.cRedShift ; ppfd->cGreenBits = lpd.cGreenBits ; ppfd->cGreenShift = lpd.cGreenShift; ppfd->cBlueBits = lpd.cBlueBits ; ppfd->cBlueShift = lpd.cBlueShift ; ppfd->cAlphaBits = lpd.cAlphaBits ; ppfd->cAlphaShift = lpd.cAlphaShift; if (!(lpd.dwFlags & LPD_SHARE_ACCUM)) { ppfd->cAccumBits = 0; ppfd->cAccumRedBits = 0; ppfd->cAccumGreenBits = 0; ppfd->cAccumBlueBits = 0; ppfd->cAccumAlphaBits = 0; } if (!(lpd.dwFlags & LPD_SHARE_DEPTH)) { ppfd->cDepthBits = 0; } if (!(lpd.dwFlags & LPD_SHARE_STENCIL)) { ppfd->cStencilBits = 0; } ppfd->cAuxBuffers = 0; return TRUE; } /******************************Public*Routine******************************\ * * IsDirectDrawDevice * * Returns surface associated with HDC if such an association exists * * History: * Wed Sep 25 13:18:02 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ BOOL APIENTRY IsDirectDrawDevice(HDC hdc) { LPDIRECTDRAWSURFACE pdds; HDC hdcDevice; if (pfnGetSurfaceFromDC != NULL && pfnGetSurfaceFromDC(hdc, &pdds, &hdcDevice) == DD_OK) { // The call gave us a reference on the surface so release it. pdds->lpVtbl->Release(pdds); return TRUE; } else { return FALSE; } } /******************************Public*Routine******************************\ * * DdPixelDepth * * Determines the number of bits per pixel for a surface. * * History: * Wed Nov 20 16:57:07 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ BYTE APIENTRY DdPixelDepth(DDSURFACEDESC *pddsd) { if (pddsd->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED4) { return 4; } else if (pddsd->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8) { return 8; } else { return (BYTE)DdPixDepthToCount(pddsd->ddpfPixelFormat.dwRGBBitCount); } } /******************************Public*Routine******************************\ * * wglIsDirectDevice * * Checks to see whether the given DC is a screen DC on the * surface for which we have direct screen access * * History: * Fri Apr 19 15:17:30 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ BOOL APIENTRY wglIsDirectDevice(HDC hdc) { if (wglObjectType(hdc) != OBJ_DC) { return FALSE; } // What about multiple displays? return GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY; } /******************************Public*Routine******************************\ * * InitDeviceSurface * * Fills out a GLSURF for an HDC-based surface * * History: * Tue Aug 27 19:22:38 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ BOOL APIENTRY InitDeviceSurface(HDC hdc, int ipfd, int iLayer, DWORD dwObjectType, BOOL bUpdatePfd, GLSURF *pgsurf) { pgsurf->dwFlags = GLSURF_HDC; pgsurf->iLayer = iLayer; pgsurf->ipfd = ipfd; pgsurf->hdc = hdc; pgsurf->hwnd = NULL; // Determine whether direct memory access is available for this surface // or not. The two cases are: // It's a screen surface and we have direct screen access // It's a DIBSECTION memory surface if (dwObjectType == OBJ_DC) { pgsurf->dwFlags |= GLSURF_DIRECTDC; if (wglIsDirectDevice(hdc)) { pgsurf->dwFlags |= GLSURF_SCREEN | GLSURF_VIDEO_MEMORY; pgsurf->hwnd = WindowFromDC(hdc); if (GLDIRECTSCREEN) { pgsurf->dwFlags |= GLSURF_DIRECT_ACCESS; } } } else if (dwObjectType == OBJ_MEMDC) { DIBSECTION ds; if (GetObject(GetCurrentObject(hdc, OBJ_BITMAP), sizeof(ds), &ds) == sizeof(ds) && ds.dsBm.bmBits != NULL) { pgsurf->dwFlags |= GLSURF_DIRECT_ACCESS; } if (bUpdatePfd) { // Update pixel format with true surface information rather // than device information wglSurfacePixelFormat(hdc, &pgsurf->pfd); } } if (bUpdatePfd && iLayer > 0 && !bLayerPixelFormat(hdc, &pgsurf->pfd, ipfd, iLayer)) { return FALSE; } return TRUE; } /******************************Public*Routine******************************\ * * InitDdSurface * * Completes a GLSURF for a DirectDraw-based surface. * Pixel format information should already be filled in. * * History: * Mon Aug 26 13:50:04 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ BOOL InitDdSurface(LPDIRECTDRAWSURFACE pdds, HDC hdcDevice, GLSURF *pgsurf) { DDSCAPS ddscaps; LPDIRECTDRAWSURFACE pddsZ; DDSURFACEDESC *pddsd; pgsurf->hdc = hdcDevice; pgsurf->dd.gddsFront.ddsd.dwSize = sizeof(DDSURFACEDESC); pgsurf->dd.gddsZ.ddsd.dwSize = sizeof(DDSURFACEDESC); pddsd = &pgsurf->dd.gddsFront.ddsd; if (pdds->lpVtbl->GetSurfaceDesc(pdds, pddsd) != DD_OK) { return FALSE; } pgsurf->dwFlags = GLSURF_DIRECTDRAW | GLSURF_DIRECT_ACCESS; pgsurf->iLayer = 0; // Check for an attached Z buffer memset(&ddscaps, 0, sizeof(ddscaps)); ddscaps.dwCaps = DDSCAPS_ZBUFFER; pddsd = &pgsurf->dd.gddsZ.ddsd; if (pdds->lpVtbl->GetAttachedSurface(pdds, &ddscaps, &pddsZ) == DD_OK) { if (pddsZ->lpVtbl->GetSurfaceDesc(pddsZ, pddsd) != DD_OK) { pddsZ->lpVtbl->Release(pddsZ); return FALSE; } } else { memset(&pgsurf->dd.gddsZ, 0, sizeof(pgsurf->dd.gddsZ)); } // If both the color buffer and the Z buffer are in video memory // then hardware acceleration is possible if ((pgsurf->dd.gddsFront.ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) && (pddsZ == NULL || (pgsurf->dd.gddsZ.ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY))) { pgsurf->dwFlags |= GLSURF_VIDEO_MEMORY; } pgsurf->dd.gddsFront.pdds = pdds; pgsurf->dd.gddsFront.dwBitDepth = DdPixDepthToCount(pgsurf->dd.gddsFront. ddsd.ddpfPixelFormat.dwRGBBitCount); // GetAttachedSurface gave us a reference to the Z buffer pgsurf->dd.gddsZ.pdds = pddsZ; pgsurf->dd.gddsZ.dwBitDepth = DdPixDepthToCount(pgsurf->dd.gddsZ. ddsd.ddpfPixelFormat.dwZBufferBitDepth); return TRUE; } /******************************Public*Routine******************************\ * wglCreateLayerContext(HDC hdc, int iLayer) * * Create a rendering context for a specific layer * * Arguments: * hdc - Device context. * iLayer - Layer * * History: * Tue Oct 26 10:25:26 1993 -by- Hock San Lee [hockl] * Rewrote it. \**************************************************************************/ HGLRC WINAPI wglCreateLayerContext(HDC hdc, int iLayer) { DWORD dwObjectType; GLSURF gsurf; LPDIRECTDRAWSURFACE pdds; HDC hdcDevice; HGLRC hrc; DBGENTRY("wglCreateLayerContext\n"); // Flush OpenGL calls. GLFLUSH(); // Validate the DC. dwObjectType = wglObjectType(hdc); switch (dwObjectType) { case OBJ_DC: case OBJ_MEMDC: break; case OBJ_ENHMETADC: #ifdef GL_METAFILE if (pfnGdiAddGlsRecord == NULL) { DBGLEVEL1(LEVEL_ERROR, "wglCreateContext: metafile hdc: 0x%lx\n", hdc); SetLastError(ERROR_INVALID_HANDLE); return((HGLRC) 0); } break; #else DBGLEVEL1(LEVEL_ERROR, "wglCreateContext: metafile hdc: 0x%lx\n", hdc); SetLastError(ERROR_INVALID_HANDLE); return((HGLRC) 0); #endif case OBJ_METADC: default: // 16-bit metafiles are not supported DBGLEVEL1(LEVEL_ERROR, "wglCreateContext: bad hdc: 0x%lx\n", hdc); SetLastError(ERROR_INVALID_HANDLE); return((HGLRC) 0); } pdds = NULL; hrc = NULL; memset(&gsurf, 0, sizeof(gsurf)); gsurf.ipfd = GetPixelFormat(hdc); #ifdef GL_METAFILE // Skip pixel format checks for metafiles if (dwObjectType == OBJ_ENHMETADC) { CreateMetafileSurf(hdc, iLayer, &gsurf); goto NoPixelFormat; } #endif // Get the current pixel format of the window or surface. // If no pixel format has been set, return error. if (gsurf.ipfd == 0) { WARNING("wglCreateContext: No pixel format set in hdc\n"); SetLastError(ERROR_INVALID_PIXEL_FORMAT); return ((HGLRC) 0); } if (!DescribePixelFormat(hdc, gsurf.ipfd, sizeof(gsurf.pfd), &gsurf.pfd)) { DBGERROR("wglCreateContext: DescribePixelFormat failed\n"); return ((HGLRC) 0); } // Check for a DirectDraw surface if (pfnGetSurfaceFromDC != NULL && pfnGetSurfaceFromDC(hdc, &pdds, &hdcDevice) == DD_OK) { // Don't allow layers for DirectDraw surfaces since // layering is done through DirectDraw itself. if (iLayer != 0 || !InitDdSurface(pdds, hdcDevice, &gsurf)) { goto Exit; } } else if (!InitDeviceSurface(hdc, gsurf.ipfd, iLayer, dwObjectType, TRUE, &gsurf)) { goto Exit; } #ifdef GL_METAFILE NoPixelFormat: #endif hrc = CreateAnyContext(&gsurf); Exit: if (hrc == NULL) { if (pdds != NULL) { pdds->lpVtbl->Release(pdds); // Release reference on Z buffer if necessary if (gsurf.dd.gddsZ.pdds != NULL) { gsurf.dd.gddsZ.pdds->lpVtbl->Release(gsurf.dd.gddsZ.pdds); } } } return hrc; } /******************************Public*Routine******************************\ * wglCreateContext(HDC hdc) * * Create a rendering context. * * Arguments: * hdc - Device context. * * History: * Tue Oct 26 10:25:26 1993 -by- Hock San Lee [hockl] * Rewrote it. \**************************************************************************/ HGLRC WINAPI wglCreateContext(HDC hdc) { return wglCreateLayerContext(hdc, 0); }