/******************************Module*Header*******************************\ * Module Name: dllinit.c * * (Brief description) * * Created: 18-Oct-1993 14:13:21 * Author: Gilman Wong [gilmanw] * * Copyright (c) 1993 Microsoft Corporation * \**************************************************************************/ #include "precomp.h" #pragma hdrstop #include "batchinf.h" #include "glteb.h" #include "glapi.h" #include "glsbcltu.h" #ifdef _CLIENTSIDE_ #include "glscreen.h" #include "glgenwin.h" #endif //_CLIENTSIDE_ #include "context.h" #include "global.h" #include "parray.h" #include "gencx.h" #include "cpu.h" #include "fixed.h" #ifdef _CLIENTSIDE_ // Global screen access info. This is NULL if screen access is not available. SCREENINFO *gpScreenInfo = NULL; extern GLubyte *dBufFill; extern GLubyte *dBufTopLeft; // // This global multiply-lookup table helps with pixel-related functions. // BYTE gbMulTable[256*256+4]; BYTE gbSatTable[256+256]; // // This global inverse-lookup table helps with rasterization setup // #define INV_TABLE_SIZE (1 << __GL_VERTEX_FRAC_BITS) * (__GL_MAX_INV_TABLE + 1) __GLfloat invTable[INV_TABLE_SIZE]; // Global thread local storage index. Allocated at process attach. // This is the slot reserved in thread local storage for per-thread // GLTLSINFO structures. static DWORD dwTlsIndex = 0xFFFFFFFF; static BOOL bProcessInitialized = FALSE; // Offset into the TEB where dwTlsIndex is // This enables us to directly access our TLS data in the TEB #if defined(_WIN64) #define NT_TLS_OFFSET 5248 #else #define NT_TLS_OFFSET 3600 #endif #define WIN95_TLS_OFFSET 136 DWORD dwTlsOffset; // Platform indicator for conditional code DWORD dwPlatformId; // Thread count LONG lThreadsAttached = 0; // Global header node for the linked list of GLGENwindow structures. // The semaphore in the header node is used as the list access semaphore. GLGENwindow gwndHeader; // Synchronization object for pixel formats CRITICAL_SECTION gcsPixelFormat; // Protection for palette watcher CRITICAL_SECTION gcsPaletteWatcher; #ifdef GL_METAFILE BOOL (APIENTRY *pfnGdiAddGlsRecord)(HDC hdc, DWORD cb, BYTE *pb, LPRECTL prclBounds); BOOL (APIENTRY *pfnGdiAddGlsBounds)(HDC hdc, LPRECTL prclBounds); BOOL (APIENTRY *pfnGdiIsMetaPrintDC)(HDC hdc); #endif #endif //_CLIENTSIDE_ // OpenGL client debug flag #if DBG long glDebugLevel; ULONG glDebugFlags; #endif BOOL bDirectScreen = FALSE; PFN_GETSURFACEFROMDC pfnGetSurfaceFromDC = NULL; /******************************Public*Routine******************************\ * * DdbdToCount * * Converts a DDBD constant to its equivalent number * * History: * Mon Aug 26 14:11:34 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ DWORD APIENTRY DdbdToCount(DWORD ddbd) { switch(ddbd) { case DDBD_1: return 1; case DDBD_2: return 2; case DDBD_4: return 4; case DDBD_8: return 8; case DDBD_16: return 16; case DDBD_24: return 24; case DDBD_32: return 32; } ASSERTOPENGL(FALSE, "DdbdToCount: Invalid ddbd\n"); return 0; } /******************************Public*Routine******************************\ * GLInitializeProcess * * Called from OPENGL32.DLL entry point for PROCESS_ATTACH. * * History: * 01-Nov-1994 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ BOOL GLInitializeProcess() { PVOID pv; #ifdef _CLIENTSIDE_ OSVERSIONINFO osvi; #endif // Attempt to locate GDI exports for metafiling support { HMODULE hdll; hdll = GetModuleHandleA("gdi32"); ASSERTOPENGL(hdll != NULL, "Unable to get gdi32 handle\n"); *(PROC *)&pfnGdiAddGlsRecord = GetProcAddress(hdll, "GdiAddGlsRecord"); *(PROC *)&pfnGdiAddGlsBounds = GetProcAddress(hdll, "GdiAddGlsBounds"); *(PROC *)&pfnGdiIsMetaPrintDC = GetProcAddress(hdll, "GdiIsMetaPrintDC"); #ifdef ALLOW_DDRAW_SURFACES hdll = GetModuleHandleA("ddraw"); ASSERTOPENGL(hdll != NULL, "Unable to get ddraw handle\n"); pfnGetSurfaceFromDC = (PFN_GETSURFACEFROMDC) GetProcAddress(hdll, "GetSurfaceFromDC"); #endif } #if DBG #define STR_OPENGL_DEBUG (PCSTR)"Software\\Microsoft\\Windows\\CurrentVersion\\DebugOpenGL" { HKEY hkDebug; // Initialize debugging level and flags. glDebugLevel = LEVEL_ERROR; glDebugFlags = 0; if ( RegOpenKeyExA(HKEY_LOCAL_MACHINE, STR_OPENGL_DEBUG, 0, KEY_QUERY_VALUE, &hkDebug) == ERROR_SUCCESS ) { DWORD dwDataType; DWORD cjSize; long lTmp; cjSize = sizeof(long); if ( (RegQueryValueExA(hkDebug, (LPSTR) "glDebugLevel", (LPDWORD) NULL, &dwDataType, (LPBYTE) &lTmp, &cjSize) == ERROR_SUCCESS) ) { glDebugLevel = lTmp; } cjSize = sizeof(long); if ( (RegQueryValueExA(hkDebug, (LPSTR) "glDebugFlags", (LPDWORD) NULL, &dwDataType, (LPBYTE) &lTmp, &cjSize) == ERROR_SUCCESS) ) { glDebugFlags = (ULONG) lTmp; } RegCloseKey(hkDebug); } } #endif #ifdef _CLIENTSIDE_ // Determine which platform we're running on and remember it osvi.dwOSVersionInfoSize = sizeof(osvi); if (!GetVersionEx(&osvi)) { WARNING1("GetVersionEx failed with %d\n", GetLastError()); goto EH_Fail; } dwPlatformId = osvi.dwPlatformId; if (!( (dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) || (dwPlatformId == VER_PLATFORM_WIN32_NT && !(osvi.dwMajorVersion == 3 && osvi.dwMinorVersion <= 51) ) ) ) { WARNING("DLL must be run on NT 4.0 or Win95"); goto EH_Fail; } // Allocate a thread local storage slot. if ( (dwTlsIndex = TlsAlloc()) == 0xFFFFFFFF ) { WARNING("DllInitialize: TlsAlloc failed\n"); goto EH_Fail; } // Set up the offset to the TLS slot, OS-specific if (dwPlatformId == VER_PLATFORM_WIN32_NT) { ASSERTOPENGL(FIELD_OFFSET(TEB, TlsSlots) == NT_TLS_OFFSET, "NT TLS offset not at expected location"); dwTlsOffset = dwTlsIndex*sizeof(DWORD_PTR)+NT_TLS_OFFSET; } #if !defined(_WIN64) else { // We don't have Win95's TIB type available so the assert is // slightly different ASSERTOPENGL(((ULONG_PTR)(NtCurrentTeb()->ThreadLocalStoragePointer)- (ULONG_PTR)NtCurrentTeb()) == WIN95_TLS_OFFSET, "Win95 TLS offset not at expected location"); dwTlsOffset = dwTlsIndex*sizeof(DWORD)+WIN95_TLS_OFFSET; } #endif #endif // Reserve memory for the local handle table. if ( (pLocalTable = (PLHE) VirtualAlloc ( (LPVOID) NULL, // let base locate it MAX_HANDLES*sizeof(LHE), MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE )) == (PLHE) NULL ) { WARNING("DllInitialize: VirtualAlloc failed\n"); goto EH_TlsIndex; } // Initialize the local handle manager semaphore. __try { INITIALIZECRITICALSECTION(&semLocal); } __except(EXCEPTION_EXECUTE_HANDLER) { goto EH_LocalTable; } #ifdef _CLIENTSIDE_ // Initialize the GLGENwindow list semaphore. __try { INITIALIZECRITICALSECTION(&gwndHeader.sem); } __except(EXCEPTION_EXECUTE_HANDLER) { goto EH_semLocal; } gwndHeader.pNext = &gwndHeader; // Initialize the pixel format critical section __try { INITIALIZECRITICALSECTION(&gcsPixelFormat); } __except(EXCEPTION_EXECUTE_HANDLER) { goto EH_gwndHeader; } // Initialize the palette watcher critical section. __try { INITIALIZECRITICALSECTION(&gcsPaletteWatcher); } __except(EXCEPTION_EXECUTE_HANDLER) { goto EH_PixelFormat; } // Initialize direct screen access. if (GetSystemMetrics(SM_CMONITORS) > 1) { gpScreenInfo = NULL; } else { #if _WIN32_WINNT >= 0x0501 BOOL wow64Process; if (IsWow64Process(GetCurrentProcess(), &wow64Process) && wow64Process) gpScreenInfo = NULL; else #endif gpScreenInfo = (SCREENINFO *)ALLOCZ(sizeof(SCREENINFO)); } if ( gpScreenInfo ) { UINT uiOldErrorMode; HRESULT hr; // We want to ensure that DDraw doesn't pop up any message // boxes on failure when we call DirectDrawCreate. DDraw // does errors on a different thread which it creates just // for the error. It waits for the error to complete before // returning. This function is running inside the loader // DllInitialize critical section, though, so other threads // do not get to run, causing a deadlock. // Force the error mode to get around this. uiOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); hr = DirectDrawCreate(NULL, &gpScreenInfo->pdd, NULL); SetErrorMode(uiOldErrorMode); if (hr == DD_OK) { hr = gpScreenInfo->pdd->lpVtbl-> SetCooperativeLevel(gpScreenInfo->pdd, NULL, DDSCL_NORMAL); if (hr == DD_OK) { gpScreenInfo->gdds.ddsd.dwSize = sizeof(DDSURFACEDESC); gpScreenInfo->gdds.ddsd.dwFlags = DDSD_CAPS; gpScreenInfo->gdds.ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; hr = gpScreenInfo->pdd->lpVtbl-> CreateSurface(gpScreenInfo->pdd, &gpScreenInfo->gdds.ddsd, &gpScreenInfo->gdds.pdds, NULL); } if (hr == DD_OK) { #if DBG #define LEVEL_SCREEN LEVEL_INFO gpScreenInfo->gdds.pdds->lpVtbl-> GetSurfaceDesc(gpScreenInfo->gdds.pdds, &gpScreenInfo->gdds.ddsd); DBGLEVEL (LEVEL_SCREEN, "=============================\n"); DBGLEVEL (LEVEL_SCREEN, "Direct screen access enabled for OpenGL\n\n"); DBGLEVEL (LEVEL_SCREEN, "Surface info:\n"); DBGLEVEL1(LEVEL_SCREEN, "\tdwSize = 0x%lx\n", gpScreenInfo->gdds.ddsd.dwSize); DBGLEVEL1(LEVEL_SCREEN, "\tdwWidth = %ld\n", gpScreenInfo->gdds.ddsd.dwWidth); DBGLEVEL1(LEVEL_SCREEN, "\tdwHeight = %ld\n", gpScreenInfo->gdds.ddsd.dwHeight); DBGLEVEL1(LEVEL_SCREEN, "\tlStride = 0x%lx\n", gpScreenInfo->gdds.ddsd.lPitch); DBGLEVEL1(LEVEL_SCREEN, "\tdwBitCount = %ld\n", gpScreenInfo->gdds.ddsd.ddpfPixelFormat.dwRGBBitCount); gpScreenInfo->gdds.pdds->lpVtbl-> Lock(gpScreenInfo->gdds.pdds, NULL, &gpScreenInfo->gdds.ddsd, DDLOCK_SURFACEMEMORYPTR, NULL); DBGLEVEL1(LEVEL_SCREEN, "\tdwOffSurface = 0x%lx\n", gpScreenInfo->gdds.ddsd.lpSurface); gpScreenInfo->gdds.pdds->lpVtbl-> Unlock(gpScreenInfo->gdds.pdds, gpScreenInfo->gdds.ddsd.lpSurface); DBGLEVEL (LEVEL_SCREEN, "=============================\n"); #endif // Verify screen access if (gpScreenInfo->gdds.pdds->lpVtbl-> GetSurfaceDesc(gpScreenInfo->gdds.pdds, &gpScreenInfo->gdds.ddsd) != DD_OK || gpScreenInfo->gdds.pdds->lpVtbl-> Lock(gpScreenInfo->gdds.pdds, NULL, &gpScreenInfo->gdds.ddsd, DDLOCK_SURFACEMEMORYPTR, NULL) != DD_OK) { DBGLEVEL(LEVEL_SCREEN, "Direct screen access failure : disabling\n"); } else { gpScreenInfo->gdds.dwBitDepth = DdPixDepthToCount(gpScreenInfo->gdds.ddsd. ddpfPixelFormat.dwRGBBitCount); gpScreenInfo->gdds.pdds->lpVtbl-> Unlock(gpScreenInfo->gdds.pdds, gpScreenInfo->gdds.ddsd.lpSurface); bDirectScreen = TRUE; } } #if DBG else { DBGLEVEL (LEVEL_SCREEN, "=============================\n"); DBGLEVEL2(LEVEL_SCREEN, "Screen access failed code 0x%08lX (%s)\n", hr, (hr == DDERR_NOTFOUND) ? "DDERR_NOTFOUND" : "unknown"); DBGLEVEL (LEVEL_SCREEN, "=============================\n"); } #endif } else { DBGLEVEL(LEVEL_SCREEN, "DirectDrawCreate failed\n"); } } if (!bDirectScreen) { if (gpScreenInfo) { if (gpScreenInfo->gdds.pdds) { gpScreenInfo->gdds.pdds->lpVtbl-> Release(gpScreenInfo->gdds.pdds); } if (gpScreenInfo->pdd) { gpScreenInfo->pdd->lpVtbl->Release(gpScreenInfo->pdd); } FREE(gpScreenInfo); gpScreenInfo = NULL; } } #endif // Set up our multiplication table: { BYTE *pMulTable = gbMulTable; ULONG i, j; for (i = 0; i < 256; i++) { ULONG tmp = 0; for (j = 0; j < 256; j++, tmp += i) { *pMulTable++ = (BYTE)(tmp >> 8); } } } // Set up our saturation table: { ULONG i; for (i = 0; i < 256; i++) gbSatTable[i] = (BYTE)i; for (; i < (256+256); i++) gbSatTable[i] = 255; } // Set up inverse-lookup table: { __GLfloat accum = (__GLfloat)(1.0 / (__GLfloat)__GL_VERTEX_FRAC_ONE); GLint i; invTable[0] = (__GLfloat)0.0; for (i = 1; i < INV_TABLE_SIZE; i++) { invTable[i] = __glOne / accum; accum += (__GLfloat)(1.0 / (__GLfloat)__GL_VERTEX_FRAC_ONE); } } bProcessInitialized = TRUE; return TRUE; EH_PixelFormat: DELETECRITICALSECTION(&gcsPixelFormat); EH_gwndHeader: DELETECRITICALSECTION(&gwndHeader.sem); EH_semLocal: DELETECRITICALSECTION(&semLocal); EH_LocalTable: VirtualFree(pLocalTable, 0, MEM_RELEASE); EH_TlsIndex: TlsFree(dwTlsIndex); dwTlsIndex = 0xFFFFFFFF; EH_Fail: return FALSE; } /******************************Public*Routine******************************\ * GLUnInitializeProcess * * Called from OPENGL32.DLL entry point for PROCESS_DETACH. * * History: * 01-Nov-1994 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ void GLUnInitializeProcess() { // If we never finished process initialization, quit now. if (!bProcessInitialized) return; // Cleanup stray HGLRCs that the app may have forgotten to delete. { static GLTEBINFO gltebInfoTmp; // Need a temporary GLTEBINFO for this thread in order to do the // cleanup processing. ASSERTOPENGL(!CURRENT_GLTEBINFO(), "GLUnInitializeProcess: GLTEBINFO not NULL!\n"); // made static and no longer need memset // memset(&gltebInfoTmp, 0, sizeof(gltebInfoTmp)); SET_CURRENT_GLTEBINFO(&gltebInfoTmp); vCleanupAllLRC(); SET_CURRENT_GLTEBINFO((PGLTEBINFO) NULL); } // Cleanup window tracking structures (GLGENwindow structs). vCleanupWnd(); // Cleanup evaluator arrays if (dBufFill) FREE(dBufFill); if (dBufTopLeft) FREE(dBufTopLeft); // Screen access shutdown. if (gpScreenInfo) { if (gpScreenInfo->gdds.pdds) { gpScreenInfo->gdds.pdds->lpVtbl->Release(gpScreenInfo->gdds.pdds); } if (gpScreenInfo->pdd) { gpScreenInfo->pdd->lpVtbl->Release(gpScreenInfo->pdd); } FREE(gpScreenInfo); } // Free the TLS slot. if (dwTlsIndex != 0xFFFFFFFF) if (!TlsFree(dwTlsIndex)) RIP("DllInitialize: TlsFree failed\n"); // Free the global semaphores. DELETECRITICALSECTION(&gcsPaletteWatcher); DELETECRITICALSECTION(&gcsPixelFormat); DELETECRITICALSECTION(&gwndHeader.sem); DELETECRITICALSECTION(&semLocal); // Free the local handle table. if ( pLocalTable ) VirtualFree(pLocalTable, 0, MEM_RELEASE); } /******************************Public*Routine******************************\ * GLInitializeThread * * Called from OPENGL32.DLL entry point for THREAD_ATTACH. May assume that * GLInitializeProcess has succeeded. * \**************************************************************************/ VOID GLInitializeThread(ULONG ulReason) { GLTEBINFO *pglti; GLMSGBATCHINFO *pMsgBatchInfo; POLYARRAY *pa; #if !defined(_WIN95_) && defined(_X86_) { TEB *pteb; pteb = NtCurrentTeb(); // Set up linear pointers to TEB regions in the TEB // this saves an addition when referencing these values // This must occur early so that these pointers are available // for the rest of thread initialization ((POLYARRAY *)pteb->glReserved1)->paTeb = (POLYARRAY *)pteb->glReserved1; pteb->glTable = pteb->glDispatchTable; } #endif pglti = (GLTEBINFO *)ALLOCZ(sizeof(GLTEBINFO)); SET_CURRENT_GLTEBINFO(pglti); if (pglti) { pa = GLTEB_CLTPOLYARRAY(); pa->flags = 0; // not in begin mode // Save shared section pointer in POLYARRAY for fast pointer access pa->pMsgBatchInfo = (PVOID) pglti->glMsgBatchInfo; pMsgBatchInfo = (GLMSGBATCHINFO *) pa->pMsgBatchInfo; pMsgBatchInfo->MaximumOffset = SHARED_SECTION_SIZE - GLMSG_ALIGN(sizeof(ULONG)); pMsgBatchInfo->FirstOffset = GLMSG_ALIGN(sizeof(GLMSGBATCHINFO)); pMsgBatchInfo->NextOffset = GLMSG_ALIGN(sizeof(GLMSGBATCHINFO)); SetCltProcTable(&glNullCltProcTable, &glNullExtProcTable, TRUE); GLTEB_SET_CLTCURRENTRC(NULL); GLTEB_SET_CLTPOLYMATERIAL(NULL); GLTEB_SET_CLTDRIVERSLOT(NULL); #if !defined(_WIN95_) ASSERTOPENGL((ULONG_PTR) pMsgBatchInfo == GLMSG_ALIGNPTR(pMsgBatchInfo), "bad shared memory alignment!\n"); #endif } else { // This can be made into a WARNING (debug builds only) later on. DbgPrint ("Memory alloc failed for TebInfo structure, thread may AV if GL calls are made without MakeCurrent\n"); } } /******************************Public*Routine******************************\ * GLUnInitializeThread * * Called from OPENGL32.DLL entry point for THREAD_DETACH. * * The server generic driver should cleanup on its own. Same for the * installable driver. * \**************************************************************************/ VOID GLUnInitializeThread(VOID) { // If we never finished process initialization, quit now. if (!bProcessInitialized) return; if (!CURRENT_GLTEBINFO()) { return; } if (GLTEB_CLTCURRENTRC() != NULL) { PLRC plrc = GLTEB_CLTCURRENTRC(); // May be an application error DBGERROR("GLUnInitializeThread: RC is current when thread exits\n"); // Release the RC plrc->tidCurrent = INVALID_THREAD_ID; plrc->gwidCurrent.iType = GLWID_ERROR; GLTEB_SET_CLTCURRENTRC(NULL); vUnlockHandle((ULONG_PTR)(plrc->hrc)); } // GLTEB_SET_CLTPROCTABLE(&glNullCltProcTable,&glNullExtProcTable); if (GLTEB_CLTPOLYMATERIAL()) FreePolyMaterial(); FREE(CURRENT_GLTEBINFO()); SET_CURRENT_GLTEBINFO(NULL); } /******************************Public*Routine******************************\ * DllInitialize * * This is the entry point for OPENGL32.DLL, which is called each time * a process or thread that is linked to it is created or terminated. * \**************************************************************************/ BOOL DllInitialize(HMODULE hModule, ULONG Reason, PVOID Reserved) { // Do the appropriate task for process and thread attach/detach. DBGLEVEL3(LEVEL_INFO, "DllInitialize: %s Pid %d, Tid %d\n", Reason == DLL_PROCESS_ATTACH ? "PROCESS_ATTACH" : Reason == DLL_PROCESS_DETACH ? "PROCESS_DETACH" : Reason == DLL_THREAD_ATTACH ? "THREAD_ATTACH" : Reason == DLL_THREAD_DETACH ? "THREAD_DETACH" : "Reason UNKNOWN!", GetCurrentProcessId(), GetCurrentThreadId()); switch (Reason) { case DLL_THREAD_ATTACH: case DLL_PROCESS_ATTACH: if (Reason == DLL_PROCESS_ATTACH) { if (!GLInitializeProcess()) return FALSE; } InterlockedIncrement(&lThreadsAttached); GLInitializeThread(Reason); break; case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: GLUnInitializeThread(); InterlockedDecrement(&lThreadsAttached); if ( Reason == DLL_PROCESS_DETACH ) { GLUnInitializeProcess(); } break; default: RIP("DllInitialize: unknown reason!\n"); break; } return(TRUE); }