接前一文,來看WMesaMakeCurrent函數在調用_mesa_make_current之後所發生的事情:
_mesa_make_current// context.c 從第1310行到第1436行GLboolean_mesa_make_current( GLcontext *newCtx, GLframebuffer *drawBuffer, GLframebuffer *readBuffer ){ if (MESA_VERBOSE & VERBOSE_API) _mesa_debug(newCtx, "_mesa_make_current()\n"); /* Check that the context's and framebuffer's visuals are compatible. */ if (newCtx && drawBuffer && newCtx->WinSysDrawBuffer != drawBuffer) { if (!check_compatible(newCtx, drawBuffer)) { _mesa_warning(newCtx, "MakeCurrent: incompatible visuals for context and drawbuffer"); return GL_FALSE; } } if (newCtx && readBuffer && newCtx->WinSysReadBuffer != readBuffer) { if (!check_compatible(newCtx, readBuffer)) { _mesa_warning(newCtx, "MakeCurrent: incompatible visuals for context and readbuffer"); return GL_FALSE; } } /* We used to call _glapi_check_multithread() here. Now do it in drivers */ _glapi_set_context((void *) newCtx); ASSERT(_mesa_get_current_context() == newCtx); if (!newCtx) { _glapi_set_dispatch(NULL); /* none current */ } else { _glapi_set_dispatch(newCtx->CurrentDispatch); if (drawBuffer && readBuffer) { /* TODO: check if newCtx and buffer's visual match??? */ ASSERT(drawBuffer->Name == 0); ASSERT(readBuffer->Name == 0); _mesa_reference_framebuffer(&newCtx->WinSysDrawBuffer, drawBuffer); _mesa_reference_framebuffer(&newCtx->WinSysReadBuffer, readBuffer); /* * Only set the context's Draw/ReadBuffer fields if they're NULL * or not bound to a user-created FBO. */ if (!newCtx->DrawBuffer || newCtx->DrawBuffer->Name == 0) { /* KW: merge conflict here, revisit. */ /* fix up the fb fields - these will end up wrong otherwise * if the DRIdrawable changes, and everything relies on them. * This is a bit messy (same as needed in _mesa_BindFramebufferEXT) */ unsigned int i; GLenum buffers[MAX_DRAW_BUFFERS]; _mesa_reference_framebuffer(&newCtx->DrawBuffer, drawBuffer); for(i = 0; i < newCtx->Const.MaxDrawBuffers; i++) { buffers[i] = newCtx->Color.DrawBuffer[i]; } _mesa_drawbuffers(newCtx, newCtx->Const.MaxDrawBuffers, buffers, NULL); } if (!newCtx->ReadBuffer || newCtx->ReadBuffer->Name == 0) { _mesa_reference_framebuffer(&newCtx->ReadBuffer, readBuffer); } /* XXX only set this flag if we're really changing the draw/read * framebuffer bindings. */ newCtx->NewState |= _NEW_BUFFERS;#if 1 /* We want to get rid of these lines: */#if _HAVE_FULL_GL if (!drawBuffer->Initialized) { initialize_framebuffer_size(newCtx, drawBuffer); } if (readBuffer != drawBuffer && !readBuffer->Initialized) { initialize_framebuffer_size(newCtx, readBuffer); } _mesa_resizebuffers(newCtx);#endif#else /* We want the drawBuffer and readBuffer to be initialized by * the driver. * This generally means the Width and Height match the actual * window size and the renderbuffers (both hardware and software * based) are allocated to match. The later can generally be * done with a call to _mesa_resize_framebuffer(). * * It's theoretically possible for a buffer to have zero width * or height, but for now, assert check that the driver did what's * expected of it. */ ASSERT(drawBuffer->Width > 0); ASSERT(drawBuffer->Height > 0);#endif if (drawBuffer) { _mesa_check_init_viewport(newCtx, drawBuffer->Width, drawBuffer->Height); } } if (newCtx->FirstTimeCurrent) { check_context_limits(newCtx); /* We can use this to help debug user's problems. Tell them to set * the MESA_INFO env variable before running their app. Then the * first time each context is made current we'll print some useful * information. */ if (_mesa_getenv("MESA_INFO")) { _mesa_print_info(); } newCtx->FirstTimeCurrent = GL_FALSE; } } return GL_TRUE;}
第1335行調用glapi.c裡_glapi_set_context函數設定當前的ctx,即設定工程全域變數_glapi_Context的值指向context,在wmesa_context結構成員中首成員即GLcontext,所以二者的指標地址是相同的;
第1342行調用glapi.c裡_glapi_set_dispatch設定當前的函數控制代碼,即設定工程全域變數_glapi_Dispatch指標指向。
在glapitable.h裡定義了_glapi_table結構,它包括所有的OpenGL發布函數gl*函數指標成員變數,在gl*系列發布函數代碼體基本上都是擷取當前_glapi_Dispatch成員地址,調用該成員所指向的函數指標地址。而這些成員的賦值是在_mesa_initialize_context函數執行時所做的。而這裡調用_glapi_set_dispatch將_glapi_Dispatch與具體的對應函數地址對應起來。
若沒有調用_mesa_make_current來關聯二者之前,_glapi_Dispatch初始指向__glapi_noop_table,而且如果使用NULL來調用_mesa_make_current也會使得_glapi_Dispatch指向__glapi_noop_table。
檔案域全域變數__glapi_noop_table是在glapi.c裡通過包含glapitemp.h定義宏替換來聲明定義的,該宏替換一方面定義了一組空的只警告又不做什麼的gl*函數的翻版,起頭是Noop*,一如它們的功能一樣,然後將這些函數指標收集起來即__glapi_noop_table變數的值,顯然如果使用者沒有在事前調用wglMakeCurrent關聯到可用的hglrc的話,那麼就是不應該使用gl*系列的函數的。
這裡暫不關注對幀緩衝區的相關操作,一時半會還關注不過來。
現在回過頭來看GLcontext結構中與_glapi_table有關的成員,以及它們的初始化賦值:
// mtypes.h /*2893*/ struct __GLcontextRec/*2894*/ { // ....../*2898*/ /** \name API function pointer tables *//*2899*/ /*@{*//*2900*/ struct _glapi_table *Save;/**< Display list save functions *//*2901*/ struct _glapi_table *Exec;/**< Execute functions *//*2902*/ struct _glapi_table *CurrentDispatch; /**< == Save or Exec !! *//*2903*/ /*@}*/ // ....../*3094*/ };
__GLcontextRec即GLcontext結構,成員Save和Exec均在_mesa_initialize_context時初始化,有許多gl*發布函數均在這裡設定對應的函數指標地址,以下代碼嘗試弄清楚該函數所做的具體工作:
_mesa_initialize_context// context.c 從第843行起至933行GLboolean_mesa_initialize_context(GLcontext *ctx, const GLvisual *visual, GLcontext *share_list, const struct dd_function_table *driverFunctions, void *driverContext){ struct gl_shared_state *shared; /*ASSERT(driverContext);*/ assert(driverFunctions->NewTextureObject); assert(driverFunctions->FreeTexImageData); /* misc one-time initializations */ one_time_init(ctx); ctx->Visual = *visual; ctx->DrawBuffer = NULL; ctx->ReadBuffer = NULL; ctx->WinSysDrawBuffer = NULL; ctx->WinSysReadBuffer = NULL; /* Plug in driver functions and context pointer here. * This is important because when we call alloc_shared_state() below * we'll call ctx->Driver.NewTextureObject() to create the default * textures. */ ctx->Driver = *driverFunctions; ctx->DriverCtx = driverContext; if (share_list) { /* share state with another context */ shared = share_list->Shared; } else { /* allocate new, unshared state */ shared = _mesa_alloc_shared_state(ctx); if (!shared) return GL_FALSE; } _glthread_LOCK_MUTEX(shared->Mutex); ctx->Shared = shared; shared->RefCount++; _glthread_UNLOCK_MUTEX(shared->Mutex); if (!init_attrib_groups( ctx )) { _mesa_free_shared_state(ctx, ctx->Shared); return GL_FALSE; } /* setup the API dispatch tables */ ctx->Exec = alloc_dispatch_table(); ctx->Save = alloc_dispatch_table(); if (!ctx->Exec || !ctx->Save) { _mesa_free_shared_state(ctx, ctx->Shared); if (ctx->Exec) _mesa_free(ctx->Exec); return GL_FALSE; }#if FEATURE_dispatch _mesa_init_exec_table(ctx->Exec);#endif ctx->CurrentDispatch = ctx->Exec;#if FEATURE_dlist _mesa_init_dlist_table(ctx->Save); _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt );#endif /* Neutral tnl module stuff */ _mesa_init_exec_vtxfmt( ctx ); ctx->TnlModule.Current = NULL; ctx->TnlModule.SwapCount = 0; ctx->FragmentProgram._MaintainTexEnvProgram = (_mesa_getenv("MESA_TEX_PROG") != NULL); ctx->VertexProgram._MaintainTnlProgram = (_mesa_getenv("MESA_TNL_PROG") != NULL); if (ctx->VertexProgram._MaintainTnlProgram) { /* this is required... */ ctx->FragmentProgram._MaintainTexEnvProgram = GL_TRUE; }#ifdef FEATURE_extra_context_init _mesa_initialize_context_extra(ctx);#endif ctx->FirstTimeCurrent = GL_TRUE; return GL_TRUE;}
第895行,申請_glapi_table結構,並以警告函數指標初始化,賦給ctx->Exec。而第896行與此一樣;
第904行,調用在api_exec.c裡第151行的_mesa_init_exec_table函數設定ctx->Exec各成員指標指向對應的工程函數;
第905行,設定當前的函數分發表指向ctx->Exec;
第908行、第909行都是對ctx->Save的初始化,暫時不去解析;
第912行則是調用在vtxfmt.c裡的_mesa_init_exec_vtxfmt函數設定ctx->Exec中對應GLvertexformat結構的成員函數指標初始化;
GLvertexformat結構在dd.h標頭檔裡定義,它所包含的函數都是有可能在glBegin()/glEnd()之間使用的函數。
neutral_vtxfmt是vtxfmt.c定義宏定義包含vtxfmt_tmp.h而得到的檔案域全域變數,這些函數的代碼體都差不多,估計是與tnl模組的互動;
第913行與914行初始化表明vtxfmt與tnl模組之間還沒有交換過成員值。