Blob Blame Raw


#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 &region, 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(const std::string &alias, const TFxP &fx,
                                      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(const std::string &alias,
                                          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(const std::string &alias, const TFxP &fx,
                                      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());
}*/
}