#include "trenderer.h" #include "trendererP.h" // TnzCore includes #include "tsystem.h" #include "tthreadmessage.h" #include "tthread.h" #include "tconvert.h" #include "tstream.h" #include "trasterimage.h" #include "trop.h" #include "timagecache.h" #include "tstopwatch.h" // TnzBase includes #include "trenderresourcemanager.h" #include "tpredictivecachemanager.h" // Qt includes #include #include #include #include #include #include // tcg includes #include "tcg/tcg_deleter_types.h" //Debug //#define DIAGNOSTICS //#include "diagnostics.h" #include #include using namespace TThread; std::vector calculateSortedFxs(TRasterFxP rootFx); //================================================================================ // Preliminaries - Anonymous namespace //================================================================================ namespace { //TRenderer-thread association storage. It provides TRenderers the per-thread //singleton status from inside a rendering process. QThreadStorage rendererStorage; //Same for render process ids. QThreadStorage renderIdsStorage; //------------------------------------------------------------------------------- //Interlacing functions for field-based rendering inline void interlace(TRasterP f0, const TRasterP &f1, int field) { if (f0->getPixelSize() != f1->getPixelSize()) throw TException("interlace: invalid raster combination"); assert(f0->getBounds() == f1->getBounds()); int outWrapBytes = f0->getWrap() * f0->getPixelSize(); int inWrapBytes = f1->getWrap() * f1->getPixelSize(); int outWrapBytes2 = outWrapBytes * 2; int inWrapBytes2 = inWrapBytes * 2; int rowSize = f0->getLx() * f0->getPixelSize(); f1->lock(); f0->lock(); UCHAR *rowIn = f1->getRawData(); UCHAR *rowOut = f0->getRawData(); if (field) rowIn += inWrapBytes; int count = f0->getLy() / 2; while (--count) { memcpy(rowOut, rowIn, rowSize); rowIn += inWrapBytes2; rowOut += outWrapBytes2; } f1->unlock(); f0->unlock(); } } // anonymous namespace //================================================================================ // Preliminaries - output rasters management //================================================================================ //=================== // RasterItem //------------------- //! The RasterItem class represents a reusable output raster for rendering purposes. //! RasterItems are used as TRenderer's output rasters in order to avoid the overhead //! of allocating one raster for each rendered frame. //! Each frame-rendering task will lock a RasterItem as preallocated output before starting the //! render, therefore changing the \b RasterItem::m_busy attribute accordingly. //! As each frame is rendered on a separate thread, the number of RasterItems that //! TRenderer will allocate depends on the number of rendering threads specified to it. //! \sa RasterPool, TRenderer classes class RasterItem { string m_rasterId; public: int m_bpp; bool m_busy; //--------------------------------------------------------- RasterItem(const TDimension &size, int bpp, bool busyFlag) : m_rasterId(""), m_bpp(bpp), m_busy(busyFlag) { TRasterP raster; if (bpp == 32) raster = TRaster32P(size); else if (bpp == 64) raster = TRaster64P(size); else assert(false); m_rasterId = TImageCache::instance()->getUniqueId(); TImageCache::instance()->add(m_rasterId, TRasterImageP(raster)); } //--------------------------------------------------------- ~RasterItem() { TImageCache::instance()->remove(m_rasterId); } //--------------------------------------------------------- TRasterP getRaster() const { TRasterImageP rimg = (TRasterImageP)TImageCache::instance()->get(m_rasterId, true); return rimg ? rimg->getRaster() : TRasterP(); } private: //not implemented RasterItem(); RasterItem(const TRaster &RasterItem); }; //================================================================================ //=================== // RasterPool //------------------- //! Stores a list of RasterItems under TRenderer's requests. class RasterPool { TDimension m_size; int m_bpp; typedef std::list RasterRepository; RasterRepository m_rasterRepository; TThread::Mutex m_repositoryLock; public: RasterPool() : m_size(-1, -1) {} ~RasterPool(); void setRasterSpecs(const TDimension &size, int bpp); void getRasterSpecs(TDimension &size, int &bpp) { size = m_size; bpp = m_bpp; } TRasterP getRaster(); TRasterP getRaster(const TDimension &size, int bpp); void releaseRaster(const TRasterP &r); void clear(); }; //--------------------------------------------------------- void RasterPool::clear() { QMutexLocker sl(&m_repositoryLock); clearPointerContainer(m_rasterRepository); } //--------------------------------------------------------- void RasterPool::setRasterSpecs(const TDimension &size, int bpp) { if (size != m_size || bpp != m_bpp) { m_size = size; m_bpp = bpp; clear(); } } //--------------------------------------------------------- TRasterP RasterPool::getRaster(const TDimension &size, int bpp) { assert(size == m_size && bpp == m_bpp); return getRaster(); } //--------------------------------------------------------- //!Returns for the first not-busy raster TRasterP RasterPool::getRaster() { QMutexLocker sl(&m_repositoryLock); RasterRepository::iterator it = m_rasterRepository.begin(); while (it != m_rasterRepository.end()) { RasterItem *rasItem = *it; if (rasItem->m_busy == false) { TRasterP raster = rasItem->getRaster(); if (!raster) { delete rasItem; m_rasterRepository.erase(it++); continue; } rasItem->m_busy = true; raster->clear(); return raster; } ++it; } RasterItem *rasItem = new RasterItem(m_size, m_bpp, true); m_rasterRepository.push_back(rasItem); return rasItem->getRaster(); } //--------------------------------------------------------- //!Cerca il raster \b r in m_rasterRepository; se lo trova setta a \b false il campo \b m_busy. void RasterPool::releaseRaster(const TRasterP &r) { if (!r) return; QMutexLocker sl(&m_repositoryLock); for (RasterRepository::iterator it = m_rasterRepository.begin(); it != m_rasterRepository.end(); ++it) { RasterItem *rasItem = *it; if (rasItem->getRaster()->getRawData() == r->getRawData()) { assert(rasItem->m_busy); rasItem->m_busy = false; return; } } } //--------------------------------------------------------- RasterPool::~RasterPool() { /*if (m_rasterRepository.size()) TSystem::outputDebug("~RasterPool: itemCount = " + toString ((int)m_rasterRepository.size())+" (should be 0)\n");*/ //Release all raster items clear(); } //================================================================================ // Internal rendering classes declaration //================================================================================ //===================== // TRendererImp //--------------------- class TRendererImp : public TSmartObject { public: struct RenderInstanceInfos { int m_canceled; int m_activeTasks; int m_status; RenderInstanceInfos() : m_canceled(false), m_activeTasks(0), m_status(TRenderer::IDLE) {} }; public: typedef std::vector PortContainer; typedef PortContainer::iterator PortContainerIterator; QReadWriteLock m_portsLock; PortContainer m_ports; QMutex m_renderInstancesMutex; std::map m_activeInstances; //! The renderer Id is a number uniquely associated with a TRenderer instance. static unsigned long m_rendererIdCounter; //! The render Id is a number uniquely associated with a render process started //! with the startRendering() method. static unsigned long m_renderIdCounter; unsigned long m_rendererId; Executor m_executor; bool m_precomputingEnabled; RasterPool m_rasterPool; std::vector m_managers; TAtomicVar m_undoneTasks; //std::vector m_waitingLoops; std::vector m_waitingLoops; TRasterFxP rootFx; //------------------------------------------------------------- TRendererImp(int nThreads); ~TRendererImp(); void startRendering(unsigned long renderId, const std::vector &renderDatas); void notifyRasterStarted(const TRenderPort::RenderData &rd); void notifyRasterCompleted(const TRenderPort::RenderData &rd); void notifyRasterFailure(const TRenderPort::RenderData &rd, TException &e); void notifyRenderFinished(bool isCanceled = false); void addPort(TRenderPort *port); void removePort(TRenderPort *port); void abortRendering(unsigned long renderId); void stopRendering(bool waitForCompleteStop); bool hasToDie(unsigned long renderId); int getRenderStatus(unsigned long renderId); void enablePrecomputing(bool on) { m_precomputingEnabled = on; } bool isPrecomputingEnabled() const { return m_precomputingEnabled; } void setThreadsCount(int nThreads) { m_executor.setMaxActiveTasks(nThreads); } inline void declareRenderStart(unsigned long renderId); inline void declareRenderEnd(unsigned long renderId); inline void declareFrameStart(double frame); inline void declareFrameEnd(double frame); inline void declareStatusStart(int renderStatus); inline void declareStatusEnd(int renderStatus); void quitWaitingLoops(); }; #ifdef WIN32 template class DVAPI TSmartPointerT; #endif typedef TSmartPointerT TRendererImpP; unsigned long TRendererImp::m_rendererIdCounter = 0; unsigned long TRendererImp::m_renderIdCounter = 0; //================================================================================ //=================== // RenderTask //------------------- class RenderTask : public TThread::Runnable { std::vector m_frames; unsigned long m_taskId; unsigned long m_renderId; TRendererImpP m_rendererImp; TFxPair m_fx; TPointD m_framePos; TDimension m_frameSize; TRenderSettings m_info; bool m_fieldRender, m_stereoscopic; Mutex m_rasterGuard; TTile m_tileA; //in normal and field rendering, Rendered at given frame; in stereoscopic, rendered left frame TTile m_tileB; //in field rendering, rendered at frame + 0.5; in stereoscopic, rendered right frame public: RenderTask(unsigned long renderId, unsigned long taskId, double frame, const TRenderSettings &ri, const TFxPair &fx, const TPointD &framePos, const TDimension &frameSize, const TRendererImpP &rendererImp); ~RenderTask() {} void addFrame(double frame) { m_frames.push_back(frame); } void buildTile(TTile &tile); void releaseTiles(); void onFrameStarted(); void onFrameCompleted(); void onFrameFailed(TException &e); void preRun(); void run(); int taskLoad() { return 100; } void onFinished(TThread::RunnableP); }; //================================================================================ // Implementations //================================================================================ //================== // TRenderer //------------------ TRenderer::TRenderer(int nThread) { m_imp = new TRendererImp(nThread); //Already adds a ref } //--------------------------------------------------------- TRenderer::TRenderer(TRendererImp *imp) : m_imp(imp) { if (m_imp) m_imp->addRef(); } //--------------------------------------------------------- TRenderer::TRenderer(const TRenderer &r) : m_imp(r.m_imp) { if (m_imp) m_imp->addRef(); } //--------------------------------------------------------- void TRenderer::operator=(const TRenderer &r) { m_imp = r.m_imp; if (m_imp) m_imp->addRef(); } //--------------------------------------------------------- TRenderer::~TRenderer() { if (m_imp) m_imp->release(); } //--------------------------------------------------------- //! The static method is intended for use inside a rendering thread only. It returns //! a copy of the eventual TRenderer interface installed on the thread - //! or an empty TRenderer if none happened to be. It can be set using the //! install() and uninstall() methods. TRenderer TRenderer::instance() { TRendererImp **lData = rendererStorage.localData(); if (lData) return TRenderer(*lData); return 0; } //--------------------------------------------------------- //! Returns the renderer id. unsigned long TRenderer::rendererId() { return m_imp->m_rendererId; } //--------------------------------------------------------- //! Returns the rendering process id currently running on the invoking //! thread. unsigned long TRenderer::renderId() { unsigned long *lData = renderIdsStorage.localData(); return lData ? *lData : -1; } //--------------------------------------------------------- //! Builds and returns an id for starting a new rendering process. unsigned long TRenderer::buildRenderId() { return TRendererImp::m_renderIdCounter++; } //--------------------------------------------------------- //! Returns the renderId that will be used by the next startRendering() call. //! This method can be used to retrieve the renderId of a rendering instance //! before it is actually started - provided that no other render instance //! is launched inbetween. unsigned long TRenderer::nextRenderId() { return TRendererImp::m_renderIdCounter; } //--------------------------------------------------------- inline void TRendererImp::declareRenderStart(unsigned long renderId) { //Inform the resource managers for (unsigned int i = 0; i < m_managers.size(); ++i) m_managers[i]->onRenderInstanceStart(renderId); } //! Declares that the render process of passed renderId is about to start. void TRenderer::declareRenderStart(unsigned long renderId) { m_imp->declareRenderStart(renderId); } //--------------------------------------------------------- inline void TRendererImp::declareRenderEnd(unsigned long renderId) { //Inform the resource managers for (int i = m_managers.size() - 1; i >= 0; --i) m_managers[i]->onRenderInstanceEnd(renderId); } //! Declares that the render process of passed renderId has ended. void TRenderer::declareRenderEnd(unsigned long renderId) { m_imp->declareRenderEnd(renderId); } //--------------------------------------------------------- inline void TRendererImp::declareFrameStart(double frame) { //Inform the resource managers for (unsigned int i = 0; i < m_managers.size(); ++i) m_managers[i]->onRenderFrameStart(frame); } //! Declares that render of passed frame is about to start. void TRenderer::declareFrameStart(double frame) { m_imp->declareFrameStart(frame); } //--------------------------------------------------------- inline void TRendererImp::declareFrameEnd(double frame) { //Inform the resource managers for (int i = m_managers.size() - 1; i >= 0; --i) m_managers[i]->onRenderFrameEnd(frame); } //! Declares that render of passed has ended. void TRenderer::declareFrameEnd(double frame) { m_imp->declareFrameEnd(frame); } //--------------------------------------------------------- inline void TRendererImp::declareStatusStart(int renderStatus) { //Inform the resource managers for (unsigned int i = 0; i < m_managers.size(); ++i) m_managers[i]->onRenderStatusStart(renderStatus); } //--------------------------------------------------------- inline void TRendererImp::declareStatusEnd(int renderStatus) { //Inform the resource managers for (int i = m_managers.size() - 1; i >= 0; --i) m_managers[i]->onRenderStatusEnd(renderStatus); } //--------------------------------------------------------- //! Installs the specified render process on the invoking thread. void TRenderer::install(unsigned long renderId) { m_imp->addRef(); rendererStorage.setLocalData(new (TRendererImp *)(m_imp)); renderIdsStorage.setLocalData(new unsigned long(renderId)); } //--------------------------------------------------------- //! Uninstalls any rendering process active on the invoking thread. void TRenderer::uninstall() { rendererStorage.setLocalData(0); renderIdsStorage.setLocalData(0); m_imp->release(); } //--------------------------------------------------------- TRenderResourceManager *TRenderer::getManager(unsigned int id) const { return m_imp->m_managers[id]; } //--------------------------------------------------------- void TRenderer::enablePrecomputing(bool on) { m_imp->enablePrecomputing(on); } //--------------------------------------------------------- bool TRenderer::isPrecomputingEnabled() const { return m_imp->isPrecomputingEnabled(); } //--------------------------------------------------------- void TRenderer::setThreadsCount(int nThreads) { m_imp->setThreadsCount(nThreads); } //--------------------------------------------------------- void TRenderer::addPort(TRenderPort *port) { m_imp->addPort(port); } //--------------------------------------------------------- void TRenderer::removePort(TRenderPort *port) { m_imp->removePort(port); } //--------------------------------------------------------- unsigned long TRenderer::startRendering( double f, const TRenderSettings &info, const TFxPair &actualRoot) { assert(f >= 0); std::vector *rds = new std::vector; rds->push_back(RenderData(f, info, actualRoot)); return startRendering(rds); } //--------------------------------------------------------- //! Queues a rendering event in the main event loop. //! NOTE: The passed pointer is owned by the TRenderer after it is submitted for rendering - //! do not delete it later. unsigned long TRenderer::startRendering(const std::vector *renderDatas) { if (renderDatas->empty()) { delete renderDatas; return -1; } //Build a new render Id unsigned long renderId = m_imp->m_renderIdCounter++; TRendererStartInvoker::StartInvokerRenderData srd; srd.m_renderId = renderId; srd.m_renderDataVector = renderDatas; TRendererStartInvoker::instance()->emitStartRender(m_imp, srd); return renderId; } //--------------------------------------------------------- void TRenderer::abortRendering(unsigned long renderId) { m_imp->abortRendering(renderId); } //--------------------------------------------------------- void TRenderer::stopRendering(bool waitForCompleteStop) { m_imp->stopRendering(waitForCompleteStop); } //--------------------------------------------------------- bool TRenderer::isAborted(unsigned long renderId) const { return m_imp->hasToDie(renderId); } //--------------------------------------------------------- int TRenderer::getRenderStatus(unsigned long renderId) const { return m_imp->getRenderStatus(renderId); } //================================================================================ //===================== // TRendererImp //--------------------- TRendererImp::TRendererImp(int nThreads) : m_executor(), m_undoneTasks(), m_rendererId(m_rendererIdCounter++), m_precomputingEnabled(true) { m_executor.setMaxActiveTasks(nThreads); std::vector &generators = TRenderResourceManagerGenerator::generators(false); //May be adopted by other TRenderers from now on. addRef(); rendererStorage.setLocalData(new (TRendererImp *)(this)); unsigned int i; for (i = 0; i < generators.size(); ++i) { TRenderResourceManager *manager = (*generators[i])(); if (manager) m_managers.push_back(manager); } rendererStorage.setLocalData(0); } //--------------------------------------------------------- TRendererImp::~TRendererImp() { rendererStorage.setLocalData(new (TRendererImp *)(this)); int i; for (i = m_managers.size() - 1; i >= 0; --i) if (m_managers[i]->renderHasOwnership()) delete m_managers[i]; rendererStorage.setLocalData(0); } //--------------------------------------------------------- void TRendererImp::addPort(TRenderPort *port) { QWriteLocker sl(&m_portsLock); PortContainerIterator it = std::find(m_ports.begin(), m_ports.end(), port); if (it == m_ports.end()) m_ports.push_back(port); } //--------------------------------------------------------- void TRendererImp::removePort(TRenderPort *port) { QWriteLocker sl(&m_portsLock); PortContainerIterator it = std::find(m_ports.begin(), m_ports.end(), port); if (it != m_ports.end()) m_ports.erase(it); } //--------------------------------------------------------- bool TRendererImp::hasToDie(unsigned long renderId) { QMutexLocker sl(&m_renderInstancesMutex); std::map::iterator it = m_activeInstances.find(renderId); assert(it != m_activeInstances.end()); return it == m_activeInstances.end() ? true : it->second.m_canceled; } //--------------------------------------------------------- int TRendererImp::getRenderStatus(unsigned long renderId) { QMutexLocker sl(&m_renderInstancesMutex); std::map::iterator it = m_activeInstances.find(renderId); assert(it != m_activeInstances.end()); return it == m_activeInstances.end() ? true : it->second.m_status; } //--------------------------------------------------------- void TRendererImp::abortRendering(unsigned long renderId) { QMutexLocker sl(&m_renderInstancesMutex); std::map::iterator it = m_activeInstances.find(renderId); if (it != m_activeInstances.end()) it->second.m_canceled = true; } //--------------------------------------------------------- void TRendererImp::stopRendering(bool waitForCompleteStop) { QMutexLocker sl(&m_renderInstancesMutex); { //Tasks already stop rendering on their own when they don't find their render ids here. std::map::iterator it; for (it = m_activeInstances.begin(); it != m_activeInstances.end(); ++it) it->second.m_canceled = true; } if (waitForCompleteStop && m_undoneTasks > 0) { //Sometimes, QEventLoop suddenly stops processing slots (especially those notifications //from active rendering instances) - therefore resulting in a block of the application. //I've not figured out why (#QTBUG-11649?) - but substituting with a plain while //seems to do the trick... /*QEventLoop eventLoop; m_waitingLoops.push_back(&eventLoop); eventLoop.exec();*/ bool loopQuit = false; m_waitingLoops.push_back(&loopQuit); sl.unlock(); while (!loopQuit) QCoreApplication::processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents); } } //--------------------------------------------------------- void TRendererImp::quitWaitingLoops() { //Make the stopRendering waiting loops quit while (!m_waitingLoops.empty()) { //rendererImp->m_waitingLoops.back()->quit(); *m_waitingLoops.back() = true; m_waitingLoops.pop_back(); } } //--------------------------------------------------------- void TRendererImp::notifyRasterStarted(const TRenderPort::RenderData &rd) { //Since notifications may trigger port removals, we always work on a copy of the ports //vector. TRendererImp::PortContainer portsCopy; { QReadLocker sl(&m_portsLock); portsCopy = m_ports; } for (PortContainerIterator it = portsCopy.begin(); it != portsCopy.end(); ++it) (*it)->onRenderRasterStarted(rd); } //--------------------------------------------------------- void TRendererImp::notifyRasterCompleted(const TRenderPort::RenderData &rd) { TRendererImp::PortContainer portsCopy; { QReadLocker sl(&m_portsLock); portsCopy = m_ports; } assert(rd.m_rasA); for (PortContainerIterator it = portsCopy.begin(); it != portsCopy.end(); ++it) (*it)->onRenderRasterCompleted(rd); } //--------------------------------------------------------- void TRendererImp::notifyRasterFailure(const TRenderPort::RenderData &rd, TException &e) { TRendererImp::PortContainer portsCopy; { QReadLocker sl(&m_portsLock); portsCopy = m_ports; } for (PortContainerIterator it = portsCopy.begin(); it != portsCopy.end(); ++it) (*it)->onRenderFailure(rd, e); } //--------------------------------------------------------- void TRendererImp::notifyRenderFinished(bool isCanceled) { TRendererImp::PortContainer portsCopy; { QReadLocker sl(&m_portsLock); portsCopy = m_ports; } auto sortedFxs = calculateSortedFxs(rootFx); for (auto fx : sortedFxs) { if (fx) const_cast(fx)->callEndRenderHandler(); } for (PortContainerIterator it = portsCopy.begin(); it != portsCopy.end(); ++it) (*it)->onRenderFinished(); } //================================================================================ //==================== // TRenderPort //-------------------- TRenderPort::TRenderPort() { } //--------------------------------------------------------- TRenderPort::~TRenderPort() { } //--------------------------------------------------------- //!Setta \b m_renderArea a \b area e pulisce l'istanza corrente di \b RasterPool. void TRenderPort::setRenderArea(const TRectD &area) { m_renderArea = area; } //--------------------------------------------------------- //!Ritorna \b m_renderArea. TRectD &TRenderPort::getRenderArea() { return m_renderArea; } //================================================================================ //=================== // RenderTask //------------------- RenderTask::RenderTask(unsigned long renderId, unsigned long taskId, double frame, const TRenderSettings &ri, const TFxPair &fx, const TPointD &framePos, const TDimension &frameSize, const TRendererImpP &rendererImp) : m_renderId(renderId), m_taskId(taskId), m_info(ri), m_fx(fx), m_frameSize(frameSize), m_framePos(framePos), m_rendererImp(rendererImp), m_fieldRender(ri.m_fieldPrevalence != TRenderSettings::NoField), m_stereoscopic(ri.m_stereoscopic) { m_frames.push_back(frame); //Connect the onFinished slot connect(this, SIGNAL(finished(TThread::RunnableP)), this, SLOT(onFinished(TThread::RunnableP))); connect(this, SIGNAL(exception(TThread::RunnableP)), this, SLOT(onFinished(TThread::RunnableP))); //The shrink info is currently reversed to the settings'affine. Shrink info in the TRenderSettings //is no longer supported. m_info.m_shrinkX = m_info.m_shrinkY = 1; } //--------------------------------------------------------- void RenderTask::preRun() { TRectD geom(m_framePos, TDimensionD(m_frameSize.lx, m_frameSize.ly)); if (m_fx.m_frameA) m_fx.m_frameA->dryCompute(geom, m_frames[0], m_info); if (m_fx.m_frameB) m_fx.m_frameB->dryCompute(geom, m_fieldRender ? m_frames[0] + 0.5 : m_frames[0], m_info); } //--------------------------------------------------------- void RenderTask::run() { //Retrieve the task's frame assert(!m_frames.empty()); double t = m_frames[0]; if (m_rendererImp->hasToDie(m_renderId)) { TException e("Render task aborted"); onFrameFailed(e); return; } //Install the renderer in current thread rendererStorage.setLocalData(new (TRendererImp *)(m_rendererImp.getPointer())); renderIdsStorage.setLocalData(new unsigned long(m_renderId)); //Inform the managers of frame start m_rendererImp->declareFrameStart(t); auto sortedFxs = calculateSortedFxs(m_fx.m_frameA); for (auto fx : sortedFxs) { if (fx) const_cast(fx)->callStartRenderFrameHandler(&m_info, t); } try { onFrameStarted(); TStopWatch::global(8).start(); if (!m_fieldRender && !m_stereoscopic) { //Common case - just build the first tile buildTile(m_tileA); /*-- 通常はここがFxのレンダリング処理 --*/ m_fx.m_frameA->compute(m_tileA, t, m_info); } else { assert(!(m_stereoscopic && m_fieldRender)); //Field rendering or stereoscopic case if (m_stereoscopic) { buildTile(m_tileA); m_fx.m_frameA->compute(m_tileA, t, m_info); buildTile(m_tileB); m_fx.m_frameB->compute(m_tileB, t, m_info); } //if fieldPrevalence, Decide the rendering frames depending on field prevalence else if (m_info.m_fieldPrevalence == TRenderSettings::EvenField) { buildTile(m_tileA); m_fx.m_frameA->compute(m_tileA, t, m_info); buildTile(m_tileB); m_fx.m_frameB->compute(m_tileB, t + 0.5, m_info); } else { buildTile(m_tileB); m_fx.m_frameA->compute(m_tileB, t, m_info); buildTile(m_tileA); m_fx.m_frameB->compute(m_tileA, t + 0.5, m_info); } } TStopWatch::global(8).stop(); onFrameCompleted(); } catch (TException &e) { onFrameFailed(e); } catch (...) { TException ex("Unknown render exception"); onFrameFailed(ex); } //Inform the managers of frame end m_rendererImp->declareFrameEnd(t); //Uninstall the renderer from current thread rendererStorage.setLocalData(0); renderIdsStorage.setLocalData(0); for (auto fx : sortedFxs) { if (fx) const_cast(fx)->callEndRenderFrameHandler(&m_info, t); } } //--------------------------------------------------------- void RenderTask::buildTile(TTile &tile) { tile.m_pos = m_framePos; tile.setRaster(m_rendererImp->m_rasterPool.getRaster(m_frameSize, m_info.m_bpp)); } //--------------------------------------------------------- void RenderTask::releaseTiles() { m_rendererImp->m_rasterPool.releaseRaster(m_tileA.getRaster()); m_tileA.setRaster(TRasterP()); if (m_fieldRender || m_stereoscopic) { m_rendererImp->m_rasterPool.releaseRaster(m_tileB.getRaster()); m_tileB.setRaster(TRasterP()); } } //--------------------------------------------------------- void RenderTask::onFrameStarted() { TRenderPort::RenderData rd(m_frames, m_info, 0, 0, m_renderId, m_taskId); m_rendererImp->notifyRasterStarted(rd); } //--------------------------------------------------------- void RenderTask::onFrameCompleted() { TRasterP rasA(m_tileA.getRaster()); TRasterP rasB(m_tileB.getRaster()); if (m_fieldRender) { assert(rasB); double t = m_frames[0]; int f = (m_info.m_fieldPrevalence == TRenderSettings::EvenField) ? 0 : 1; interlace(rasA, rasB, f); rasB = TRasterP(); } TRenderPort::RenderData rd(m_frames, m_info, rasA, rasB, m_renderId, m_taskId); m_rendererImp->notifyRasterCompleted(rd); } //--------------------------------------------------------- void RenderTask::onFrameFailed(TException &e) { //TRasterP evenRas(m_evenTile.getRaster()); TRenderPort::RenderData rd(m_frames, m_info, m_tileA.getRaster(), m_tileB.getRaster(), m_renderId, m_taskId); m_rendererImp->notifyRasterFailure(rd, e); } //--------------------------------------------------------- void RenderTask::onFinished(TThread::RunnableP) { TRendererImp *rendererImp = m_rendererImp.getPointer(); --rendererImp->m_undoneTasks; //Tiles release back to the Raster Pool happens in the main thread, after all possible //signals emitted in the onFrameCompleted/Failed notifications have been resolved, thus //ensuring that no other rendering thread owns the rasters before them. releaseTiles(); //Update the render instance status bool instanceExpires = false; { QMutexLocker sl(&rendererImp->m_renderInstancesMutex); std::map::iterator it = rendererImp->m_activeInstances.find(m_renderId); if (it != rendererImp->m_activeInstances.end() && (--it->second.m_activeTasks) <= 0) { instanceExpires = true; rendererImp->m_activeInstances.erase(m_renderId); } } //If the render instance has just expired if (instanceExpires) { /*-- キャンセルされた場合はm_overallRenderedRegionの更新をしない --*/ bool isCanceled = (m_info.m_isCanceled && *m_info.m_isCanceled); //Inform the render ports rendererImp->notifyRenderFinished(isCanceled); //NOTE: This slot is currently invoked on the main thread. It could eventually be //invoked directly on rendering threads, specifying the Qt::DirectConnection option - //but probably there would be no real advantage in doing so... //Temporarily install the renderer in current thread rendererStorage.setLocalData(new (TRendererImp *)(rendererImp)); renderIdsStorage.setLocalData(new unsigned long(m_renderId)); //Inform the resource managers rendererImp->declareRenderEnd(m_renderId); //Uninstall the temporary rendererStorage.setLocalData(0); renderIdsStorage.setLocalData(0); rendererImp->m_rasterPool.clear(); // Isn't this misplaced? Should be in the block } // below... //If no rendering task (of this or other render instances) is found... if (rendererImp->m_undoneTasks == 0) { QMutexLocker sl(&rendererImp->m_renderInstancesMutex); rendererImp->quitWaitingLoops(); } } //================================================================================ // Tough Stuff //================================================================================ void TRendererStartInvoker::emitStartRender( TRendererImp *renderer, StartInvokerRenderData rd) { renderer->addRef(); Q_EMIT startRender(renderer, rd); } //--------------------------------------------------------- void TRendererStartInvoker::doStartRender( TRendererImp *renderer, StartInvokerRenderData rd) { renderer->startRendering(rd.m_renderId, *rd.m_renderDataVector); renderer->release(); delete rd.m_renderDataVector; } std::vector calculateSortedFxs(TRasterFxP rootFx) { std::map> E; /* 辺の情報 */ std::set Sources; /* 入次数0のノード群 */ std::queue Q; Q.push(rootFx.getPointer()); E[rootFx.getPointer()] = std::set(); while (!Q.empty()) { const TFx *vptr = Q.front(); Q.pop(); if (!vptr) { continue; } /* 繋がっている入力ポートの先の Fx を訪問する 入力ポートが無ければ終了 */ int portCount = vptr->getInputPortCount(); if (portCount < 1) { Sources.insert(vptr); continue; } for (int i = 0; i < portCount; i++) { TFxPort *port = vptr->getInputPort(i); if (!port) { continue; } TFxP u = port->getFx(); const TFx *uptr = u.getPointer(); if (E.count(uptr) == 0) { E[uptr] = std::set(); } if (E[uptr].count(vptr) == 0) { E[uptr].insert(vptr); } Q.push(uptr); } } /* トポロジカルソート */ std::set visited; std::vector L; std::function visit = [&visit, &visited, &E, &L](const TFx *fx) { if (visited.count(fx)) return; visited.insert(fx); auto edge = E[fx]; for (auto i = edge.cbegin(); i != edge.cend(); i++) { visit(*i); } L.insert(L.begin(), fx); }; for (auto i = E.cbegin(); i != E.cend(); i++) { visit(i->first); } return L; } //--------------------------------------------------------- void TRendererImp::startRendering(unsigned long renderId, const vector &renderDatas) { rootFx = renderDatas.front().m_fxRoot.m_frameA; int T = renderDatas.size(); for (int z = 0; z < T; z++) { auto sortedFxs = calculateSortedFxs(renderDatas[z].m_fxRoot.m_frameA); if (z == 0) { for (auto fx : sortedFxs) { if (fx) const_cast(fx)->callStartRenderHandler(); } } } struct locals { static inline void setStorage(TRendererImp *imp, unsigned long renderId) { rendererStorage.setLocalData(new (TRendererImp *)(imp)); renderIdsStorage.setLocalData(new unsigned long(renderId)); } static inline void clearStorage() { rendererStorage.setLocalData(0); renderIdsStorage.setLocalData(0); } static inline void declareStatusStart( TRendererImp *imp, TRenderer::RenderStatus status, RenderInstanceInfos *renderInfos) { renderInfos->m_status = status; imp->declareStatusStart(status); } //----------------------------------------------------------------------- struct InstanceDeclaration { TRendererImp *m_imp; unsigned long m_renderId; bool m_rollback; InstanceDeclaration(TRendererImp *imp, unsigned long renderId) : m_imp(imp), m_renderId(renderId), m_rollback(true) {} ~InstanceDeclaration() { if (m_rollback) { QMutexLocker locker(&m_imp->m_renderInstancesMutex); m_imp->m_activeInstances.erase(m_renderId); if (m_imp->m_undoneTasks == 0) m_imp->quitWaitingLoops(); } } void commit() { m_rollback = false; } }; struct StorageDeclaration { StorageDeclaration(TRendererImp *imp, unsigned long renderId) { setStorage(imp, renderId); } ~StorageDeclaration() { clearStorage(); } }; struct RenderDeclaration { TRendererImp *m_imp; unsigned long m_renderId; bool m_rollback; RenderDeclaration(TRendererImp *imp, unsigned long renderId) : m_imp(imp), m_renderId(renderId), m_rollback(true) { imp->declareRenderStart(renderId); } ~RenderDeclaration() { if (m_rollback) m_imp->declareRenderEnd(m_renderId); } void commit() { m_rollback = false; } }; struct StatusDeclaration { TRendererImp *m_imp; TRenderer::RenderStatus m_status; StatusDeclaration(TRendererImp *imp, TRenderer::RenderStatus status, RenderInstanceInfos *renderInfos) : m_imp(imp), m_status(status) { declareStatusStart(imp, status, renderInfos); } ~StatusDeclaration() { m_imp->declareStatusEnd(m_status); } }; }; // locals //DIAGNOSTICS_CLEAR; //---------------------------------------------------------------------- // Preliminary initializations //---------------------------------------------------------------------- // Calculate the overall render area - sum of all render ports' areas TRectD renderArea; { QReadLocker sl(&m_portsLock); for (PortContainerIterator it = m_ports.begin(); it != m_ports.end(); ++it) renderArea += (*it)->getRenderArea(); } const TRenderSettings &info(renderDatas[0].m_info); //Extract the render geometry TPointD pos(renderArea.getP00()); TDimension frameSize(tceil(renderArea.getLx()), tceil(renderArea.getLy())); TRectD camBox(TPointD(pos.x / info.m_shrinkX, pos.y / info.m_shrinkY), TDimensionD(frameSize.lx, frameSize.ly)); //Refresh the raster pool specs m_rasterPool.setRasterSpecs(frameSize, info.m_bpp); //Set a temporary active instance count - so that hasToDie(renderId) returns false RenderInstanceInfos *renderInfos; { QMutexLocker locker(&m_renderInstancesMutex); renderInfos = &m_activeInstances[renderId]; renderInfos->m_activeTasks = 1; } locals::InstanceDeclaration instanceDecl(this, renderId); //---------------------------------------------------------------------- // Clustering - Render Tasks creation //---------------------------------------------------------------------- std::vector tasksVector; struct TasksCleaner { std::vector &m_tasksVector; ~TasksCleaner() { std::for_each(m_tasksVector.begin(), m_tasksVector.end(), tcg::deleter()); } } tasksCleaner = {tasksVector}; unsigned long tasksIdCounter = 0; std::map clusters; std::vector::const_iterator it; std::map::iterator jt; for (it = renderDatas.begin(); it != renderDatas.end(); ++it) { // Check for user cancels if (hasToDie(renderId)) return; // Build the frame's description alias const TRenderer::RenderData &renderData = *it; /*--- カメラサイズ (LevelAutoやノイズで使用する) ---*/ TRenderSettings rs = renderData.m_info; rs.m_cameraBox = camBox; /*--- 途中でPreview計算がキャンセルされたときのフラグ ---*/ rs.m_isCanceled = &renderInfos->m_canceled; TRasterFxP fx = renderData.m_fxRoot.m_frameA; assert(fx); double frame = renderData.m_frame; string alias = fx->getAlias(frame, renderData.m_info); if (renderData.m_fxRoot.m_frameB) alias = alias + renderData.m_fxRoot.m_frameB->getAlias(frame, renderData.m_info); // Search the alias among stored clusters - and store the frame jt = clusters.find(alias); if (jt == clusters.end()) { RenderTask *newTask = new RenderTask( renderId, tasksIdCounter++, renderData.m_frame, rs, renderData.m_fxRoot, pos, frameSize, this); tasksVector.push_back(newTask); clusters.insert(std::make_pair(alias, newTask)); } else jt->second->addFrame(renderData.m_frame); // Call processEvents to make the GUI reactive. QCoreApplication::instance()->processEvents(); } // Release the clusters - we'll just need the tasks vector from now on clusters.clear(); std::vector::iterator kt, kEnd = tasksVector.end(); { // Install TRenderer on current thread before proceeding locals::StorageDeclaration storageDecl(this, renderId); // Inform the resource managers locals::RenderDeclaration renderDecl(this, renderId); //---------------------------------------------------------------------- // Precomputing //---------------------------------------------------------------------- if (m_precomputingEnabled) { //Set current maxTileSize for cache manager precomputation const TRenderSettings &rs = renderDatas[0].m_info; TPredictiveCacheManager::instance()->setMaxTileSize(rs.m_maxTileSize); TPredictiveCacheManager::instance()->setBPP(rs.m_bpp); //Perform the first precomputing run - fx usages declaration { locals::StatusDeclaration firstrunDecl(this, TRenderer::FIRSTRUN, renderInfos); for (kt = tasksVector.begin(); kt != kEnd; ++kt) { if (hasToDie(renderId)) return; (*kt)->preRun(); //NOTE: Thread-specific data must be temporarily uninstalled before //processing events (which may redefine the thread data). locals::clearStorage(); QCoreApplication::instance()->processEvents(); locals::setStorage(this, renderId); } } //Pass to the TESTRUN status - this one should faithfully reproduce //the actual COMPUTING status { locals::StatusDeclaration testrunDecl(this, TRenderer::TESTRUN, renderInfos); for (kt = tasksVector.begin(); kt != kEnd; ++kt) { if (hasToDie(renderId)) return; (*kt)->preRun(); //NOTE: Thread-specific data must be temporarily uninstalled before //processing events (which may redefine the thread data). locals::clearStorage(); QCoreApplication::instance()->processEvents(); locals::setStorage(this, renderId); } } } //---------------------------------------------------------------------- // Render //---------------------------------------------------------------------- locals::declareStatusStart(this, TRenderer::COMPUTING, renderInfos); // Update the tasks counts m_undoneTasks += tasksVector.size(); { QMutexLocker locker(&m_renderInstancesMutex); renderInfos->m_activeTasks = tasksVector.size(); } renderDecl.commit(); // Declarations are taken over by render tasks } instanceDecl.commit(); // Same here // Launch the render for (kt = tasksVector.begin(); kt != tasksVector.end(); ++kt) m_executor.addTask(*kt); tasksVector.clear(); // Prevent tasks destruction by TasksCleaner } void TRenderer::initialize() { TRendererStartInvoker::instance(); }