/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace osg; ///////////////////////////////////////////////////////////////////////////// // // WindowSystemInterfaces // GraphicsContext::WindowingSystemInterfaces::WindowingSystemInterfaces() { } GraphicsContext::WindowingSystemInterfaces::~WindowingSystemInterfaces() { } void GraphicsContext::WindowingSystemInterfaces::addWindowingSystemInterface(GraphicsContext::WindowingSystemInterface* wsi) { if (std::find(_interfaces.begin(), _interfaces.end(), wsi)==_interfaces.end()) { _interfaces.push_back(wsi); } } void GraphicsContext::WindowingSystemInterfaces::removeWindowingSystemInterface(GraphicsContext::WindowingSystemInterface* wsi) { Interfaces::iterator itr = std::find(_interfaces.begin(), _interfaces.end(), wsi); if (itr!=_interfaces.end()) { _interfaces.erase(itr); } } GraphicsContext::WindowingSystemInterface* GraphicsContext::WindowingSystemInterfaces::getWindowingSystemInterface(const std::string& name) { if (_interfaces.empty()) { OSG_WARN<<"Warning: GraphicsContext::WindowingSystemInterfaces::getWindowingSystemInterface() failed, no interfaces available."<getName()==name) { return itr->get(); } OSG_NOTICE<<" tried interface "<& GraphicsContext::getWindowingSystemInterfaces() { static ref_ptr s_WindowingSystemInterface = new GraphicsContext::WindowingSystemInterfaces; return s_WindowingSystemInterface; } OSG_INIT_SINGLETON_PROXY(ProxyInitWindowingSystemInterfaces, GraphicsContext::getWindowingSystemInterfaces()) // GraphicsContext static method implementations GraphicsContext::WindowingSystemInterface* GraphicsContext::getWindowingSystemInterface(const std::string& name) { return GraphicsContext::getWindowingSystemInterfaces()->getWindowingSystemInterface(name); } GraphicsContext* GraphicsContext::createGraphicsContext(Traits* traits) { ref_ptr wsref = getWindowingSystemInterface(traits ? traits->windowingSystemPreference : "") ; if ( wsref.valid()) { // catch any undefined values. if (traits) traits->setUndefinedScreenDetailsToDefaultScreen(); return wsref->createGraphicsContext(traits); } else return 0; } GraphicsContext::ScreenIdentifier::ScreenIdentifier(): displayNum(0), screenNum(0) {} GraphicsContext::ScreenIdentifier::ScreenIdentifier(int in_screenNum): displayNum(0), screenNum(in_screenNum) {} GraphicsContext::ScreenIdentifier::ScreenIdentifier(const std::string& in_hostName,int in_displayNum, int in_screenNum): hostName(in_hostName), displayNum(in_displayNum), screenNum(in_screenNum) {} std::string GraphicsContext::ScreenIdentifier::displayName() const { std::stringstream ostr; ostr<getMinimumNumAlphaBits(); stencil = ds->getMinimumNumStencilBits(); if (ds->getMultiSamples()!=0) sampleBuffers = 1; samples = ds->getNumMultiSamples(); if (ds->getStereo()) { switch(ds->getStereoMode()) { case(osg::DisplaySettings::QUAD_BUFFER): quadBufferStereo = true; break; case(osg::DisplaySettings::VERTICAL_INTERLACE): case(osg::DisplaySettings::CHECKERBOARD): case(osg::DisplaySettings::HORIZONTAL_INTERLACE): stencil = 8; break; default: break; } } glContextVersion = ds->getGLContextVersion(); glContextFlags = ds->getGLContextFlags(); glContextProfileMask = ds->getGLContextProfileMask(); swapMethod = ds->getSwapMethod(); } } bool GraphicsContext::Traits::getContextVersion(unsigned int& major, unsigned int& minor) const { if (glContextVersion.empty()) return false; std::istringstream istr( glContextVersion ); unsigned char dot; istr >> major >> dot >> minor; return true; } #if 0 class ContextData { public: ContextData(): _numContexts(0) {} unsigned int _numContexts; void incrementUsageCount() { ++_numContexts; } void decrementUsageCount() { --_numContexts; OSG_INFO<<"decrementUsageCount()"<<_numContexts<referenceCount()< _compileContext; }; #endif unsigned int GraphicsContext::createNewContextID() { return ContextData::createNewContextID(); } unsigned int GraphicsContext::getMaxContextID() { return ContextData::getMaxContextID(); } void GraphicsContext::incrementContextIDUsageCount(unsigned int contextID) { return ContextData::incrementContextIDUsageCount(contextID); } void GraphicsContext::decrementContextIDUsageCount(unsigned int contextID) { return ContextData::decrementContextIDUsageCount(contextID); } void GraphicsContext::registerGraphicsContext(GraphicsContext* gc) { ContextData::registerGraphicsContext(gc); } void GraphicsContext::unregisterGraphicsContext(GraphicsContext* gc) { ContextData::unregisterGraphicsContext(gc); } GraphicsContext::GraphicsContexts GraphicsContext::getAllRegisteredGraphicsContexts() { return ContextData::getAllRegisteredGraphicsContexts(); } GraphicsContext::GraphicsContexts GraphicsContext::getRegisteredGraphicsContexts(unsigned int contextID) { return ContextData::getRegisteredGraphicsContexts(contextID); } GraphicsContext* GraphicsContext::getOrCreateCompileContext(unsigned int contextID) { return ContextData::getOrCreateCompileContext(contextID); } void GraphicsContext::setCompileContext(unsigned int contextID, GraphicsContext* gc) { return ContextData::setCompileContext(contextID, gc); } GraphicsContext* GraphicsContext::getCompileContext(unsigned int contextID) { return ContextData::getCompileContext(contextID); } ///////////////////////////////////////////////////////////////////////////// // // GraphicsContext standard method implementations // GraphicsContext::GraphicsContext(): _clearColor(osg::Vec4(0.0f,0.0f,0.0f,1.0f)), _clearMask(0), _threadOfLastMakeCurrent(0), _lastClearTick(0), _defaultFboId(0) { setThreadSafeRefUnref(true); _operationsBlock = new RefBlock; registerGraphicsContext(this); } GraphicsContext::GraphicsContext(const GraphicsContext&, const osg::CopyOp&): _clearColor(osg::Vec4(0.0f,0.0f,0.0f,1.0f)), _clearMask(0), _threadOfLastMakeCurrent(0), _lastClearTick(0), _defaultFboId(0) { setThreadSafeRefUnref(true); _operationsBlock = new RefBlock; registerGraphicsContext(this); } GraphicsContext::~GraphicsContext() { close(false); unregisterGraphicsContext(this); } void GraphicsContext::clear() { _lastClearTick = osg::Timer::instance()->tick(); if (_clearMask==0 || !_traits) return; glViewport(0, 0, _traits->width, _traits->height); glScissor(0, 0, _traits->width, _traits->height); glClearColor( _clearColor[0], _clearColor[1], _clearColor[2], _clearColor[3]); glClear( _clearMask ); } bool GraphicsContext::realize() { if (realizeImplementation()) { return true; } else { return false; } } void GraphicsContext::close(bool callCloseImplementation) { OSG_INFO<<"close("<getContextID()); if (cd && cd->getNumContexts()>1) sharedContextExists = true; } // release all the OpenGL objects in the scene graphs associated with this for(Cameras::iterator itr = _cameras.begin(); itr != _cameras.end(); ++itr) { Camera* camera = (*itr); if (camera) { OSG_INFO<<"Releasing GL objects for Camera="<getContextID()="<<_state->getContextID()<getContextID()); osg::flushAllDeletedGLObjects(_state->getContextID()); OSG_INFO<<"Done delete of GL objects"<getContextID()); } releaseContext(); } else { OSG_INFO<<"makeCurrent did not succeed, could not do flush/deletion of OpenGL objects."<getContextID()); } if (_state.valid()) { decrementContextIDUsageCount(_state->getContextID()); _state = 0; } } bool GraphicsContext::makeCurrent() { _threadOfLastMakeCurrent = OpenThreads::Thread::CurrentThread(); bool result = makeCurrentImplementation(); if (result) { // initialize extension process, not only initializes on first // call, will be a non-op on subsequent calls. getState()->initializeExtensionProcs(); } return result; } bool GraphicsContext::makeContextCurrent(GraphicsContext* readContext) { bool result = makeContextCurrentImplementation(readContext); if (result) { _threadOfLastMakeCurrent = OpenThreads::Thread::CurrentThread(); // initialize extension process, not only initializes on first // call, will be a non-op on subsequent calls. getState()->initializeExtensionProcs(); } return result; } bool GraphicsContext::releaseContext() { bool result = releaseContextImplementation(); _threadOfLastMakeCurrent = (OpenThreads::Thread*)(-1); return result; } void GraphicsContext::swapBuffers() { if (isCurrent()) { swapBuffersCallbackOrImplementation(); clear(); } else if (_graphicsThread.valid() && _threadOfLastMakeCurrent == _graphicsThread.get()) { _graphicsThread->add(new SwapBuffersOperation); } else { makeCurrent(); swapBuffersCallbackOrImplementation(); clear(); } } void GraphicsContext::createGraphicsThread() { if (!_graphicsThread) { setGraphicsThread(new GraphicsThread); if (_traits.valid()) { _graphicsThread->setProcessorAffinity(_traits->affinity); } } } void GraphicsContext::setGraphicsThread(GraphicsThread* gt) { if (_graphicsThread==gt) return; if (_graphicsThread.valid()) { // need to kill the thread in some way... _graphicsThread->cancel(); _graphicsThread->setParent(0); } _graphicsThread = gt; if (_graphicsThread.valid()) { _graphicsThread->setParent(this); } } void GraphicsContext::add(Operation* operation) { OSG_INFO<<"Doing add"< lock(_operationsMutex); // add the operation to the end of the list _operations.push_back(operation); _operationsBlock->set(true); } void GraphicsContext::remove(Operation* operation) { OSG_INFO<<"Doing remove operation"< lock(_operationsMutex); for(GraphicsOperationQueue::iterator itr = _operations.begin(); itr!=_operations.end();) { if ((*itr)==operation) itr = _operations.erase(itr); else ++itr; } if (_operations.empty()) { _operationsBlock->set(false); } } void GraphicsContext::remove(const std::string& name) { OSG_INFO<<"Doing remove named operation"< lock(_operationsMutex); // find the remove all operations with specified name for(GraphicsOperationQueue::iterator itr = _operations.begin(); itr!=_operations.end();) { if ((*itr)->getName()==name) itr = _operations.erase(itr); else ++itr; } if (_operations.empty()) { _operationsBlock->set(false); } } void GraphicsContext::removeAllOperations() { OSG_INFO<<"Doing remove all operations"< lock(_operationsMutex); _operations.clear(); _operationsBlock->set(false); } void GraphicsContext::runOperations() { // sort the cameras into order typedef std::vector CameraVector; CameraVector camerasCopy; std::copy(_cameras.begin(), _cameras.end(), std::back_inserter(camerasCopy)); std::sort(camerasCopy.begin(), camerasCopy.end(), CameraRenderOrderSortOp()); for(CameraVector::iterator itr = camerasCopy.begin(); itr != camerasCopy.end(); ++itr) { osg::Camera* camera = *itr; if (camera->getRenderer()) (*(camera->getRenderer()))(this); } for(GraphicsOperationQueue::iterator itr = _operations.begin(); itr != _operations.end(); ) { { OpenThreads::ScopedLock lock(_operationsMutex); _currentOperation = *itr; if (!_currentOperation->getKeep()) { itr = _operations.erase(itr); if (_operations.empty()) { _operationsBlock->set(false); } } else { ++itr; } } if (_currentOperation.valid()) { // OSG_INFO<<"Doing op "<<_currentOperation->getName()<<" "< lock(_operationsMutex); _currentOperation = 0; } } } } void GraphicsContext::addCamera(osg::Camera* camera) { _cameras.push_back(camera); } void GraphicsContext::removeCamera(osg::Camera* camera) { Cameras::iterator itr = std::find(_cameras.begin(), _cameras.end(), camera); if (itr != _cameras.end()) { // find a set of nodes attached the camera that we are removing that isn't // shared by any other cameras on this GraphicsContext typedef std::set NodeSet; NodeSet nodes; for(unsigned int i=0; igetNumChildren(); ++i) { nodes.insert(camera->getChild(i)); } for(Cameras::iterator citr = _cameras.begin(); citr != _cameras.end(); ++citr) { if (citr != itr) { osg::Camera* otherCamera = *citr; for(unsigned int i=0; igetNumChildren(); ++i) { NodeSet::iterator nitr = nodes.find(otherCamera->getChild(i)); if (nitr != nodes.end()) nodes.erase(nitr); } } } // now release the GLobjects associated with these non shared nodes for(NodeSet::iterator nitr = nodes.begin(); nitr != nodes.end(); ++nitr) { (*nitr)->releaseGLObjects(_state.get()); } // release the context of the any RenderingCache that the Camera has. if (camera->getRenderingCache()) { camera->getRenderingCache()->releaseGLObjects(_state.get()); } _cameras.erase(itr); } } void GraphicsContext::resizedImplementation(int x, int y, int width, int height) { std::set processedViewports; if (!_traits) return; double widthChangeRatio = double(width) / double(_traits->width); double heigtChangeRatio = double(height) / double(_traits->height); double aspectRatioChange = widthChangeRatio / heigtChangeRatio; for(Cameras::iterator itr = _cameras.begin(); itr != _cameras.end(); ++itr) { Camera* camera = (*itr); // resize doesn't affect Cameras set up with FBO's. if (camera->getRenderTargetImplementation()==osg::Camera::FRAME_BUFFER_OBJECT) continue; Viewport* viewport = camera->getViewport(); if (viewport) { // avoid processing a shared viewport twice if (processedViewports.count(viewport)==0) { processedViewports.insert(viewport); if (viewport->x()==0 && viewport->y()==0 && viewport->width()>=_traits->width && viewport->height()>=_traits->height) { viewport->setViewport(0,0,width,height); } else { viewport->x() = static_cast(double(viewport->x())*widthChangeRatio); viewport->y() = static_cast(double(viewport->y())*heigtChangeRatio); viewport->width() = static_cast(double(viewport->width())*widthChangeRatio); viewport->height() = static_cast(double(viewport->height())*heigtChangeRatio); } } } // if aspect ratio adjusted change the project matrix to suit. if (aspectRatioChange != 1.0) { osg::View* view = camera->getView(); osg::View::Slave* slave = view ? view->findSlaveForCamera(camera) : 0; if (slave) { if (camera->getReferenceFrame()==osg::Transform::RELATIVE_RF) { switch(view->getCamera()->getProjectionResizePolicy()) { case(osg::Camera::HORIZONTAL): slave->_projectionOffset *= osg::Matrix::scale(1.0/aspectRatioChange,1.0,1.0); break; case(osg::Camera::VERTICAL): slave->_projectionOffset *= osg::Matrix::scale(1.0, aspectRatioChange,1.0); break; default: break; } } else { switch(camera->getProjectionResizePolicy()) { case(osg::Camera::HORIZONTAL): camera->getProjectionMatrix() *= osg::Matrix::scale(1.0/aspectRatioChange,1.0,1.0); break; case(osg::Camera::VERTICAL): camera->getProjectionMatrix() *= osg::Matrix::scale(1.0, aspectRatioChange,1.0); break; default: break; } } } else { Camera::ProjectionResizePolicy policy = view ? view->getCamera()->getProjectionResizePolicy() : camera->getProjectionResizePolicy(); switch(policy) { case(osg::Camera::HORIZONTAL): camera->getProjectionMatrix() *= osg::Matrix::scale(1.0/aspectRatioChange,1.0,1.0); break; case(osg::Camera::VERTICAL): camera->getProjectionMatrix() *= osg::Matrix::scale(1.0, aspectRatioChange,1.0); break; default: break; } osg::Camera* master = view ? view->getCamera() : 0; if (view && camera==master) { for(unsigned int i=0; igetNumSlaves(); ++i) { osg::View::Slave& child = view->getSlave(i); if (child._camera.valid() && child._camera->getReferenceFrame()==osg::Transform::RELATIVE_RF) { // scale the slaves by the inverse of the change that has been applied to master, to avoid them be // scaled twice (such as when both master and slave are on the same GraphicsContexts) or by the wrong scale // when master and slave are on different GraphicsContexts. switch(policy) { case(osg::Camera::HORIZONTAL): child._projectionOffset *= osg::Matrix::scale(aspectRatioChange,1.0,1.0); break; case(osg::Camera::VERTICAL): child._projectionOffset *= osg::Matrix::scale(1.0, 1.0/aspectRatioChange,1.0); break; default: break; } } } } } } } _traits->x = x; _traits->y = y; _traits->width = width; _traits->height = height; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SyncSwapBuffersCallback // SyncSwapBuffersCallback::SyncSwapBuffersCallback() { OSG_INFO<<"Created SyncSwapBuffersCallback."<swapBuffersImplementation(); //glFinish(); GLExtensions* ext = gc->getState()->get(); if (ext->glClientWaitSync) { if (_previousSync) { unsigned int num_seconds = 1; GLuint64 timeout = num_seconds * ((GLuint64)1000 * 1000 * 1000); ext->glClientWaitSync(_previousSync, 0, timeout); ext->glDeleteSync(_previousSync); } _previousSync = ext->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); } //gc->getState()->checkGLErrors("after glWaitSync"); //OSG_NOTICE<<"After swap"<