本文接着osg窗口的创建(一)中关于GraphicsContext的内容继续阅读。
还是先看看GraphicsContext中的继承图
由于是在Windows平台下分析窗口的创建,因此本文选取Win32中的实现来探讨,其他平台的原理类似,只是在细节上稍有不同。
前文说到了 isRealized的实现,我们在看看实现过程:
GraphicsContext的isRealized实现如下:
inline bool isRealized() const
{
return isRealizedImplementation();
}
isRealizedImplementation函数在GraphicsContext中是一个纯虚函数,在GraphicsWindow中的实现仅仅打印出一个提示信息(等同于没实现),真正的实现在GraphicsWindowWin32之中,实现如下:
virtual bool GraphicsWindowWin32::isRealizedImplementation() const
{
return _realized;
}
仅仅是返回一个标识,于是我们有理由认为在 realize函数中设置了该标识。(实际上确实是这样的,后续再realize代码中再分析)
继续看frame中的实现:
if (!isRealized())
{
realize();
}
可以看到如果没有设置好窗口(isRealized返回值为false),那么就调用 realize函数来设置窗口,事实上osg的窗口创建过程就是在realize函数中完成的,在代码调试中到断点越过realize函数体,可以在任务栏上看到新的窗口出现。realize的详细实现如下:
1. realize在ViewerBase中是纯虚函数
2. realize的具体实现在osgViewer::Viewer类中实现:
void Viewer::realize()
{
Contexts contexts;
getContexts(contexts);
if (contexts.empty())
{
OSG_INFO<<"Viewer::realize() - No valid contexts found, setting up view across all screens."<<std::endl;
const char* ptr = 0;
if ((ptr = getenv("OSG_CONFIG_FILE")) != 0)
{
readConfiguration(ptr);
}
else
{
int screenNum = -1;
if ((ptr = getenv("OSG_SCREEN")) != 0)
{
if (strlen(ptr)!=0) screenNum = atoi(ptr);
else screenNum = -1;
}
int x = -1, y = -1, width = -1, height = -1;
if ((ptr = getenv("OSG_WINDOW")) != 0)
{
std::istringstream iss(ptr);
iss >> x >> y >> width >> height;
}
if (width>0 && height>0)
{
if (screenNum>=0) setUpViewInWindow(x, y, width, height, screenNum);
else setUpViewInWindow(x,y,width,height);
}
else if (screenNum>=0)
{
setUpViewOnSingleScreen(screenNum);
}
else
{
setUpViewAcrossAllScreens();
}
}
getContexts(contexts);
}
if (contexts.empty())
{
OSG_NOTICE<<"Viewer::realize() - failed to set up any windows"<<std::endl;
_done = true;
return;
}
unsigned int maxTexturePoolSize = osg::DisplaySettings::instance()->getMaxTexturePoolSize();
if (_camera->getDisplaySettings()) maxTexturePoolSize = std::max(maxTexturePoolSize, _camera->getDisplaySettings()->getMaxTexturePoolSize());
if (_displaySettings.valid()) maxTexturePoolSize = std::max(maxTexturePoolSize, _displaySettings->getMaxTexturePoolSize());
unsigned int maxBufferObjectPoolSize = osg::DisplaySettings::instance()->getMaxBufferObjectPoolSize();
if (_displaySettings.valid()) maxBufferObjectPoolSize = std::max(maxBufferObjectPoolSize, _displaySettings->getMaxBufferObjectPoolSize());
if (_camera->getDisplaySettings()) maxBufferObjectPoolSize = std::max(maxBufferObjectPoolSize, _camera->getDisplaySettings()->getMaxBufferObjectPoolSize());
for(Contexts::iterator citr = contexts.begin();
citr != contexts.end();
++citr)
{
osg::GraphicsContext* gc = *citr;
// set the pool sizes, 0 the default will result in no GL object pools.
gc->getState()->setMaxTexturePoolSize(maxTexturePoolSize);
gc->getState()->setMaxBufferObjectPoolSize(maxBufferObjectPoolSize);
gc->realize();
if (_realizeOperation.valid() && gc->valid())
{
gc->makeCurrent();
(*_realizeOperation)(gc);
gc->releaseContext();
}
}
// attach contexts to _incrementalCompileOperation if attached.
if (_incrementalCompileOperation) _incrementalCompileOperation->assignContexts(contexts);
bool grabFocus = true;
if (grabFocus)
{
for(Contexts::iterator citr = contexts.begin();
citr != contexts.end();
++citr)
{
osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(*citr);
if (gw)
{
gw->grabFocusIfPointerInWindow();
}
}
}
// initialize the global timer to be relative to the current time.
osg::Timer::instance()->setStartTick();
// pass on the start tick to all the associated event queues
setStartTick(osg::Timer::instance()->getStartTick());
setUpThreading();
if (osg::DisplaySettings::instance()->getCompileContextsHint())
{
int numProcessors = OpenThreads::GetNumberOfProcessors();
int processNum = 0;
for(unsigned int i=0; i<= osg::GraphicsContext::getMaxContextID(); ++i)
{
osg::GraphicsContext* gc = osg::GraphicsContext::getOrCreateCompileContext(i);
if (gc)
{
gc->createGraphicsThread();
gc->getGraphicsThread()->setProcessorAffinity(processNum % numProcessors);
gc->getGraphicsThread()->startThread();
++processNum;
}
}
}
}
函数体的实现代码相对较长,分段进行分析:
1. 首先收集当前视景器Viewer中相机的渲染上下文,代码和isRealized中的实现一样
2. 如果不存在渲染上下文,我们进入第一段if判断中,可以看到主要进行的工作如下:
- 读取环境变量OSG_CONFIG_FILE,如果设置了OSG_CONFIG_FILE指向一个.view后缀的文件,可以创建该文件描述的窗口,格式如下:(在openscenegraph-data目录中的configuration目录下有.view类型的文件)
osgViewer::Viewer
{
setUpViewInWindow 100 200 600 400 0
}
- 如果没有设置OSG_CONFIG_FILE环境变量,那么程序会检查 OSG_SCREEN和OSG_WINDOW这两个环境变量的值,OSG_SCREEN对应窗口的个数(值是一个整型数)OSG_WINDOW对应窗口的大小和位置,格式是(x, y, w, h) 分别是窗口左上角点坐标(x, y)以及窗口的长和宽(w, h),具体来说是以下情况:
Screen取值 | Window中width和height取值 | 窗口设置函数 |
大于或等于0 | 长和宽都大于0 | setUpViewInWindow(x, y, width, height, screenNum) |
小于0 | 长和宽都大于0 | setUpViewInWindow(x, y, width, height) |
大于0 | 长和宽为0或者负值 | setUpViewOnSingleScreen(screenNum) |
这三个函数的实现如下所示:
void View::setUpViewAcrossAllScreens()
{
apply(new osgViewer::AcrossAllScreens());
}
void View::setUpViewInWindow(int x, int y, int width, int height, unsigned int screenNum)
{
apply(new osgViewer::SingleWindow(x, y, width, height, screenNum));
}
void View::setUpViewOnSingleScreen(unsigned int screenNum)
{
apply(new osgViewer::SingleScreen(screenNum));
}
这桑函数调用了osgViewer命名空间中的三个窗口配置类,它们的关系如下图所示:
基类osgViewer::ViewConfig提供了一个虚函数 virtual void configure (osgViewer::View &) const,在子类中通过实现该函数来配置渲染的窗口大小和位置。
SingleScreen在实现过程中会调用SingleWindow来完成设置,AcrossAllScreen在未设置窗口的大小和显示屏数目的时候会被调用(一般就是我们默认使用osg的场景),那么它会调用SingleWindow来设置,在设置过程中会读取外接显示器的分辨率,产生一个全屏的窗口,代码如下:
//来自于osgViewer::SingleWindow::configure
if (traits->width<=0 || traits->height<=0 )
{
osg::GraphicsContext::ScreenIdentifier si;
si.readDISPLAY();
// displayNum has not been set so reset it to 0.
if (si.displayNum<0) si.displayNum = 0;
si.screenNum = _screenNum;
unsigned int width, height;
wsi->getScreenResolution(si, width, height);
if (traits->width<=0) traits->width = width;
if (traits->height<=0) traits->height = height;
}
下面继续realize后面的代码,在完成窗口参数的设置之后,后面的代码无非是一些关于OpenGL缓冲区对象的设置等,暂时与窗口无关。在此之后进入到for循环之中,这里面就是窗口设置的内容了,实现的内容是对所有的相机中的设备上下文进行检查,并真正的产生窗口,窗口的产生调用在
gc->realize();
我们查看一下它的实现:
bool GraphicsContext::realize()
{
if (realizeImplementation())
{
return true;
}
else
{
return false;
}
}
实现中调用了另一个函数,继续往下看,它与isRealizedImplementation函数类似,在GraphicsContext中是一个纯虚函数,在窗口系统中实现相应的代码:
bool GraphicsWindowWin32::realizeImplementation()
{
if (_realized) return true;
if (!_initialized)
{
init();
if (!_initialized) return false;
}
if (_traits.valid() && (_traits->sharedContext.valid() || _traits->vsync || _traits->swapGroupEnabled))
{
// make context current so we can test capabilities and set up context sharing
struct RestoreContext
{
RestoreContext()
{
_hdc = wglGetCurrentDC();
_hglrc = wglGetCurrentContext();
}
~RestoreContext()
{
wglMakeCurrent(_hdc,_hglrc);
}
protected:
HDC _hdc;
HGLRC _hglrc;
} restoreContext;
_realized = true;
bool result = makeCurrent();
_realized = false;
if (!result)
{
return false;
}
// set up sharing of contexts if required
GraphicsHandleWin32* graphicsHandleWin32 = dynamic_cast<GraphicsHandleWin32*>(_traits->sharedContext.get());
if (graphicsHandleWin32)
{
if (!wglShareLists(graphicsHandleWin32->getWGLContext(), getWGLContext()))
{
reportErrorForScreen("GraphicsWindowWin32::realizeImplementation() - Unable to share OpenGL context", _traits->screenNum, ::GetLastError());
return false;
}
}
// if vysnc should be on then enable it.
if (_traits->vsync)
{
setSyncToVBlank(_traits->vsync);
}
// If the swap group is active then enable it.
if (_traits->swapGroupEnabled)
{
setSwapGroup(_traits->swapGroupEnabled, _traits->swapGroup, _traits->swapBarrier);
}
}
if (_ownsWindow)
{
//
// Bring the window on top of other ones (including the taskbar if it covers it completely)
//
// NOTE: To cover the taskbar with a window that does not completely cover it, the HWND_TOPMOST
// Z-order must be used in the code below instead of HWND_TOP.
// @todo: This should be controlled through a flag in the traits (topMostWindow)
//
if (!::SetWindowPos(_hwnd,
HWND_TOP,
_windowOriginXToRealize,
_windowOriginYToRealize,
_windowWidthToRealize,
_windowHeightToRealize,
SWP_SHOWWINDOW))
{
reportErrorForScreen("GraphicsWindowWin32::realizeImplementation() - Unable to show window", _traits->screenNum, ::GetLastError());
return false;
}
if (!::UpdateWindow(_hwnd))
{
reportErrorForScreen("GraphicsWindowWin32::realizeImplementation() - Unable to update window", _traits->screenNum, ::GetLastError());
return false;
}
}
_realized = true;
// make sure the event queue has the correct window rectangle size and input range
getEventQueue()->syncWindowRectangleWithGraphcisContext();
return true;
}
可以看到窗口的产生是在这个函数中,在Win32程序设计中的常用函数UpdateWindow与SetWindowPos就是在这里调用的,SetWindowPos最终将窗口Show出来,让我们在系统中可以看到创建好的场景。