2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include "trenderer.h"
|
|
|
|
#include "tcacheresourcepool.h"
|
|
|
|
|
|
|
|
#include "tfxcachemanager.h"
|
|
|
|
|
|
|
|
//Debug
|
|
|
|
//#define DIAGNOSTICS
|
|
|
|
#ifdef DIAGNOSTICS
|
|
|
|
|
|
|
|
#include "diagnostics.h"
|
|
|
|
|
|
|
|
//#define WRITESTACK
|
|
|
|
//#define WRITESUBRECTS
|
|
|
|
//#define WRITEGENERAL
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
QString traduce(const TRectD &rect)
|
|
|
|
{
|
|
|
|
return "[" + QString::number(rect.x0) + " " + QString::number(rect.y0) + " " + QString::number(rect.x1) + " " + QString::number(rect.y1) + "]";
|
|
|
|
}
|
|
|
|
|
|
|
|
QString traduce(const TTile &tile)
|
|
|
|
{
|
|
|
|
TDimension dim(tile.getRaster()->getSize());
|
|
|
|
TRectD tileRect(tile.m_pos, TDimensionD(dim.lx, dim.ly));
|
|
|
|
return traduce(tileRect);
|
|
|
|
}
|
|
|
|
|
|
|
|
QString prefixInfo("#info.txt | ");
|
|
|
|
QString prefixWarn("#warning.txt | ");
|
|
|
|
QString prefixErr("#error.txt | ");
|
|
|
|
QString prefixTest("#TestRun.txt | ");
|
|
|
|
QString prefixComp("#Computing.txt | ");
|
|
|
|
QString prefixSubTiles("#SubTiles.txt | ");
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif //DIAGNOSTICS
|
|
|
|
|
|
|
|
//****************************************************************************************************
|
|
|
|
// Explanation
|
|
|
|
//****************************************************************************************************
|
|
|
|
|
|
|
|
/*
|
|
|
|
This file contains most of the code that deals with smart caching during a Toonz render process.
|
|
|
|
|
|
|
|
The paradigms on which the 'smart caching' takes place are:
|
|
|
|
|
|
|
|
- Only calculations resulting in an image positioned in a plane are dealt. These results are
|
|
|
|
called 'tiles' - and are modeled by a TTile instance.
|
|
|
|
These images MUST BE wrapped to INTEGER POSITIONS on the reference, and are intended so that
|
|
|
|
a PIXEL corresponds to a UNIT SQUARE.
|
|
|
|
- Given one such calculation procedure, it MUST BE SIMULABLE, so that children calculations
|
|
|
|
are invoked 'faithfully' with respect to the actual calculation.
|
|
|
|
This is necessary to predict results in advance in force of which we can efficiently store
|
|
|
|
results in the cache, releasing them when they will no longer be required.
|
|
|
|
|
|
|
|
Now, the principal classes dealing with this job are:
|
|
|
|
|
|
|
|
- One TFxCacheManager per render instance, used to store predictive informations.
|
|
|
|
- A set of TFxCacheManagerDelegate per render instance, used to store references to cache resources
|
|
|
|
and providing reasons for caching results.
|
|
|
|
- The ResourceBuilder interface class, used by users to access the smart caching ensemble. This class
|
|
|
|
implements the actual resources build and simulation code.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
//****************************************************************************************************
|
|
|
|
// Preliminaries
|
|
|
|
//****************************************************************************************************
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
//Global variables
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
//Utility functions
|
|
|
|
|
|
|
|
inline QRect toQRect(const TRect &r) { return QRect(r.x0, r.y0, r.getLx(), r.getLy()); }
|
|
|
|
inline TRect toTRect(const QRect &r) { return TRect(r.left(), r.top(), r.right(), r.bottom()); }
|
|
|
|
inline QPoint toQPoint(const TPoint &p) { return QPoint(p.x, p.y); }
|
|
|
|
|
|
|
|
inline bool isEmpty(const TRectD &rect) { return rect.x0 >= rect.x1 || rect.y0 >= rect.y1; }
|
|
|
|
inline void enlargeToI(TRectD &r)
|
|
|
|
{
|
|
|
|
TRectD temp(tfloor(r.x0), tfloor(r.y0), tceil(r.x1), tceil(r.y1));
|
|
|
|
if (!isEmpty(temp))
|
|
|
|
r = temp; //Since r could have TConsts::infiniteRectD-like coordinates...
|
|
|
|
}
|
|
|
|
|
|
|
|
//Qt's contains actually returns QRegion::intersected... I wonder why...
|
|
|
|
inline bool contains(const QRegion ®ion, const TRect &rect)
|
|
|
|
{
|
|
|
|
return QRegion(toQRect(rect)).subtracted(region).isEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool getTilesToBuild(const ResourceData &data, const TRectD &rect,
|
|
|
|
std::vector<ResourceDeclaration::TileData *> &rectsToCalculate);
|
|
|
|
}
|
|
|
|
|
|
|
|
//****************************************************************************************************
|
|
|
|
// TFxCacheManager Generator
|
|
|
|
//****************************************************************************************************
|
|
|
|
|
|
|
|
class TFxCacheManagerGenerator : public TRenderResourceManagerGenerator
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
TFxCacheManagerGenerator() : TRenderResourceManagerGenerator(true) {}
|
|
|
|
|
|
|
|
TRenderResourceManager *operator()() { return new TFxCacheManager; }
|
|
|
|
};
|
|
|
|
|
|
|
|
MANAGER_FILESCOPE_DECLARATION(TFxCacheManager, TFxCacheManagerGenerator);
|
|
|
|
|
|
|
|
//****************************************************************************************************
|
|
|
|
// TFxCacheManager implementation
|
|
|
|
//****************************************************************************************************
|
|
|
|
|
|
|
|
class TFxCacheManager::Imp
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
typedef std::map<std::string, ResourceDeclaration> ResourceInstanceDataMap;
|
|
|
|
|
|
|
|
ResourceInstanceDataMap m_resourcesData;
|
|
|
|
std::map<ResourceDeclaration *, ResourceDeclaration::RawData> m_rawData;
|
|
|
|
int m_renderStatus;
|
|
|
|
|
|
|
|
QMutex m_mutex;
|
|
|
|
|
|
|
|
public:
|
|
|
|
void prepareTilesToCalculate(ResourceDeclaration &data);
|
|
|
|
inline void subdivideIntoSmallerTiles(const TRectD &rect, std::vector<TRectD> &tileSet);
|
|
|
|
void recursiveRectSubdivide(
|
|
|
|
std::vector<ResourceDeclaration::TileData> &results, TRasterFx *fx,
|
|
|
|
const TRectD &rect, double frame, const TRenderSettings &info,
|
|
|
|
int dropTol = (std::numeric_limits<int>::max)());
|
|
|
|
};
|
|
|
|
|
|
|
|
//****************************************************************************************************
|
|
|
|
// Methods implementation
|
|
|
|
//****************************************************************************************************
|
|
|
|
|
|
|
|
//========================
|
|
|
|
// TFxCacheManager
|
|
|
|
//------------------------
|
|
|
|
|
|
|
|
TFxCacheManager::TFxCacheManager()
|
|
|
|
: m_imp(new Imp)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
TFxCacheManager::~TFxCacheManager()
|
|
|
|
{
|
|
|
|
//Release all the static-cached images
|
|
|
|
std::set<std::string>::iterator it;
|
|
|
|
for (it = m_staticCacheIds.begin(); it != m_staticCacheIds.end(); ++it)
|
|
|
|
TImageCache::instance()->remove(*it);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
TFxCacheManager *TFxCacheManager::instance()
|
|
|
|
{
|
|
|
|
return static_cast<TFxCacheManager *>(
|
|
|
|
TFxCacheManager::gen()->getManager(TRenderer::renderId()));
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void TFxCacheManager::add(const std::string &cacheId, TImageP img)
|
|
|
|
{
|
|
|
|
TImageCache::instance()->add(cacheId, img);
|
|
|
|
|
|
|
|
QMutexLocker locker(&m_imp->m_mutex);
|
|
|
|
m_staticCacheIds.insert(cacheId);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void TFxCacheManager::remove(const std::string &cacheId)
|
|
|
|
{
|
|
|
|
TImageCache::instance()->remove(cacheId);
|
|
|
|
|
|
|
|
QMutexLocker locker(&m_imp->m_mutex);
|
|
|
|
m_staticCacheIds.erase(cacheId);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void TFxCacheManager::install(TFxCacheManagerDelegate *managerDelegate)
|
|
|
|
{
|
|
|
|
m_delegates.insert(managerDelegate);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
/*void TFxCacheManager::install(TFxCacheManagerListener* listener)
|
|
|
|
{
|
|
|
|
m_listeners.insert(listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void TFxCacheManager::notifyResourceUpload(const TCacheResourceP& resource, const TRect& rect)
|
|
|
|
{
|
|
|
|
std::set<TFxCacheManagerListener*>::iterator it;
|
|
|
|
for(it = m_listeners.begin(); it != m_listeners.end(); ++it)
|
|
|
|
(*it)->onResourceUpload(resource, rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void TFxCacheManager::notifyResourceDownload(const TCacheResourceP& resource, const TRect& rect)
|
|
|
|
{
|
|
|
|
std::set<TFxCacheManagerListener*>::iterator it;
|
|
|
|
for(it = m_listeners.begin(); it != m_listeners.end(); ++it)
|
|
|
|
(*it)->onResourceDownload(resource, rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void TFxCacheManager::notifyPredictedRelease(const TCacheResourceP& resource)
|
|
|
|
{
|
|
|
|
std::set<TFxCacheManagerListener*>::iterator it;
|
|
|
|
for(it = m_listeners.begin(); it != m_listeners.end(); ++it)
|
|
|
|
(*it)->onPredictedRelease(resource);
|
|
|
|
}*/
|
|
|
|
|
|
|
|
//****************************************************************************************************
|
|
|
|
// Resources dealing
|
|
|
|
//****************************************************************************************************
|
|
|
|
|
|
|
|
void TFxCacheManager::declareResource(
|
2016-04-19 19:32:17 +12:00
|
|
|
const std::string &alias, const TFxP &fx,
|
2016-03-19 06:57:51 +13:00
|
|
|
const TRectD &rect, double frame, const TRenderSettings &rs,
|
|
|
|
bool subtileable)
|
|
|
|
{
|
|
|
|
Imp::ResourceInstanceDataMap::iterator it;
|
|
|
|
it = m_imp->m_resourcesData.insert(std::make_pair(alias, ResourceDeclaration())).first;
|
|
|
|
it->second.m_rawData =
|
|
|
|
&m_imp->m_rawData.insert(std::make_pair(&it->second, ResourceDeclaration::RawData())).first->second;
|
|
|
|
|
|
|
|
ResourceDeclaration::RawData &rawData = *it->second.m_rawData;
|
|
|
|
|
|
|
|
//Assign the sim data
|
|
|
|
rawData.m_fx = fx;
|
|
|
|
rawData.m_tiles.push_back(rect);
|
|
|
|
rawData.m_rs = rs;
|
|
|
|
rawData.m_frame = frame;
|
|
|
|
//rawData.m_bbox = bbox;
|
|
|
|
rawData.m_subtileable = subtileable;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
ResourceData TFxCacheManager::getResource(
|
2016-04-19 19:32:17 +12:00
|
|
|
const std::string &alias,
|
2016-03-19 06:57:51 +13:00
|
|
|
const TFxP &fx, double frame, const TRenderSettings &rs)
|
|
|
|
{
|
|
|
|
TCacheResourceP result, temp;
|
|
|
|
|
|
|
|
//Seek the associated infos
|
|
|
|
Imp::ResourceInstanceDataMap::iterator jt =
|
|
|
|
m_imp->m_resourcesData.find(alias);
|
|
|
|
ResourceDeclaration *decl = (jt == m_imp->m_resourcesData.end()) ? 0 : &jt->second;
|
|
|
|
|
|
|
|
//Search the resource in cached mode.
|
|
|
|
//TCacheResourcePool* pool = TCacheResourcePool::instance();
|
|
|
|
//pool->beginCachedSearch();
|
|
|
|
|
|
|
|
//Ask every installed delegate if it's managing - or want to manage
|
|
|
|
//the passed resource specs.
|
|
|
|
std::set<TFxCacheManagerDelegate *>::iterator it;
|
|
|
|
for (it = m_delegates.begin(); it != m_delegates.end(); ++it) {
|
|
|
|
(*it)->getResource(temp, alias, fx, frame, rs, decl);
|
|
|
|
if (!result && temp)
|
|
|
|
result = temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
//pool->endCachedSearch();
|
|
|
|
|
|
|
|
return ResourceData(decl, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void TFxCacheManager::onRenderStatusStart(int renderStatus)
|
|
|
|
{
|
|
|
|
//Store current render status
|
|
|
|
m_imp->m_renderStatus = renderStatus;
|
|
|
|
|
|
|
|
#ifdef WRITESTACK
|
|
|
|
if (renderStatus == TRenderer::TESTRUN) {
|
|
|
|
DIAGNOSTICS_GLOSTRSET("status", "test");
|
|
|
|
DIAGNOSTICS_GLOSET("testInst", DIAGNOSTICS_GLOGET("compInst") + 1);
|
|
|
|
DIAGNOSTICS_GLOSTRSET("instVar", QString::number(DIAGNOSTICS_GLOGET("testInst")));
|
|
|
|
DIAGNOSTICS_GLOSTRSET("testRenderStr", "Render #" + QString::number(DIAGNOSTICS_GLOGET("testInst")) + " | ");
|
|
|
|
} else if (renderStatus == TRenderer::COMPUTING) {
|
|
|
|
DIAGNOSTICS_GLOSTRSET("status", "comp");
|
|
|
|
DIAGNOSTICS_GLOSTRSET("instVar", QString::number(DIAGNOSTICS_GLOGET("compInst")));
|
|
|
|
DIAGNOSTICS_GLOSTRSET("compRenderStr", "Render #" + QString::number(DIAGNOSTICS_GLOGET("compInst")) + " | ");
|
|
|
|
DIAGNOSTICS_GLOSET(DIAGNOSTICS_THRSTRGET("stackVar"), 0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void TFxCacheManager::onRenderStatusEnd(int renderStatus)
|
|
|
|
{
|
|
|
|
if (renderStatus == TRenderer::FIRSTRUN) {
|
|
|
|
Imp::ResourceInstanceDataMap &resMap = m_imp->m_resourcesData;
|
|
|
|
|
|
|
|
Imp::ResourceInstanceDataMap::iterator it;
|
|
|
|
for (it = resMap.begin(); it != resMap.end();) {
|
|
|
|
m_imp->prepareTilesToCalculate(it->second);
|
|
|
|
|
|
|
|
//Cannot be done. The resource could still be feasible to caching,
|
|
|
|
//due to external requests.
|
|
|
|
|
|
|
|
//Erase all resource datas which have been declared and prepared only once
|
|
|
|
/*if(it->second.m_tiles.size() == 1 && it->second.m_simData->m_tiles.size() == 1)
|
|
|
|
{
|
|
|
|
it = resMap.erase(it);
|
|
|
|
continue;
|
|
|
|
}*/
|
|
|
|
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
} else if (renderStatus == TRenderer::TESTRUN) {
|
|
|
|
Imp::ResourceInstanceDataMap &resMap = m_imp->m_resourcesData;
|
|
|
|
Imp::ResourceInstanceDataMap::iterator it;
|
|
|
|
for (it = resMap.begin(); it != resMap.end();) {
|
|
|
|
//Release all resource declarations which are declared to be used only once.
|
|
|
|
if (it->second.m_tiles.size() == 1 && it->second.m_tiles[0].m_refCount == 1) {
|
|
|
|
Imp::ResourceInstanceDataMap::iterator jt = it++;
|
|
|
|
resMap.erase(jt);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
//In any case, release all simulation datas - they are no longer useful.
|
|
|
|
//An associated cache resource avoids deletion only in case some manager
|
|
|
|
//retained it.
|
|
|
|
it->second.m_rawData = 0;
|
|
|
|
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef WRITEGENERAL
|
|
|
|
DIAGNOSTICS_SET("Declarations used more than once", resMap.size());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
m_imp->m_rawData.clear();
|
|
|
|
}
|
|
|
|
#ifdef WRITEGENERAL
|
|
|
|
else {
|
|
|
|
//Print the number of not depleted declarations
|
|
|
|
Imp::ResourceInstanceDataMap &resMap = m_imp->m_resourcesData;
|
|
|
|
Imp::ResourceInstanceDataMap::iterator it;
|
|
|
|
|
|
|
|
DIAGNOSTICS_ADD(prefixErr + "Computing | Declarations survived after Test Run", resMap.size());
|
|
|
|
if (resMap.size() > 0) {
|
|
|
|
for (it = resMap.begin(); it != resMap.end(); ++it) {
|
|
|
|
DIAGNOSTICS_STR(prefixErr + "Survived Declarations | " + QString::fromStdString(it->first).left(40));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//****************************************************************************************************
|
|
|
|
// Tiles to calculate - methods
|
|
|
|
//****************************************************************************************************
|
|
|
|
|
|
|
|
void TFxCacheManager::Imp::prepareTilesToCalculate(ResourceDeclaration &data)
|
|
|
|
{
|
|
|
|
//First, build the total sum of declared tiles
|
|
|
|
TRectD sum;
|
|
|
|
int tilesCount = data.m_rawData->m_tiles.size();
|
|
|
|
|
|
|
|
for (int i = 0; i < tilesCount; ++i)
|
|
|
|
sum += data.m_rawData->m_tiles[i];
|
|
|
|
|
|
|
|
//Intersect the sum with bbox and ensure integer geometry
|
|
|
|
//sum *= data.m_rawData->m_bbox;
|
|
|
|
enlargeToI(sum);
|
|
|
|
|
|
|
|
if (!data.m_rawData->m_subtileable) {
|
|
|
|
data.m_tiles.push_back(sum);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRasterFx *fx = dynamic_cast<TRasterFx *>(data.m_rawData->m_fx.getPointer());
|
|
|
|
|
|
|
|
//Now, subdivide the sum
|
|
|
|
recursiveRectSubdivide(data.m_tiles, fx, sum, data.m_rawData->m_frame, data.m_rawData->m_rs);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
//! Calculates at max 4 smaller subrects of passed one. Returns true or false whether the subdivision was
|
|
|
|
//! successfully applied.
|
|
|
|
inline void TFxCacheManager::Imp::subdivideIntoSmallerTiles(
|
|
|
|
const TRectD &rect, std::vector<TRectD> &tileSet)
|
|
|
|
{
|
|
|
|
//Find the greater rect edge
|
|
|
|
TRectD subTile1, subTile2;
|
|
|
|
if (rect.getLx() > rect.getLy()) {
|
|
|
|
int sep = rect.x0 + tceil(0.5 * rect.getLx());
|
|
|
|
subTile1 = TRectD(rect.x0, rect.y0, sep, rect.y1);
|
|
|
|
subTile2 = TRectD(sep, rect.y0, rect.x1, rect.y1);
|
|
|
|
} else {
|
|
|
|
int sep = rect.y0 + tceil(0.5 * rect.getLy());
|
|
|
|
subTile1 = TRectD(rect.x0, rect.y0, rect.x1, sep);
|
|
|
|
subTile2 = TRectD(rect.x0, sep, rect.x1, rect.y1);
|
|
|
|
}
|
|
|
|
|
|
|
|
tileSet.push_back(subTile1);
|
|
|
|
tileSet.push_back(subTile2);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void TFxCacheManager::Imp::recursiveRectSubdivide(
|
|
|
|
std::vector<ResourceDeclaration::TileData> &results,
|
|
|
|
TRasterFx *fx,
|
|
|
|
const TRectD &rect,
|
|
|
|
double frame,
|
|
|
|
const TRenderSettings &info,
|
|
|
|
int dropTol)
|
|
|
|
{
|
|
|
|
//Here is the subdivision strategy:
|
|
|
|
// - First, since cache tiles are newly ALLOCATED, we impose the raster
|
|
|
|
// size restriction on them directly
|
|
|
|
// - Then, we check that the memory requirement for the fx (max raster size
|
|
|
|
// that it will allocate) is little.
|
|
|
|
// - As an exception to previous point, if the memory requirement stagnates
|
|
|
|
// near the same memory size, quit
|
|
|
|
//NOTE: Level images pass here, but haven't any fx. So they are currently not subdivided.
|
|
|
|
|
|
|
|
//Retrieve the memory requirement for this input.
|
|
|
|
int memReq = fx ? fx->getMemoryRequirement(rect, frame, info) : 0;
|
|
|
|
|
|
|
|
//In case memReq < 0, we assume a strong subdivision denial, just as if the usage was
|
|
|
|
//explicitly set as unsubdividable.
|
|
|
|
if (memReq < 0) {
|
|
|
|
results.push_back(rect);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((memReq > info.m_maxTileSize && memReq < dropTol) ||
|
|
|
|
TRasterFx::memorySize(rect, info.m_bpp) > info.m_maxTileSize) {
|
|
|
|
std::vector<TRectD> subTileRects;
|
|
|
|
subdivideIntoSmallerTiles(rect, subTileRects);
|
|
|
|
|
|
|
|
while (!subTileRects.empty()) {
|
|
|
|
TRectD subTileRect(subTileRects.back());
|
|
|
|
subTileRects.pop_back();
|
|
|
|
|
|
|
|
//Pass subdivision below, with updated drop-tolerance
|
|
|
|
recursiveRectSubdivide(results, fx, subTileRect, frame, info,
|
|
|
|
memReq - (memReq >> 2));
|
|
|
|
|
|
|
|
//The newly required memory must be under 3/4 of the previous.
|
|
|
|
//This is required in order to make it worth subdividing.
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
results.push_back(ResourceDeclaration::TileData(rect));
|
|
|
|
}
|
|
|
|
|
|
|
|
//****************************************************************************************************
|
|
|
|
// ResourceBuilder
|
|
|
|
//****************************************************************************************************
|
|
|
|
|
|
|
|
ResourceBuilder::ResourceBuilder(
|
|
|
|
const std::string &resourceName,
|
|
|
|
const TFxP &fx, double frame, const TRenderSettings &rs)
|
|
|
|
: m_cacheManager(TFxCacheManager::instance()), m_data(m_cacheManager->getResource(resourceName, fx, frame, rs))
|
|
|
|
{
|
|
|
|
#ifdef WRITESTACK
|
|
|
|
DIAGNOSTICS_THRSET("frame", frame);
|
|
|
|
DIAGNOSTICS_THRSTRSET("frameStr", "Frame " + QString::number(frame).rightJustified(4, ' ') + " | ");
|
|
|
|
DIAGNOSTICS_THRSTRSET("stackVar",
|
|
|
|
DIAGNOSTICS_GLOSTRGET("status") + "sv" + DIAGNOSTICS_GLOSTRGET("instVar") + "fr" + QString::number(frame));
|
|
|
|
DIAGNOSTICS_THRSTRSET("ResourceName", QString::fromStdString(resourceName).left(35));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void ResourceBuilder::declareResource(
|
2016-04-19 19:32:17 +12:00
|
|
|
const std::string &alias, const TFxP &fx,
|
2016-03-19 06:57:51 +13:00
|
|
|
const TRectD &rect, double frame, const TRenderSettings &rs,
|
|
|
|
bool subtileable)
|
|
|
|
{
|
|
|
|
TFxCacheManager::instance()->declareResource(
|
|
|
|
alias, fx, rect, frame, rs, subtileable);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
// Retrieves all interesting tiles with respect to the build procedure.
|
|
|
|
// Explicitly, this refers to tiles intersecting the required rect, that are either
|
|
|
|
// not in the resource, or supposed to be not.
|
|
|
|
// Returns true if the required tile is contained in the sum of the predicted ones
|
|
|
|
// (which should definitely happen if the predictive step is coherent with the
|
|
|
|
// computation one).
|
|
|
|
bool getTilesToBuild(
|
|
|
|
const ResourceData &data, const TRectD &rect, std::vector<ResourceDeclaration::TileData *> &rectsToCalculate)
|
|
|
|
{
|
|
|
|
assert(data.first); //The declaration must DEFINITELY be present
|
|
|
|
assert(data.second); //The resource should be present here
|
|
|
|
|
|
|
|
//Now, fill in with all prepared rects which intersect input rect and are not
|
|
|
|
//already in the resource
|
|
|
|
|
|
|
|
std::vector<ResourceDeclaration::TileData> &preparedRects =
|
|
|
|
data.first->m_tiles;
|
|
|
|
std::vector<ResourceDeclaration::TileData>::iterator jt;
|
|
|
|
|
|
|
|
TRectD sum;
|
|
|
|
for (jt = preparedRects.begin(); jt != preparedRects.end(); ++jt) {
|
|
|
|
sum += jt->m_rect;
|
|
|
|
|
|
|
|
if (!(isEmpty(rect * jt->m_rect) || jt->m_calculated))
|
|
|
|
rectsToCalculate.push_back(&(*jt));
|
|
|
|
}
|
|
|
|
|
|
|
|
return sum.contains(rect);
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void ResourceBuilder::simBuild(const TRectD &rect)
|
|
|
|
{
|
|
|
|
//Retrieve the render status
|
|
|
|
int renderStatus = m_cacheManager->m_imp->m_renderStatus;
|
|
|
|
|
|
|
|
//In the initial precomputing stage, just retrieve all the declarations without efforts.
|
|
|
|
if (renderStatus == TRenderer::FIRSTRUN) {
|
|
|
|
simCompute(rect);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Perform the test run
|
|
|
|
if (renderStatus == TRenderer::TESTRUN) {
|
|
|
|
if (!(m_data.first && m_data.second))
|
|
|
|
return;
|
|
|
|
|
|
|
|
#ifdef WRITESTACK
|
|
|
|
QString resName(DIAGNOSTICS_THRSTRGET("ResourceName"));
|
|
|
|
|
|
|
|
QString renderStr(DIAGNOSTICS_GLOSTRGET("testRenderStr"));
|
|
|
|
QString frameStr(DIAGNOSTICS_THRSTRGET("frameStr"));
|
|
|
|
DIAGNOSTICS_NUMBEREDSTRSET(prefixTest + renderStr + frameStr,
|
|
|
|
DIAGNOSTICS_THRSTRGET("stackVar"), DIAGNOSTICS_STACKGET("parentResource") + " " + resName,
|
|
|
|
::traduce(rect), 4);
|
|
|
|
DIAGNOSTICS_PUSHAUTO("parentResource",
|
|
|
|
QString::number(DIAGNOSTICS_GLOGET(DIAGNOSTICS_THRSTRGET("stackVar"))), bla);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//Retrieve the tiles to build
|
|
|
|
std::vector<ResourceDeclaration::TileData> &tiles = m_data.first->m_tiles;
|
|
|
|
|
|
|
|
//For every tile intersecting rect
|
|
|
|
std::vector<ResourceDeclaration::TileData>::iterator it;
|
|
|
|
for (it = tiles.begin(); it != tiles.end(); ++it) {
|
|
|
|
ResourceDeclaration::TileData &tileData = *it;
|
|
|
|
|
|
|
|
if (!isEmpty(tileData.m_rect * rect)) {
|
|
|
|
//If the tile ref count == 0, assume that this tile has not yet been simComputed.
|
|
|
|
//Do so, then; further, add 1 to the number of predicted actively accessed tiles.
|
|
|
|
if (tileData.m_refCount == 0) {
|
|
|
|
#ifdef WRITESUBRECTS
|
|
|
|
DIAGNOSTICS_NUMBEREDSTRSET(prefixTest + renderStr + frameStr,
|
|
|
|
DIAGNOSTICS_THRSTRGET("stackVar"), DIAGNOSTICS_STACKGET("parentResource") + " " + resName,
|
|
|
|
::traduce(tileData.m_rect), 4);
|
|
|
|
DIAGNOSTICS_PUSHAUTO("parentResource",
|
|
|
|
QString::number(DIAGNOSTICS_GLOGET(DIAGNOSTICS_THRSTRGET("stackVar"))), bla2);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
simCompute(tileData.m_rect);
|
|
|
|
++m_data.first->m_tilesCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Add a reference to this tile
|
|
|
|
++tileData.m_refCount;
|
|
|
|
|
|
|
|
if (m_data.second) {
|
|
|
|
QMutexLocker locker(m_data.second->getMutex());
|
|
|
|
|
|
|
|
TRect tileRectI(
|
|
|
|
tileData.m_rect.x0, tileData.m_rect.y0,
|
|
|
|
tileData.m_rect.x1 - 1, tileData.m_rect.y1 - 1);
|
|
|
|
|
|
|
|
m_data.second->addRef2(tileRectI);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//In this case, the simulation is used to declare that this rect will NOT be calculated.
|
|
|
|
//So, this is the behaviour: 1 refCount is depleted by all cells intersecting the rect
|
|
|
|
// - if the refCount is still >0, then another request will occur that will make the
|
|
|
|
//tile calculated. If it is == 0, and the tile has not yet been calculated, then the tile
|
|
|
|
//will supposedly be NO MORE calculated, so the simCompute has to be launched on that tile, too.
|
|
|
|
|
|
|
|
if (renderStatus == TRenderer::COMPUTING) {
|
|
|
|
if (!(m_data.first && m_data.second))
|
|
|
|
return;
|
|
|
|
|
|
|
|
QMutexLocker locker(m_data.second->getMutex());
|
|
|
|
|
|
|
|
//Retrieve the tiles to build
|
|
|
|
std::vector<ResourceDeclaration::TileData> &tiles = m_data.first->m_tiles;
|
|
|
|
|
|
|
|
//Please note that the request should definitely be fitting the predicted results,
|
|
|
|
//since the original rect which generated these simCompute calls must have been fitting.
|
|
|
|
|
|
|
|
//For every tile to build
|
|
|
|
std::vector<ResourceDeclaration::TileData>::iterator it;
|
|
|
|
for (it = tiles.begin(); it != tiles.end(); ++it) {
|
|
|
|
ResourceDeclaration::TileData &tileData = *it;
|
|
|
|
|
|
|
|
if (!isEmpty(tileData.m_rect * rect)) {
|
|
|
|
if (tileData.m_refCount <= 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (--tileData.m_refCount == 0 && !tileData.m_calculated) {
|
|
|
|
--m_data.first->m_tilesCount;
|
|
|
|
simCompute(tileData.m_rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_data.second) {
|
|
|
|
TRect tileRectI(
|
|
|
|
tileData.m_rect.x0, tileData.m_rect.y0,
|
|
|
|
tileData.m_rect.x1 - 1, tileData.m_rect.y1 - 1);
|
|
|
|
|
|
|
|
m_data.second->release2(tileRectI);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void ResourceBuilder::build(const TRectD &tileRect)
|
|
|
|
{
|
|
|
|
#ifdef WRITESTACK
|
|
|
|
QString resName(DIAGNOSTICS_THRSTRGET("ResourceName"));
|
|
|
|
|
|
|
|
QString renderStr(DIAGNOSTICS_GLOSTRGET("compRenderStr"));
|
|
|
|
QString frameStr(DIAGNOSTICS_THRSTRGET("frameStr"));
|
|
|
|
DIAGNOSTICS_NUMBEREDSTRSET(prefixComp + renderStr + frameStr,
|
|
|
|
DIAGNOSTICS_THRSTRGET("stackVar"), DIAGNOSTICS_STACKGET("parentResource") + " " + resName,
|
|
|
|
::traduce(tileRect), 4);
|
|
|
|
DIAGNOSTICS_PUSHAUTO("parentResource",
|
|
|
|
QString::number(DIAGNOSTICS_GLOGET(DIAGNOSTICS_THRSTRGET("stackVar"))), bla);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//If there is no resource, just compute the tile directly.
|
|
|
|
if (!m_data.second) {
|
|
|
|
#ifdef WRITEGENERAL
|
|
|
|
if (m_data.first)
|
|
|
|
if (m_data.first->m_tilesCount > 0)
|
|
|
|
DIAGNOSTICS_ADD(prefixErr + "Computing | No-resource build, active decl, tilesCount > 0", 1);
|
|
|
|
else
|
|
|
|
DIAGNOSTICS_ADD(prefixErr + "Computing | No-resource build, active decl, tilesCount <= 0", 1);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//assert(!m_data.first); //Should have been erased before the COMPUTING run.
|
|
|
|
compute(tileRect);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Since this function must be thread-safe, use the appropriate synchronization tool.
|
|
|
|
QMutexLocker locker(m_data.second->getMutex());
|
|
|
|
|
|
|
|
//Without declaration, you can just deal with the required tile.
|
|
|
|
if (!(m_data.first && m_data.first->m_tilesCount > 0)) {
|
|
|
|
#ifdef WRITEGENERAL
|
|
|
|
if (!m_data.first)
|
|
|
|
DIAGNOSTICS_ADD("#error.txt | Resources without declaration", 1);
|
|
|
|
else
|
|
|
|
DIAGNOSTICS_ADD("#error.txt | Resources with declaration, tilesCount <=0", 1);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (download(m_data.second))
|
|
|
|
return;
|
|
|
|
|
|
|
|
compute(tileRect);
|
|
|
|
|
|
|
|
//Since there is an associated resource, the calculated content is supposedly
|
|
|
|
//an interesting one. Upload it.
|
|
|
|
upload(m_data.second);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Now, both the declaration and the resource exist.
|
|
|
|
|
|
|
|
//Retrieve the predicted tile that must be built in place of tile.
|
|
|
|
TDimension dim(tileRect.getLx(), tileRect.getLy());
|
|
|
|
|
|
|
|
std::vector<ResourceDeclaration::TileData *> tiles;
|
|
|
|
bool fittingPrediction = getTilesToBuild(m_data, tileRect, tiles);
|
|
|
|
|
|
|
|
if (!fittingPrediction) {
|
|
|
|
#ifdef WRITEGENERAL
|
|
|
|
DIAGNOSTICS_ADD(prefixErr + "Computing | Not fitting tiles", 1);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//If the required tile is not fitting the prediction, we assume it is a
|
|
|
|
//full un-predicted one - so no reference count will be updated (this would comply
|
|
|
|
//with the simCompute() method, in case we assume that mis-predicted
|
|
|
|
//computes are always prudently built IN EXCESS).
|
|
|
|
|
|
|
|
//For now, just calculate it and stop.
|
|
|
|
locker.unlock();
|
|
|
|
|
|
|
|
compute(tileRect);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//If necessary, calculate something
|
|
|
|
if (tiles.size() > 0) {
|
|
|
|
//For every tile to build
|
|
|
|
std::vector<ResourceDeclaration::TileData *>::iterator it;
|
|
|
|
for (it = tiles.begin(); it != tiles.end(); ++it) {
|
|
|
|
ResourceDeclaration::TileData &tileData = **it;
|
|
|
|
|
|
|
|
//If the tile can be downloaded from the resource, it's because it has actually
|
|
|
|
//been calculated by another render process - either a concurrent one, or any
|
|
|
|
//which has written this resource part on disk storage.
|
|
|
|
|
|
|
|
//Since reference counts built in the simCompute assume that tiles downloaded
|
|
|
|
//from the resource have been calculated in THIS very render process,
|
|
|
|
//therefore having tileData.m_calculated == true, in this case
|
|
|
|
//heir refCounts must be updated since no computing will happen on them
|
|
|
|
//due to the predicted node builds of this resource.
|
|
|
|
TRect tileRectI(
|
|
|
|
tileData.m_rect.x0, tileData.m_rect.y0,
|
|
|
|
tileData.m_rect.x1 - 1, tileData.m_rect.y1 - 1);
|
|
|
|
|
|
|
|
if (m_data.second->canDownloadAll(tileRectI)) {
|
|
|
|
if (!tileData.m_calculated && tileData.m_refCount > 0) {
|
|
|
|
/*#ifdef WRITESTACK
|
|
|
|
QString renderStr(DIAGNOSTICS_GLOSTRGET("compRenderStr"));
|
|
|
|
QString frameStr(DIAGNOSTICS_THRSTRGET("frameStr"));
|
|
|
|
DIAGNOSTICS_NUMBEREDSTRSET(prefixComp + renderStr + frameStr,
|
|
|
|
DIAGNOSTICS_THRSTRGET("stackVar"), "$ > " + resName,
|
|
|
|
::traduce(tileData.m_rect), 4);
|
|
|
|
#endif*/
|
|
|
|
|
|
|
|
//Deplete children refsCount - rely on the simulated procedure
|
|
|
|
simCompute(tileData.m_rect);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
#ifdef WRITESUBRECTS
|
|
|
|
DIAGNOSTICS_NUMBEREDSTRSET(prefixComp + renderStr + frameStr,
|
|
|
|
DIAGNOSTICS_THRSTRGET("stackVar"), DIAGNOSTICS_STACKGET("parentResource") + " " + resName,
|
|
|
|
::traduce(tileData.m_rect), 4);
|
|
|
|
DIAGNOSTICS_PUSHAUTO("parentResource",
|
|
|
|
QString::number(DIAGNOSTICS_GLOGET(DIAGNOSTICS_THRSTRGET("stackVar"))), bla2);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//Compute the tile to be calculated
|
|
|
|
compute(tileData.m_rect);
|
|
|
|
if (tileData.m_refCount > 0)
|
|
|
|
tileData.m_calculated = true;
|
|
|
|
|
|
|
|
//Upload the tile into the resource - do so even if the tile
|
|
|
|
//was unpredicted. In this case, we rely on the resource refCount to
|
|
|
|
//provide the deallocations... Should be so?
|
|
|
|
upload(m_data.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Finally, download the built resource in the required tile
|
|
|
|
bool ret = download(m_data.second);
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
#ifdef WRITESTACK
|
|
|
|
if (!ret)
|
|
|
|
DIAGNOSTICS_STRSET(prefixErr + "Download falliti | " +
|
|
|
|
DIAGNOSTICS_GLOSTRGET("compRenderStr") + DIAGNOSTICS_THRSTRGET("frameStr") +
|
|
|
|
QString::number(DIAGNOSTICS_GLOGET(DIAGNOSTICS_THRSTRGET("stackVar"))),
|
|
|
|
"CROP #" + QString::number(DIAGNOSTICS_GLOGET("crStack")));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//Deplete a usage for all tiles intersecting the downloaded one. Fully depleted
|
|
|
|
//tiles become unpredicted from now on.
|
|
|
|
std::vector<ResourceDeclaration::TileData> &resTiles = m_data.first->m_tiles;
|
|
|
|
std::vector<ResourceDeclaration::TileData>::iterator it;
|
|
|
|
for (it = resTiles.begin(); it != resTiles.end(); ++it) {
|
|
|
|
ResourceDeclaration::TileData &tileData = *it;
|
|
|
|
|
|
|
|
if (!isEmpty(tileData.m_rect * tileRect)) {
|
|
|
|
if (tileData.m_refCount <= 0) {
|
|
|
|
#ifdef WRITEGENERAL
|
|
|
|
DIAGNOSTICS_ADD(prefixErr + "Computing | Over-used subtiles", 1);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (--tileData.m_refCount == 0) {
|
|
|
|
tileData.m_calculated = false;
|
|
|
|
--m_data.first->m_tilesCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRect tileRectI(
|
|
|
|
tileData.m_rect.x0, tileData.m_rect.y0,
|
|
|
|
tileData.m_rect.x1 - 1, tileData.m_rect.y1 - 1);
|
|
|
|
|
|
|
|
m_data.second->release2(tileRectI);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//If the declaration has been completely used up, destroy it.
|
|
|
|
//NOTE: Keeping the declarations is useful for diagnostic purposes. The following code
|
|
|
|
//could be reactivated - but declarations tend to be lightweight now...
|
|
|
|
//NOTE: If re-enabled, competing mutexes must be set where the resourcesData map is used...
|
|
|
|
/*if(m_data.first->m_tilesCount <= 0)
|
|
|
|
{
|
|
|
|
QMutexLocker locker(&m_cacheManager->m_imp->m_mutex);
|
|
|
|
m_cacheManager->m_imp->m_resourcesData.erase(m_data.second->getName());
|
|
|
|
}*/
|
|
|
|
}
|