/// This is called whenever a param is changed by the plugin so that /// the recursive instanceChangedAction will be fed the correct /// renderScale void OfxImageEffectInstance::getRenderScaleRecursive(double &x, double &y) const { assert( getOfxEffectInstance() ); std::list<ViewerInstance*> attachedViewers; getOfxEffectInstance()->getNode()->hasViewersConnected(&attachedViewers); ///get the render scale of the 1st viewer if ( !attachedViewers.empty() ) { ViewerInstance* first = attachedViewers.front(); int mipMapLevel = first->getMipMapLevel(); x = Natron::Image::getScaleFromMipMapLevel( (unsigned int)mipMapLevel ); y = x; } else { x = 1.; y = 1.; } }
void ViewerCollector::deleteDeadInstances() { AutoLock l(&m_lockObj); InstanceList::iterator iter = m_instances.begin(); while (iter != m_instances.end()) { ViewerInstance *instance = *iter; if (instance->isStopped()) { if (instance->requiresReconnect()) { ++m_countToReconnect; } delete instance; iter = m_instances.erase(iter); } else { iter++; } } }
void NodeGraph::selectNode(const NodeGuiPtr & n, bool addToSelection) { if ( !n->isVisible() ) { return; } bool alreadyInSelection = std::find(_imp->_selection.begin(),_imp->_selection.end(),n) != _imp->_selection.end(); assert(n); if (addToSelection && !alreadyInSelection) { _imp->_selection.push_back(n); } else if (!addToSelection) { clearSelection(); _imp->_selection.push_back(n); } n->setUserSelected(true); ViewerInstance* isViewer = n->getNode()->isEffectViewer(); if (isViewer) { OpenGLViewerI* viewer = isViewer->getUiContext(); const std::list<ViewerTab*> & viewerTabs = getGui()->getViewersList(); for (std::list<ViewerTab*>::const_iterator it = viewerTabs.begin(); it != viewerTabs.end(); ++it) { if ( (*it)->getViewer() == viewer ) { setLastSelectedViewer( (*it) ); } } } bool magnifiedNodeSelected = false; if (_imp->_magnifiedNode) { magnifiedNodeSelected = std::find(_imp->_selection.begin(),_imp->_selection.end(),_imp->_magnifiedNode) != _imp->_selection.end(); } if (magnifiedNodeSelected && _imp->_magnifOn) { _imp->_magnifOn = false; _imp->_magnifiedNode->setScale_natron(_imp->_nodeSelectedScaleBeforeMagnif); } }
Natron::Status VideoEngine::renderFrame(SequenceTime time,bool singleThreaded) { /*pre process frame*/ // Status stat = _tree.preProcessFrame(); // if (stat == StatFailed) { // return stat; // //don't throw an exception here, this is regular behaviour when a mandatory input is not connected. // // We don't want to popup a dialog everytime it occurs // // throw std::runtime_error("PreProcessFrame failed, mandatory inputs are probably not connected."); // } bool isSequentialRender = _currentRunArgs._frameRequestsCount > 1 || _currentRunArgs._frameRequestsCount == -1 || _currentRunArgs._forceSequential; Status stat = StatOK; /*get the time at which we started rendering the frame*/ gettimeofday(&_startRenderFrameTime, 0); if (_tree.isOutputAViewer() && !_tree.isOutputAnOpenFXNode()) { ViewerInstance* viewer = _tree.outputAsViewer(); stat = viewer->renderViewer(time,singleThreaded,isSequentialRender); if (!_currentRunArgs._sameFrame) { QMutexLocker timerLocker(&_timerMutex); _timer->waitUntilNextFrameIsDue(); // timer synchronizing with the requested fps if ((_timerFrameCount % NATRON_FPS_REFRESH_RATE) == 0 && _currentRunArgs._frameRequestsCount == -1) { emit fpsChanged(_timer->actualFrameRate(),_timer->getDesiredFrameRate()); // refreshing fps display on the GUI _timerFrameCount = 1; //reseting to 1 } else { ++_timerFrameCount; } } if (stat == StatFailed) { viewer->disconnectViewer(); } } else { RenderScale scale; scale.x = scale.y = 1.; RectI rod; bool isProjectFormat; int viewsCount = _tree.getOutput()->getApp()->getProject()->getProjectViewsCount(); int mainView = 0; if (isSequentialRender) { mainView = _tree.getOutput()->getApp()->getMainView(); } for (int i = 0; i < viewsCount;++i) { if (isSequentialRender && i != mainView) { ///@see the warning in EffectInstance::evaluate continue; } // Do not catch exceptions: if an exception occurs here it is probably fatal, since // it comes from Natron itself. All exceptions from plugins are already caught // by the HostSupport library. stat = _tree.getOutput()->getRegionOfDefinition_public(time,scale,i, &rod,&isProjectFormat); if (stat != StatFailed) { ImageComponents components; ImageBitDepth imageDepth; _tree.getOutput()->getPreferredDepthAndComponents(-1, &components, &imageDepth); (void)_tree.getOutput()->renderRoI(EffectInstance::RenderRoIArgs(time, //< the time at which to render scale, //< the scale at which to render 0, //< the mipmap level (redundant with the scale) i , //< the view to render rod, //< the region of interest (in pixel coordinates) isSequentialRender, // is this sequential false, // is this render due to user interaction ? false,//< bypass cache ? &rod, // < any precomputed rod ? components, imageDepth)); } else { break; } } } // // if (stat == StatFailed) { // throw std::runtime_error("Render failed"); // } return stat; }
void VideoEngine::iterateKernel(bool singleThreaded) { for(;;){ // infinite loop { QMutexLocker locker(&_abortedRequestedMutex); if(_abortRequested > 0) { locker.unlock(); return; } } Natron::OutputEffectInstance* output = dynamic_cast<Natron::OutputEffectInstance*>(_tree.getOutput()); assert(output); ViewerInstance* viewer = dynamic_cast<ViewerInstance*>(output); /*update the tree inputs */ _tree.refreshRenderInputs(); if(viewer){ if (singleThreaded || appPTR->isBackground()) { getFrameRange(); } else { { QMutexLocker l(&_abortedRequestedMutex); if (_abortRequested > 0) { return; } } bool mustQuit; { QMutexLocker l(&_mustQuitMutex); mustQuit = _mustQuit; } QMutexLocker l(&_getFrameRangeMutex); _gettingFrameRange = true; emit mustGetFrameRange(); while (_gettingFrameRange && !mustQuit) { _getFrameRangeCond.wait(&_getFrameRangeMutex); } } //If the frame range is not locked, let the user define it. if (viewer->isFrameRangeLocked()) { _timeline->setFrameRange(_firstFrame, _lastFrame); } } int firstFrame,lastFrame; if (viewer) { firstFrame = _timeline->leftBound(); lastFrame = _timeline->rightBound(); } else { firstFrame = output->getFirstFrame(); lastFrame = output->getLastFrame(); } ////////////////////////////// // Set the current frame // int currentFrame = 0; if (!_currentRunArgs._recursiveCall) { /*if writing on disk and not a recursive call, move back the timeline cursor to the start*/ if (viewer) { currentFrame = _timeline->currentFrame(); } else { output->setCurrentFrame(firstFrame); currentFrame = firstFrame; } } else if(!_currentRunArgs._sameFrame && _currentRunArgs._seekTimeline) { assert(_currentRunArgs._recursiveCall); // we're in the else part if (!viewer) { output->setCurrentFrame(output->getCurrentFrame()+1); currentFrame = output->getCurrentFrame(); if(currentFrame > lastFrame){ return; } } else { // viewer assert(viewer); if (_currentRunArgs._forward) { currentFrame = _timeline->currentFrame(); if (currentFrame < lastFrame) { _timeline->incrementCurrentFrame(output); ++currentFrame; } else { QMutexLocker loopModeLocker(&_loopModeMutex); if (_loopMode) { // loop only for a viewer currentFrame = firstFrame; _timeline->seekFrame(currentFrame,output); } else { loopModeLocker.unlock(); return; } } } else { currentFrame = _timeline->currentFrame(); if (currentFrame > firstFrame) { _timeline->decrementCurrentFrame(output); --currentFrame; } else { QMutexLocker loopModeLocker(&_loopModeMutex); if (_loopMode) { //loop only for a viewer currentFrame = lastFrame; _timeline->seekFrame(currentFrame,output); } else { loopModeLocker.unlock(); return; } } } } } /////////////////////////////// // Check whether we need to stop the engine or not for various reasons. // { QMutexLocker locker(&_abortedRequestedMutex); if(_abortRequested > 0 || // #1 aborted by the user (_tree.isOutputAViewer() // #2 the Tree contains only 1 frame and we rendered it && _currentRunArgs._recursiveCall && firstFrame == lastFrame && _currentRunArgs._frameRequestsCount == -1 && _currentRunArgs._frameRequestIndex == 1) || _currentRunArgs._frameRequestsCount == 0// #3 the sequence ended and it was not an infinite run || (appPTR->getAppType() == AppManager::APP_BACKGROUND_AUTO_RUN && appPTR->hasAbortAnyProcessingBeenCalled())) { return; } } ///before rendering the frame, clear any persistent message that may be left _tree.clearPersistentMessages(); //////////////////////// // Render currentFrame // // if the output is a writer, _tree.outputAsWriter() returns a valid pointer/ Status stat; try { stat = renderFrame(currentFrame,singleThreaded); } catch (const std::exception &e) { std::stringstream ss; ss << "Error while rendering" << " frame " << currentFrame << ": " << e.what(); if (viewer) { //viewer->setPersistentMessage(Natron::ERROR_MESSAGE, ss.str()); viewer->disconnectViewer(); } else { std::cout << ss.str() << std::endl; } return; } if (stat == StatFailed) { return; } /*The frame has been rendered , we call engineLoop() which will reset all the flags, update viewers and appropriately increment counters for the next frame in the sequence.*/ emit frameRendered(currentFrame); if(appPTR->isBackground()){ QString frameStr = QString::number(currentFrame); appPTR->writeToOutputPipe(kFrameRenderedStringLong + frameStr,kFrameRenderedStringShort + frameStr); } if (singleThreaded) { QCoreApplication::processEvents(); ///if single threaded: the user might have requested to exit and the engine might be deleted after the events process. if (_mustQuit) { return; } } if(_currentRunArgs._frameRequestIndex == 0 && _currentRunArgs._frameRequestsCount == 1 && !_currentRunArgs._sameFrame){ _currentRunArgs._frameRequestsCount = 0; }else if(_currentRunArgs._frameRequestsCount!=-1){ // if the frameRequestCount is defined (i.e: not indefinitely running) --_currentRunArgs._frameRequestsCount; } ++_currentRunArgs._frameRequestIndex;//incrementing the frame counter _currentRunArgs._recursiveCall = true; } // end for(;;) }
void ProjectGuiSerialization::initialize(const ProjectGui* projectGui) { NodesList activeNodes; projectGui->getInternalProject()->getActiveNodesExpandGroups(&activeNodes); _serializedNodes.clear(); for (NodesList::iterator it = activeNodes.begin(); it != activeNodes.end(); ++it) { boost::shared_ptr<NodeGuiI> nodegui_i = (*it)->getNodeGui(); if (!nodegui_i) { continue; } NodeGuiPtr nodegui = boost::dynamic_pointer_cast<NodeGui>(nodegui_i); if ( nodegui->isVisible() ) { boost::shared_ptr<NodeCollection> isInCollection = (*it)->getGroup(); NodeGroup* isCollectionAGroup = dynamic_cast<NodeGroup*>( isInCollection.get() ); if (!isCollectionAGroup) { ///Nodes within a group will be serialized recursively in the node group serialization NodeGuiSerialization state; nodegui->serialize(&state); _serializedNodes.push_back(state); } ViewerInstance* viewer = (*it)->isEffectViewer(); if (viewer) { ViewerTab* tab = projectGui->getGui()->getViewerTabForInstance(viewer); assert(tab); ViewerData viewerData; double zoompar; tab->getViewer()->getProjection(&viewerData.zoomLeft, &viewerData.zoomBottom, &viewerData.zoomFactor, &zoompar); viewerData.userRoI = tab->getViewer()->getUserRegionOfInterest(); viewerData.userRoIenabled = tab->getViewer()->isUserRegionOfInterestEnabled(); viewerData.isClippedToProject = tab->isClippedToProject(); viewerData.autoContrastEnabled = tab->isAutoContrastEnabled(); viewerData.gain = tab->getGain(); viewerData.gamma = tab->getGamma(); viewerData.colorSpace = tab->getColorSpace(); viewerData.channels = tab->getChannelsString(); viewerData.renderScaleActivated = tab->getRenderScaleActivated(); viewerData.mipMapLevel = tab->getMipMapLevel(); viewerData.zoomOrPanSinceLastFit = tab->getZoomOrPannedSinceLastFit(); viewerData.wipeCompositingOp = (int)tab->getCompositingOperator(); viewerData.leftToolbarVisible = tab->isLeftToolbarVisible(); viewerData.rightToolbarVisible = tab->isRightToolbarVisible(); viewerData.topToolbarVisible = tab->isTopToolbarVisible(); viewerData.infobarVisible = tab->isInfobarVisible(); viewerData.playerVisible = tab->isPlayerVisible(); viewerData.timelineVisible = tab->isTimelineVisible(); viewerData.checkerboardEnabled = tab->isCheckerboardEnabled(); viewerData.fps = tab->getDesiredFps(); viewerData.fpsLocked = tab->isFPSLocked(); viewerData.isPauseEnabled[0] = tab->isViewerPaused(0); viewerData.isPauseEnabled[1] = tab->isViewerPaused(1); viewerData.layerName = tab->getCurrentLayerName().toStdString(); viewerData.alphaLayerName = tab->getCurrentAlphaLayerName().toStdString(); tab->getTimelineBounds(&viewerData.leftBound, &viewerData.rightBound); tab->getActiveInputs(&viewerData.aChoice, &viewerData.bChoice); viewerData.version = VIEWER_DATA_SERIALIZATION_VERSION; _viewersData.insert( std::make_pair(viewer->getNode()->getScriptName_mt_safe(), viewerData) ); } } } ///Init windows _layoutSerialization.initialize( projectGui->getGui() ); ///save histograms std::list<Histogram*> histograms = projectGui->getGui()->getHistograms_mt_safe(); for (std::list<Histogram*>::const_iterator it = histograms.begin(); it != histograms.end(); ++it) { _histograms.push_back( (*it)->objectName().toStdString() ); } ///save opened panels by order std::list<DockablePanel*> panels = projectGui->getGui()->getVisiblePanels_mt_safe(); for (std::list<DockablePanel*>::iterator it = panels.begin(); it != panels.end(); ++it) { if ( (*it)->isVisible() ) { KnobHolder* holder = (*it)->getHolder(); assert(holder); EffectInstance* isEffect = dynamic_cast<EffectInstance*>(holder); Project* isProject = dynamic_cast<Project*>(holder); if (isProject) { _openedPanelsOrdered.push_back(kNatronProjectSettingsPanelSerializationName); } else if (isEffect) { _openedPanelsOrdered.push_back( isEffect->getNode()->getFullyQualifiedName() ); } } } _scriptEditorInput = projectGui->getGui()->getScriptEditor()->getAutoSavedScript().toStdString(); std::map<NATRON_PYTHON_NAMESPACE::PyPanel*, std::string> pythonPanels = projectGui->getGui()->getPythonPanels(); for (std::map<NATRON_PYTHON_NAMESPACE::PyPanel*, std::string>::iterator it = pythonPanels.begin(); it != pythonPanels.end(); ++it) { boost::shared_ptr<PythonPanelSerialization> s(new PythonPanelSerialization); s->initialize(it->first, it->second); _pythonPanels.push_back(s); } } // initialize
void ProjectGuiSerialization::initialize(const ProjectGui* projectGui) { std::list<boost::shared_ptr<NodeGui> > activeNodes = projectGui->getVisibleNodes(); _serializedNodes.clear(); for (std::list<boost::shared_ptr<NodeGui> >::iterator it = activeNodes.begin();it!=activeNodes.end();++it) { NodeGuiSerialization state; (*it)->serialize(&state); _serializedNodes.push_back(state); if ((*it)->getNode()->pluginID() == "Viewer") { ViewerInstance* viewer = dynamic_cast<ViewerInstance*>((*it)->getNode()->getLiveInstance()); assert(viewer); ViewerTab* tab = projectGui->getGui()->getViewerTabForInstance(viewer); assert(tab); ViewerData viewerData; tab->getViewer()->getProjection(&viewerData.zoomLeft, &viewerData.zoomBottom, &viewerData.zoomFactor, &viewerData.zoomPAR); viewerData.userRoI = tab->getViewer()->getUserRegionOfInterest(); viewerData.userRoIenabled = tab->getViewer()->isUserRegionOfInterestEnabled(); viewerData.isClippedToProject = tab->isClippedToProject(); viewerData.autoContrastEnabled = tab->isAutoContrastEnabled(); viewerData.gain = tab->getGain(); viewerData.colorSpace = tab->getColorSpace(); viewerData.channels = tab->getChannelsString(); viewerData.renderScaleActivated = tab->getRenderScaleActivated(); viewerData.mipMapLevel = tab->getMipMapLevel(); viewerData.zoomOrPanSinceLastFit = tab->getZoomOrPannedSinceLastFit(); viewerData.wipeCompositingOp = (int)tab->getCompositingOperator(); _viewersData.insert(std::make_pair(viewer->getNode()->getName_mt_safe(),viewerData)); } } std::list<TabWidget*> tabWidgets = projectGui->getGui()->getPanes_mt_safe(); for (std::list<TabWidget*>::const_iterator it = tabWidgets.begin(); it!= tabWidgets.end(); ++it) { QString widgetName = (*it)->objectName_mt_safe(); if(widgetName.isEmpty()){ qDebug() << "Warning: attempting to save the layout of an unnamed TabWidget, discarding."; continue; } PaneLayout layout; layout.parentingCreated = false; std::map<TabWidget*,bool> userSplits = (*it)->getUserSplits(); for (std::map<TabWidget*,bool>::const_iterator split = userSplits.begin(); split!=userSplits.end(); ++split) { layout.splits.push_back(split->second); } layout.floating = (*it)->isFloating(); if(layout.floating){ QPoint pos = (*it)->pos_mt_safe(); layout.posx = pos.x(); layout.posy = pos.y(); }else{ //not releveant since the tab is not floating layout.posx = -1; layout.posy = -1; } QStringList tabNames = (*it)->getTabNames(); for (int i = 0; i < tabNames.size(); ++i) { if(tabNames[i].isEmpty()){ qDebug() << "Warning: attempting to save the position of an unnamed tab, discarding."; continue; } layout.tabs.push_back(tabNames[i].toStdString()); } _layout.insert(std::make_pair(widgetName.toStdString(),layout)); } ///now we do a second pass to insert for each layout what are its children splits for (std::map<std::string,PaneLayout>::iterator it = _layout.begin(); it!= _layout.end(); ++it) { if(!it->second.parentingCreated){ createParenting(it); } } ///save application's splitters states std::list<Splitter*> splitters = projectGui->getGui()->getSplitters(); for (std::list<Splitter*>::const_iterator it = splitters.begin(); it!= splitters.end(); ++it) { QByteArray ba = (*it)->saveState(); ba = ba.toBase64(); QString str(ba); _splittersStates.insert(std::make_pair((*it)->objectName_mt_safe().toStdString(),str.toStdString())); } _arePreviewTurnedOffGlobally = projectGui->getGui()->getNodeGraph()->areAllPreviewTurnedOff(); ///save histograms std::list<Histogram*> histograms = projectGui->getGui()->getHistograms_mt_safe(); for (std::list<Histogram*>::const_iterator it = histograms.begin(); it != histograms.end(); ++it) { _histograms.push_back((*it)->objectName().toStdString()); } std::list<NodeBackDrop*> backdrops = projectGui->getGui()->getNodeGraph()->getActiveBackDrops(); for (std::list<NodeBackDrop*>::iterator it = backdrops.begin(); it!=backdrops.end();++it) { NodeBackDropSerialization s; s.initialize(*it); _backdrops.push_back(s); } }