void DiskCacheNode::fetchRenderCloneKnobs() { EffectInstance::fetchRenderCloneKnobs(); _imp->frameRange = toKnobChoice(getKnobByName(kDiskCacheNodeFrameRange)); _imp->firstFrame = toKnobInt(getKnobByName(kDiskCacheNodeFirstFrame)); _imp->lastFrame = toKnobInt(getKnobByName(kDiskCacheNodeLastFrame)); }
void OutputEffectInstance::createWriterPath() { ///Make sure that the file path exists KnobPtr fileParam = getKnobByName(kOfxImageEffectFileParamName); if (fileParam) { Knob<std::string>* isString = dynamic_cast<Knob<std::string>*>( fileParam.get() ); if (isString) { std::string pattern = isString->getValue(); std::string path = SequenceParsing::removePath(pattern); std::map<std::string, std::string> env; getApp()->getProject()->getEnvironmentVariables(env); Project::expandVariable(env, path); if ( !path.empty() ) { QDir().mkpath( QString::fromUtf8( path.c_str() ) ); } } } }
void RotoShapeRenderNode::fetchRenderCloneKnobs() { RotoDrawableItemPtr mainInstanceItem = getOriginalAttachedItem(); assert(mainInstanceItem); FrameViewRenderKey key = {getCurrentRenderTime(), getCurrentRenderView(), getCurrentRender()}; KnobHolderPtr itemClone = mainInstanceItem->createRenderClone(key); assert(itemClone); (void)itemClone; assert(isRenderClone()); EffectInstance::fetchRenderCloneKnobs(); _imp->renderType = toKnobChoice(getKnobByName(kRotoShapeRenderNodeParamType)); _imp->outputRoDTypeKnob = toKnobChoice(getKnobByName(kRotoOutputRodType)); _imp->outputFormatKnob = toKnobChoice(getKnobByName(kRotoFormatParam)); _imp->outputFormatSizeKnob = toKnobInt(getKnobByName(kRotoFormatSize)); assert(_imp->outputFormatSizeKnob.lock()); _imp->outputFormatParKnob = toKnobDouble(getKnobByName(kRotoFormatPar)); _imp->clipToFormatKnob = toKnobBool(getKnobByName(kRotoClipToFormatParam)); }
void OutputEffectInstance::reportStats(int time, ViewIdx view, double wallTime, const std::map<NodePtr, NodeRenderStats > & stats) { std::string filename; KnobPtr fileKnob = getKnobByName(kOfxImageEffectFileParamName); if (fileKnob) { KnobOutputFile* strKnob = dynamic_cast<KnobOutputFile*>( fileKnob.get() ); if (strKnob) { QString qfileName = QString::fromUtf8( SequenceParsing::generateFileNameFromPattern(strKnob->getValue( 0, ViewIdx(view) ), getApp()->getProject()->getProjectViewNames(), time, view).c_str() ); QtCompat::removeFileExtension(qfileName); qfileName.append( QString::fromUtf8("-stats.txt") ); filename = qfileName.toStdString(); } } //If there's no filename knob, do not write anything if ( filename.empty() ) { std::cout << tr("Cannot write render statistics file: " "%1 does not seem to have a parameter named \"filename\" " "to determine the location where to write the stats file.") .arg( QString::fromUtf8( getScriptName_mt_safe().c_str() ) ).toStdString(); return; } FStreamsSupport::ofstream ofile; FStreamsSupport::open(&ofile, filename); if (!ofile) { std::cout << tr("Failure to write render statistics file.").toStdString() << std::endl; return; } ofile << "Time spent to render frame (wall clock time): " << Timer::printAsTime(wallTime, false).toStdString() << std::endl; for (std::map<NodePtr, NodeRenderStats >::const_iterator it = stats.begin(); it != stats.end(); ++it) { ofile << "------------------------------- " << it->first->getScriptName_mt_safe() << "------------------------------- " << std::endl; ofile << "Time spent rendering: " << Timer::printAsTime(it->second.getTotalTimeSpentRendering(), false).toStdString() << std::endl; const RectD & rod = it->second.getRoD(); ofile << "Region of definition: x1 = " << rod.x1 << " y1 = " << rod.y1 << " x2 = " << rod.x2 << " y2 = " << rod.y2 << std::endl; ofile << "Is Identity to Effect? "; NodePtr identity = it->second.getInputImageIdentity(); if (identity) { ofile << "Yes, to " << identity->getScriptName_mt_safe() << std::endl; } else { ofile << "No" << std::endl; } ofile << "Has render-scale support? "; if ( it->second.isRenderScaleSupportEnabled() ) { ofile << "Yes"; } else { ofile << "No"; } ofile << std::endl; ofile << "Has tiles support? "; if ( it->second.isTilesSupportEnabled() ) { ofile << "Yes"; } else { ofile << "No"; } ofile << std::endl; ofile << "Channels processed: "; std::bitset<4> processChannels = it->second.getChannelsRendered(); if (processChannels[0]) { ofile << "red "; } if (processChannels[1]) { ofile << "green "; } if (processChannels[2]) { ofile << "blue "; } if (processChannels[3]) { ofile << "alpha"; } ofile << std::endl; ofile << "Output alpha premultiplication: "; switch ( it->second.getOutputPremult() ) { case eImagePremultiplicationOpaque: ofile << "opaque"; break; case eImagePremultiplicationPremultiplied: ofile << "premultiplied"; break; case eImagePremultiplicationUnPremultiplied: ofile << "unpremultiplied"; break; } ofile << std::endl; ofile << "Mipmap level(s) rendered: "; for (std::set<unsigned int>::const_iterator it2 = it->second.getMipMapLevelsRendered().begin(); it2 != it->second.getMipMapLevelsRendered().end(); ++it2) { ofile << *it2 << ' '; } ofile << std::endl; int nbCacheMiss, nbCacheHit, nbCacheHitButDownscaled; it->second.getCacheAccessInfos(&nbCacheMiss, &nbCacheHit, &nbCacheHitButDownscaled); ofile << "Nb cache hit: " << nbCacheMiss << std::endl; ofile << "Nb cache miss: " << nbCacheMiss << std::endl; ofile << "Nb cache hit requiring mipmap downscaling: " << nbCacheHitButDownscaled << std::endl; const std::set<std::string> & planes = it->second.getPlanesRendered(); ofile << "Plane(s) rendered: "; for (std::set<std::string>::const_iterator it2 = planes.begin(); it2 != planes.end(); ++it2) { ofile << *it2 << ' '; } ofile << std::endl; std::list<std::pair<RectI, NodePtr > > identityRectangles = it->second.getIdentityRectangles(); const std::list<RectI> & renderedRectangles = it->second.getRenderedRectangles(); ofile << "Identity rectangles: " << identityRectangles.size() << std::endl; for (std::list<std::pair<RectI, NodePtr > > ::iterator it2 = identityRectangles.begin(); it2 != identityRectangles.end(); ++it2) { ofile << "Origin: " << it2->second->getScriptName_mt_safe() << ", rect: x1 = " << it2->first.x1 << " y1 = " << it2->first.y1 << " x2 = " << it2->first.x2 << " y2 = " << it2->first.y2 << std::endl; } ofile << "Rectangles rendered: " << renderedRectangles.size() << std::endl; for (std::list<RectI>::const_iterator it2 = renderedRectangles.begin(); it2 != renderedRectangles.end(); ++it2) { ofile << "x1 = " << it2->x1 << " y1 = " << it2->y1 << " x2 = " << it2->x2 << " y2 = " << it2->y2 << std::endl; } } } // OutputEffectInstance::reportStats
void OutputEffectInstance::renderFullSequence(bool isBlocking, bool enableRenderStats, BlockingBackgroundRender* renderController, int first, int last, int frameStep) { int viewsCount = getApp()->getProject()->getProjectViewsCount(); const ViewIdx mainView(0); std::vector<ViewIdx> viewsToRender(viewsCount); for (int i = 0; i < viewsCount; ++i) { viewsToRender[i] = ViewIdx(i); } ///The effect is sequential (e.g: WriteFFMPEG), and thus cannot render multiple views, we have to choose one ///We pick the user defined main view in the project settings SequentialPreferenceEnum sequentiallity = getSequentialPreference(); bool canOnlyHandleOneView = sequentiallity == eSequentialPreferenceOnlySequential || sequentiallity == eSequentialPreferencePreferSequential; if (canOnlyHandleOneView) { viewsToRender.clear(); viewsToRender.push_back(mainView); } if ( isViewAware() ) { //If the Writer is view aware, check if it wants to render all views at once or not KnobPtr outputFileNameKnob = getKnobByName(kOfxImageEffectFileParamName); if (outputFileNameKnob) { KnobOutputFile* outputFileName = dynamic_cast<KnobOutputFile*>( outputFileNameKnob.get() ); assert(outputFileName); if (outputFileName) { std::string pattern = outputFileName->getValue(); std::size_t foundViewPattern = pattern.find_first_of("%v"); if (foundViewPattern == std::string::npos) { foundViewPattern = pattern.find_first_of("%V"); } if (foundViewPattern == std::string::npos) { ///No view pattern ///all views will be overwritten to the same file ///If this is WriteOIIO, check the parameter "viewsSelector" to determine if the user wants to encode all ///views to a single file or not KnobPtr viewsKnob = getKnobByName(kWriteOIIOParamViewsSelector); bool hasViewChoice = false; if ( viewsKnob && !viewsKnob->getIsSecret() ) { KnobChoice* viewsChoice = dynamic_cast<KnobChoice*>( viewsKnob.get() ); if (viewsChoice) { hasViewChoice = true; int viewChoice_i = viewsChoice->getValue(); if (viewChoice_i == 0) { // the "All" choice viewsToRender.clear(); // note: if the plugin renders all views to a single file, then rendering view 0 will do the job. viewsToRender.push_back( ViewIdx(0) ); } else { //The user has specified a view viewsToRender.clear(); assert(viewChoice_i >= 1); viewsToRender.push_back( ViewIdx(viewChoice_i - 1) ); } } } if (!hasViewChoice) { if (viewsToRender.size() > 1) { std::string mainViewName; const std::vector<std::string>& viewNames = getApp()->getProject()->getProjectViewNames(); if ( mainView < (int)viewNames.size() ) { mainViewName = viewNames[mainView]; } QString message = tr("%1 does not support multi-view, only the view %2 will be rendered.") .arg( QString::fromUtf8( getNode()->getLabel_mt_safe().c_str() ) ) .arg( QString::fromUtf8( mainViewName.c_str() ) ); if (!renderController) { message.append( QChar::fromLatin1('\n') ); message.append( QString::fromUtf8("You can use the %v or %V indicator in the filename to render to separate files.\n" "Would you like to continue?") ); StandardButtonEnum rep = Dialogs::questionDialog(tr("Multi-view support").toStdString(), message.toStdString(), false, StandardButtons(eStandardButtonOk | eStandardButtonCancel), eStandardButtonOk); if (rep != eStandardButtonOk) { return; } } else { Dialogs::warningDialog( tr("Multi-view support").toStdString(), message.toStdString() ); } } //Render the main-view only... viewsToRender.clear(); viewsToRender.push_back(mainView); } } else { ///The user wants to write each view into a separate file ///This will disregard the content of kWriteOIIOParamViewsSelector and the Writer ///should write one view per-file. } } } } else { // !isViewAware if (viewsToRender.size() > 1) { std::string mainViewName; const std::vector<std::string>& viewNames = getApp()->getProject()->getProjectViewNames(); if ( mainView < (int)viewNames.size() ) { mainViewName = viewNames[mainView]; } QString message = tr("%1 does not support multi-view, only the view %2 will be rendered.") .arg( QString::fromUtf8( getNode()->getLabel_mt_safe().c_str() ) ) .arg( QString::fromUtf8( mainViewName.c_str() ) ); if (!renderController) { message.append( QChar::fromLatin1('\n') ); message.append( QString::fromUtf8("You can use the %v or %V indicator in the filename to render to separate files.\n" "Would you like to continue?") ); StandardButtonEnum rep = Dialogs::questionDialog(tr("Multi-view support").toStdString(), message.toStdString(), false, StandardButtons(eStandardButtonOk | eStandardButtonCancel), eStandardButtonOk); if (rep != eStandardButtonOk) { return; } } else { Dialogs::warningDialog( tr("Multi-view support").toStdString(), message.toStdString() ); } } } RenderSequenceArgs args; { QMutexLocker k(&_outputEffectDataLock); args.firstFrame = first; args.lastFrame = last; args.frameStep = frameStep; args.renderController = renderController; args.useStats = enableRenderStats; args.blocking = isBlocking; args.viewsToRender = viewsToRender; _renderSequenceRequests.push_back(args); if (_renderSequenceRequests.size() > 1) { //The node is already rendering a sequence, queue it and dequeue it in notifyRenderFinished() return; } } launchRenderSequence(args); } // OutputEffectInstance::renderFullSequence