windows-nt/Source/XPSP1/NT/multimedia/opengl/client/dllinit.c
2020-09-26 16:20:57 +08:00

799 lines
22 KiB
C

/******************************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);
}