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(;;) }