
// @(#)root/gl:$Id: TGLContext.cxx 28662 2009-05-18 10:23:34Z rdm $ // Author: Timur Pocheptsov, Jun 2007 /************************************************************************* * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ #include <stdexcept> #include <algorithm> #include <memory> #include "TVirtualX.h" #include "GuiTypes.h" #include "TString.h" #include "TError.h" #include "TROOT.h" #include "TGLContextPrivate.h" #include "TGLIncludes.h" #include "TGLContext.h" #include "TGLWidget.h" #include "TGLFormat.h" #include "TGLUtil.h" #include "TGLFontManager.h" //______________________________________________________________________________ // // This class encapsulates window-system specific information about a // GL-context and alows their proper management in ROOT. ClassImp(TGLContext); //______________________________________________________________________________ TGLContext::TGLContext(TGLWidget *wid, Bool_t shareDefault, const TGLContext *shareList) : fDevice(wid), fPimpl(0), fFromCtor(kTRUE), fValid(kFALSE), fIdentity(0) { // TGLContext ctor "from" TGLWidget. // Is shareDefault is true, the shareList is set from default // context-identity. Otherwise the given shareList is used (can be // null). // Makes thread switching. if (shareDefault) shareList = TGLContextIdentity::GetDefaultContextAny(); if (!gVirtualX->IsCmdThread()) { gROOT->ProcessLineFast(Form("((TGLContext *)0x%lx)->SetContext((TGLWidget *)0x%lx, (TGLContext *)0x%lx)", this, wid, shareList)); } else SetContext(wid, shareList); if (shareDefault) fIdentity = TGLContextIdentity::GetDefaultIdentity(); else fIdentity = shareList ? shareList->GetIdentity() : new TGLContextIdentity; fIdentity->AddRef(this); fFromCtor = kFALSE; } //______________________________________________________________________________ /* TGLContext::TGLContext(TGLPBuffer *pbuff, const TGLContext *shareList) : fDevice(pbuff), fPimpl(0), fFromCtor(kTRUE), fValid(kFALSE) { if (!gVirtualX->IsCmdThread()) { gROOT->ProcessLineFast(Form("((TGLContext *)0x%lx)->SetContextPB((TGLPBuffer *)0x%lx, (TGLContext *)0x%lx)", this, pbuff, shareList)); } else SetContextPB(pbuff, shareList); fFromCtor = kFALSE; fValid = kTRUE; } */ //============================================================================== #ifdef WIN32 //============================================================================== namespace { struct LayoutCompatible_t { void *fDummy0; void *fDummy1; HWND *fPHwnd; unsigned char fDummy2; unsigned fDummy3; unsigned short fDummy4; unsigned short fDummy5; void *fDummy6; unsigned fDummy7:2; }; } //______________________________________________________________________________ void TGLContext::SetContext(TGLWidget *widget, const TGLContext *shareList) { //WIN32 gl-context creation. Defined as a member-function (this code removed from ctor) //to make WIN32/X11 separation cleaner. //This function is public only for calls via gROOT and called from ctor. if (!fFromCtor) { Error("TGLContext::SetContext", "SetContext must be called only from ctor"); return; } std::auto_ptr<TGLContextPrivate> safe_ptr(fPimpl = new TGLContextPrivate); LayoutCompatible_t *trick = reinterpret_cast<LayoutCompatible_t *>(widget->GetId()); HWND hWND = *trick->fPHwnd; HDC hDC = GetWindowDC(hWND); if (!hDC) { Error("TGLContext::SetContext", "GetWindowDC failed"); throw std::runtime_error("GetWindowDC failed"); } const Rgl::TGuardBase &dcGuard = Rgl::make_guard(ReleaseDC, hWND, hDC); if (HGLRC glContext = wglCreateContext(hDC)) { if (shareList && !wglShareLists(shareList->fPimpl->fGLContext, glContext)) { wglDeleteContext(glContext); Error("TGLContext::SetContext", "Context sharing failed!"); throw std::runtime_error("Context sharing failed"); } fPimpl->fHWND = hWND; fPimpl->fHDC = hDC; fPimpl->fGLContext = glContext; } else { Error("TGLContext::SetContext", "wglCreateContext failed"); throw std::runtime_error("wglCreateContext failed"); } //Register context for "parent" gl-device. fValid = kTRUE; fDevice->AddContext(this); TGLContextPrivate::RegisterContext(this); dcGuard.Stop(); safe_ptr.release(); } /* //______________________________________________________________________________ void TGLContext::SetContextPB(TGLPBuffer *pbuff, const TGLContext *shareList) { if (!fFromCtor) { Error("TGLContext::SetContextPB", "SetContextPB must be called only from ctor"); return; } std::auto_ptr<TGLContextPrivate> safe_ptr(fPimpl = new TGLContextPrivate); fPimpl->fHDC = pbuff->fPimpl->fHDC; fPimpl->fGLContext = pbuff->fPimpl->fGLRC; if (shareList && !wglShareLists(shareList->fPimpl->fGLContext, fPimpl->fGLContext)) Error("TGLContext::SetContextPV", "Cannot share lists"); fDevice->AddContext(this); safe_ptr.release(); } */ //______________________________________________________________________________ Bool_t TGLContext::MakeCurrent() { //If context is valid (TGLPaintDevice, for which context was created still exists), //make it current. if (!fValid) { Error("TGLContext::MakeCurrent", "This context is invalid."); return kFALSE; } if (!gVirtualX->IsCmdThread()) return Bool_t(gROOT->ProcessLineFast(Form("((TGLContext *)0x%lx)->MakeCurrent()", this))); else { Bool_t rez = wglMakeCurrent(fPimpl->fHDC, fPimpl->fGLContext); if (rez) fIdentity->DeleteGLResources(); return rez; } } //______________________________________________________________________________ Bool_t TGLContext::ClearCurrent() { //Reset current context. return wglMakeCurrent(0, 0); } //______________________________________________________________________________ void TGLContext::SwapBuffers() { //If context is valid (TGLPaintDevice, for which context was created still exists), //swap buffers (in case of P-buffer call glFinish()). if (!fValid) { Error("TGLContext::SwapBuffers", "This context is invalid."); return; } if (!gVirtualX->IsCmdThread()) gROOT->ProcessLineFast(Form("((TGLContext *)0x%lx)->SwapBuffers()", this)); else { if (fPimpl->fHWND) wglSwapLayerBuffers(fPimpl->fHDC, WGL_SWAP_MAIN_PLANE); else glFinish(); } } //______________________________________________________________________________ void TGLContext::Release() { //Make the context invalid and (do thread switch, if needed) //free resources. if (!gVirtualX->IsCmdThread()) { gROOT->ProcessLineFast(Form("((TGLContext *)0x%lx)->Release()", this)); return; } if (fPimpl->fHWND) ReleaseDC(fPimpl->fHWND, fPimpl->fHDC); TGLContextPrivate::RemoveContext(this); wglDeleteContext(fPimpl->fGLContext); fValid = kFALSE; } //============================================================================== #else // Non WIN32 //============================================================================== //______________________________________________________________________________ void TGLContext::SetContext(TGLWidget *widget, const TGLContext *shareList) { //X11 gl-context creation. Defined as a member-function (this code removed from ctor) //to make WIN32/X11 separation cleaner. //This function is public only for calls via gROOT and called from ctor. if (!fFromCtor) { Error("TGLContext::SetContext", "SetContext must be called only from ctor"); return; } std::auto_ptr<TGLContextPrivate> safe_ptr(fPimpl = new TGLContextPrivate); Display *dpy = static_cast<Display *>(widget->GetInnerData().first); XVisualInfo *visInfo = static_cast<XVisualInfo *>(widget->GetInnerData().second); GLXContext glCtx = shareList ? glXCreateContext(dpy, visInfo, shareList->fPimpl->fGLContext, True) : glXCreateContext(dpy, visInfo, None, True); if (!glCtx) { Error("TGLContext::SetContext", "glXCreateContext failed!"); throw std::runtime_error("glXCreateContext failed!"); } fPimpl->fDpy = dpy; fPimpl->fVisualInfo = visInfo; fPimpl->fGLContext = glCtx; fPimpl->fWindowID = widget->GetId(); fValid = kTRUE; fDevice->AddContext(this); TGLContextPrivate::RegisterContext(this); safe_ptr.release(); } /* //______________________________________________________________________________ void TGLContext::SetContextPB(TGLPBuffer *pbuff, const TGLContext *shareList) { std::auto_ptr<TGLContextPrivate> safe_ptr(fPimpl = new TGLContextPrivate); fPimpl->fDpy = pbuff->fPimpl->fDpy; fPimpl->fGLContext = pbuff->fPimpl->fPBRC; fPimpl->fPBDC = pbuff->fPimpl->fPBDC; fDevice->AddContext(this); safe_ptr.release(); } */ //______________________________________________________________________________ Bool_t TGLContext::MakeCurrent() { //If context is valid (TGLPaintDevice, for which context was created still exists), //make it current. if (!fValid) { Error("TGLContext::MakeCurrent", "This context is invalid."); return kFALSE; } if (fPimpl->fWindowID != 0) { const Bool_t rez = glXMakeCurrent(fPimpl->fDpy, fPimpl->fWindowID, fPimpl->fGLContext); if (rez) fIdentity->DeleteGLResources(); return rez; } return kFALSE;//NO pbuffer part yet. //return glXMakeCurrent(fPimpl->fDpy, fPimpl->fPBDC, fPimpl->fGLContext); } //______________________________________________________________________________ Bool_t TGLContext::ClearCurrent() { //Reset current context. return glXMakeCurrent(fPimpl->fDpy, None, 0); } //______________________________________________________________________________ void TGLContext::SwapBuffers() { //If context is valid (TGLPaintDevice, for which context was created still exists), //swap buffers (in case of P-buffer call glFinish()). if (!fValid) { Error("TGLContext::SwapCurrent", "This context is invalid."); return; } if (fPimpl->fWindowID != 0) glXSwapBuffers(fPimpl->fDpy, fPimpl->fWindowID); else glFinish(); } //______________________________________________________________________________ void TGLContext::Release() { //Make the context invalid and (do thread switch, if needed) //free resources. TGLContextPrivate::RemoveContext(this); glXDestroyContext(fPimpl->fDpy, fPimpl->fGLContext); fValid = kFALSE; } //============================================================================== #endif //============================================================================== //______________________________________________________________________________ TGLContext::~TGLContext() { //TGLContext dtor. If it's called before TGLPaintDevice's dtor //(context is valid) resource will be freed and context //un-registered. if (fValid) { Release(); fDevice->RemoveContext(this); } fIdentity->Release(this); delete fPimpl; } //______________________________________________________________________________ TGLContextIdentity *TGLContext::GetIdentity()const { //We can have several shared contexts, //and gl-scene wants to know, if some context //(defined by its identity) can be used. return fIdentity; } //______________________________________________________________________________ TGLContext *TGLContext::GetCurrent() { //Ask TGLContextPrivate to lookup context in its internal map. return TGLContextPrivate::GetCurrentContext(); } //______________________________________________________________________________ // // Identifier of a shared GL-context. // Objects shared among GL-contexts include: // display-list definitions, texture objects and shader programs. ClassImp(TGLContextIdentity) TGLContextIdentity* TGLContextIdentity::fgDefaultIdentity = new TGLContextIdentity; //______________________________________________________________________________ TGLContextIdentity::TGLContextIdentity(): fFontManager(0), fCnt(0), fClientCnt(0) { // Constructor. } //______________________________________________________________________________ TGLContextIdentity::~TGLContextIdentity() { // Destructor. if (fFontManager) delete fFontManager; } //______________________________________________________________________________ void TGLContextIdentity::AddRef(TGLContext* ctx) { //Add context ctx to the list of references. ++fCnt; fCtxs.push_back(ctx); } //______________________________________________________________________________ void TGLContextIdentity::Release(TGLContext* ctx) { //Remove context ctx from the list of references. CtxList_t::iterator i = std::find(fCtxs.begin(), fCtxs.end(), ctx); if (i != fCtxs.end()) { fCtxs.erase(i); --fCnt; CheckDestroy(); } else { Error("TGLContextIdentity::Release", "unregistered context."); } } //______________________________________________________________________________ void TGLContextIdentity::RegisterDLNameRangeToWipe(UInt_t base, Int_t size) { //Remember dl range for deletion in next MakeCurrent or dtor execution. fDLTrash.push_back(DLRange_t(base, size)); } //______________________________________________________________________________ void TGLContextIdentity::DeleteGLResources() { //Delete GL resources registered for destruction. if (!fDLTrash.empty()) { for (DLTrashIt_t it = fDLTrash.begin(), e = fDLTrash.end(); it != e; ++it) glDeleteLists(it->first, it->second); fDLTrash.clear(); } if (fFontManager) fFontManager->ClearFontTrash(); } //______________________________________________________________________________ TGLContextIdentity* TGLContextIdentity::GetCurrent() { //Find identitfy of current context. Static. TGLContext* ctx = TGLContext::GetCurrent(); return ctx ? ctx->GetIdentity() : 0; } //______________________________________________________________________________ TGLContextIdentity* TGLContextIdentity::GetDefaultIdentity() { //Get identity of a default Gl context. Static. if (fgDefaultIdentity == 0) fgDefaultIdentity = new TGLContextIdentity; return fgDefaultIdentity; } //______________________________________________________________________________ TGLContext* TGLContextIdentity::GetDefaultContextAny() { //Get the first GL context with the default identity. //Can return zero, but that's OK, too. Static. if (fgDefaultIdentity == 0 || fgDefaultIdentity->fCtxs.empty()) return 0; return fgDefaultIdentity->fCtxs.front(); } //______________________________________________________________________________ TGLFontManager* TGLContextIdentity::GetFontManager() { //Get the free-type font-manager associated with this context-identity. if(!fFontManager) fFontManager = new TGLFontManager(); return fFontManager; } //______________________________________________________________________________ void TGLContextIdentity::CheckDestroy() { //Private function called when reference count is reduced. if (fCnt <= 0 && fClientCnt <= 0) { if (this == fgDefaultIdentity) fgDefaultIdentity = 0; delete this; } }