1064 lines
30 KiB
C++
1064 lines
30 KiB
C++
|
|
||
|
|
||
|
#include "tfxattributes.h"
|
||
|
|
||
|
#include "trenderer.h"
|
||
|
#include "tcacheresource.h"
|
||
|
#include "tcacheresourcepool.h"
|
||
|
|
||
|
#include "tpassivecachemanager.h"
|
||
|
|
||
|
//#define USE_SQLITE_HDPOOL
|
||
|
|
||
|
/* PRACTICAL EXPLANATION:
|
||
|
|
||
|
The TPassiveCacheManager's purpose is that of storing render results from a specified set of fx nodes.
|
||
|
|
||
|
When an fx is passed to the manager to be cached, a structure of type FxData is generated for the fx.
|
||
|
It is stored inside the manager and ASSOCIATED to the fx through an INDEX key - this one being stored
|
||
|
inside the fx data.
|
||
|
|
||
|
In particular, the FxData instances contain a PERSISTANT identifier (passiveCacheId) that is saved inside
|
||
|
the scene data, a flag specifying the cache status of the fx node (like, if it's enabled or not), and
|
||
|
a description of the schematic dag below the associated fx.
|
||
|
|
||
|
Now, when a resource request is made to the TPassiveCacheManager, it checks the FxData about the generating
|
||
|
fx - if it contains the flag specifying that it must be cached, a resource is allocated (if not already
|
||
|
present), a reference to it is stored, and finally the resource is returned.
|
||
|
|
||
|
References to interesting resources are stored in a TABLE container, indexed by "rendering context" and
|
||
|
fx. A rendering context is hereby intended as a SEQUENCE of render processes where the next render of the
|
||
|
sequence is assumed to REPLACE the previous one.
|
||
|
We therefore have one context for the swatch viewer, one for File>Preview, one for the Preview Fx of each
|
||
|
fx node, and one context for EACH FRAME of the sceneviewer preview (one frame is not supposed to replace
|
||
|
another in that case).
|
||
|
|
||
|
The table is in practice a map of maps (a kind of 'comb' structure) - where the primary map is associated
|
||
|
by render context and the secondary ones by fx. This mean that we are able to address and iterate contexts
|
||
|
easier than fxs. Values of the table are set of resources.
|
||
|
|
||
|
RESOURCES MAINTENANCE POLICY:
|
||
|
|
||
|
As resources use a concrete amount of memory for storage, they should be kept in the resources table only
|
||
|
for the time they are supposedly useful to the user. There are 2 ways of dealing with this issue.
|
||
|
|
||
|
1) First, we can track scene changes and eliminate those resources that will no longer be accessed due to the
|
||
|
applied change. This happens, for example, when a level is being drawn, fxs are inserted, moved or removed.
|
||
|
|
||
|
Level changes are delivered immediately to the ::invalidateLevel(..) method, which removes all resources
|
||
|
whose name contains the level's name. Unfortunately, this cannot be done at frame precision (ie you cannot
|
||
|
invalidate a single frame of the level, but ALL the level).
|
||
|
|
||
|
Xsheet (schematic) changes are tracked through the schematic node description stored inside the FxData structure.
|
||
|
Once one such change happens, all resources update their description - if that changes, the associated resources
|
||
|
are released.
|
||
|
|
||
|
The same schematic description is used to track and release resources affected by changes on a downstream fx's
|
||
|
parameters.
|
||
|
|
||
|
There are also scene changes that are inherently hard to track or intercept. For example, pegbar affines changes,
|
||
|
fx nodes expressions referencing data not directly connected to the fx, and possibly others. These are currently
|
||
|
NOT handled by direct tracking.
|
||
|
|
||
|
2) As there may be resources which escape the resource control by point (1), we need some extra control
|
||
|
policies. Here they are:
|
||
|
|
||
|
We assume that the pool of resources accessed by render processes of a same render context should be CONSTANT
|
||
|
IF NO SCENE CHANGE HAPPENS. In other words, we are guessing that only those resources used in the last
|
||
|
rendering will be used in the next. Resources accessed in a render but NOT in the next (of the same context)
|
||
|
are released.
|
||
|
|
||
|
However, it could be that the sequence of render processes from a context is halted from a certain moment on.
|
||
|
In that case, it is a waste to keep the resources accessed by its last render if no new render will ever take
|
||
|
place. We then assume further that a rendering context can be ENABLED or DISABLED - when a render context is
|
||
|
enabled, it will most likely have a next render - and therefore can keep its resources in memory.
|
||
|
Once a context is DISABLED, it moves the resources to a TEMPORARY context.
|
||
|
|
||
|
Resources in the temporary context are those 'on their way for release'. They will be KEPT only if the
|
||
|
next rendering - INDEPENDENTLY FROM THE RENDER CONTEXT - requires them (in this case, they will be adopted
|
||
|
by the new context). This is necessary since context disables typically happen when a preview window closes.
|
||
|
It is not unfrequent that users close the preview window, modify the scene, and then restore the preview.
|
||
|
|
||
|
CONSIDERATIONS:
|
||
|
|
||
|
* The File>Render generates NO CONTEXT - and therefore does NOT PASSIVELY CACHE RESOURCES, since it cannot be
|
||
|
stated whether it is in the 'enabled' or 'disabled' state. It could be considered coherent with what tcomposer
|
||
|
does, by the way...
|
||
|
|
||
|
* Our resources maintenance policy ensures that the memory usage should be stable over time - that is, no
|
||
|
useless resource is kept in memory.
|
||
|
Of course, it is possibly more restrictive than what the user may desire. For example, closing 2 preview
|
||
|
windows marks their resources for deletion, but only one of the two restored previews will keep its
|
||
|
resources intact...
|
||
|
|
||
|
*/
|
||
|
|
||
|
//*****************************************************************************************
|
||
|
// Debug stuff
|
||
|
//*****************************************************************************************
|
||
|
|
||
|
/*
|
||
|
#define DIAGNOSTICS
|
||
|
#include "diagnostics.h"
|
||
|
|
||
|
namespace
|
||
|
{
|
||
|
QString prefix("#Passive.txt | STACK | ");
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------
|
||
|
|
||
|
inline std::string traduce(const TRectD& rect)
|
||
|
{
|
||
|
return "[" + toString(rect.x0) + " " + toString(rect.y0) + " " +
|
||
|
toString(rect.x1) + " " + toString(rect.y1) + "]";
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------
|
||
|
|
||
|
inline std::string traduce(const TAffine& aff)
|
||
|
{
|
||
|
return "(" +
|
||
|
toString(aff.a11) + " " + toString(aff.a12) + " " +
|
||
|
toString(aff.a13) + " " + toString(aff.a21) + " " +
|
||
|
toString(aff.a22) + " " + toString(aff.a23) + ")";
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
//*****************************************************************************************
|
||
|
// Preliminaries
|
||
|
//*****************************************************************************************
|
||
|
|
||
|
// Local stuff - inlines
|
||
|
namespace
|
||
|
{
|
||
|
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()); }
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
|
||
|
TFx *TPassiveCacheManager::getNotAllowingAncestor(TFx *fx)
|
||
|
{
|
||
|
//Trace all output ports
|
||
|
int outputPortsCount = fx->getOutputConnectionCount();
|
||
|
/*if(!outputPortsCount) //We have no access to TApp here!!
|
||
|
{
|
||
|
//It could be a terminal fx. In that case, pass to the xsheet fx
|
||
|
FxDag* dag = TApp::instance()->getCurrentXsheet()->getXsheet()->getFxDag();
|
||
|
if(dag->getTerminalFxs()->containsFx(fx))
|
||
|
return getNotAllowingAncestor(dag->getXsheetFx());
|
||
|
}*/
|
||
|
|
||
|
//Now, for common ports
|
||
|
for (int i = 0; i < outputPortsCount; ++i) {
|
||
|
//Find the output Fx and the port connected to our fx
|
||
|
TFxPort *port = fx->getOutputConnection(i);
|
||
|
TRasterFx *outFx = static_cast<TRasterFx *>(port->getOwnerFx());
|
||
|
|
||
|
int portIdx, portsCount = outFx->getInputPortCount();
|
||
|
for (portIdx = 0; portIdx < portsCount; ++portIdx)
|
||
|
if (outFx->getInputPort(portIdx) == port)
|
||
|
break;
|
||
|
assert(portIdx < portsCount);
|
||
|
|
||
|
if (!outFx->allowUserCacheOnPort(portIdx))
|
||
|
return outFx;
|
||
|
|
||
|
TFx *naAncestor = getNotAllowingAncestor(outFx);
|
||
|
if (naAncestor)
|
||
|
return naAncestor;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************************
|
||
|
// Resources Container Definition
|
||
|
//*****************************************************************************************
|
||
|
|
||
|
template <typename RowKey, typename ColKey, typename Val>
|
||
|
class Table
|
||
|
{
|
||
|
public:
|
||
|
typedef typename std::map<ColKey, Val> Row;
|
||
|
|
||
|
private:
|
||
|
std::map<RowKey, Row> m_table;
|
||
|
|
||
|
friend class Iterator;
|
||
|
friend class ColIterator;
|
||
|
|
||
|
public:
|
||
|
typedef typename std::map<RowKey, Row>::iterator RowsIterator;
|
||
|
|
||
|
class Iterator
|
||
|
{
|
||
|
protected:
|
||
|
Table *m_table;
|
||
|
RowsIterator m_rowIt;
|
||
|
typename Row::iterator m_it;
|
||
|
|
||
|
friend class Table;
|
||
|
Iterator(Table *table) : m_table(table) {}
|
||
|
|
||
|
virtual void makeConsistent()
|
||
|
{
|
||
|
if (m_it == m_rowIt->second.end()) {
|
||
|
if (++m_rowIt == m_table->m_table.end())
|
||
|
return;
|
||
|
m_it = m_rowIt->second.begin();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
const RowKey &row() { return m_rowIt->first; }
|
||
|
const ColKey &col() { return m_it->first; }
|
||
|
|
||
|
virtual void operator++()
|
||
|
{
|
||
|
++m_it;
|
||
|
makeConsistent();
|
||
|
}
|
||
|
|
||
|
virtual operator bool() { return m_rowIt != m_table->m_table.end(); }
|
||
|
|
||
|
Val &operator*() { return m_it->second; }
|
||
|
Val *operator->() { return &m_it->second; }
|
||
|
|
||
|
bool operator==(const Iterator &it)
|
||
|
{
|
||
|
return m_it == it.m_it;
|
||
|
}
|
||
|
|
||
|
bool operator!=(const Iterator &it)
|
||
|
{
|
||
|
return !operator==(it);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class ColIterator : public Iterator
|
||
|
{
|
||
|
ColKey m_colKey;
|
||
|
|
||
|
friend class Table;
|
||
|
ColIterator(Table *table, const ColKey &c) : Iterator(table), m_colKey(c) {}
|
||
|
|
||
|
void makeConsistent()
|
||
|
{
|
||
|
Iterator::m_rowIt = Iterator::m_rowIt;
|
||
|
while (Iterator::m_rowIt != Iterator::m_table->m_table.end()) {
|
||
|
Iterator::m_it = Iterator::m_rowIt->second.find(m_colKey);
|
||
|
if (Iterator::m_it != Iterator::m_rowIt->second.end())
|
||
|
break;
|
||
|
++Iterator::m_rowIt;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
void operator++()
|
||
|
{
|
||
|
++Iterator::m_rowIt;
|
||
|
makeConsistent();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class RowIterator : public Iterator
|
||
|
{
|
||
|
friend class Table;
|
||
|
RowIterator(Table *table) : Iterator(table) {}
|
||
|
|
||
|
void makeConsistent() {}
|
||
|
|
||
|
public:
|
||
|
RowIterator(const RowsIterator rowIt) : Iterator(0)
|
||
|
{
|
||
|
Iterator::m_rowIt = rowIt;
|
||
|
Iterator::m_it = rowIt->second.begin();
|
||
|
}
|
||
|
|
||
|
void operator++() { ++Iterator::m_it; }
|
||
|
operator bool() { return Iterator::m_it != Iterator::m_rowIt->second.end(); }
|
||
|
};
|
||
|
|
||
|
public:
|
||
|
Table() {}
|
||
|
~Table() {}
|
||
|
|
||
|
std::map<RowKey, Row> &rows() { return m_table; }
|
||
|
|
||
|
Iterator begin()
|
||
|
{
|
||
|
Iterator result(this);
|
||
|
result.m_rowIt = m_table.begin();
|
||
|
if (result.m_rowIt != m_table.end())
|
||
|
result.m_it = result.m_rowIt->second.begin();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
RowIterator rowBegin(const RowKey &row)
|
||
|
{
|
||
|
RowIterator result(this);
|
||
|
result.m_rowIt = m_table.find(row);
|
||
|
if (result.m_rowIt != m_table.end())
|
||
|
result.m_it = result.m_rowIt->second.begin();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
ColIterator colBegin(const ColKey &col)
|
||
|
{
|
||
|
ColIterator result(this, col);
|
||
|
result.m_rowIt = m_table.begin();
|
||
|
result.makeConsistent();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
Val &value(const RowKey &r, const ColKey &c)
|
||
|
{
|
||
|
return m_table[r][c];
|
||
|
}
|
||
|
|
||
|
Iterator insert(const RowKey &r, const ColKey &c, const Val &val)
|
||
|
{
|
||
|
Iterator result(this);
|
||
|
result.m_rowIt = m_table.insert(std::make_pair(r, Row())).first;
|
||
|
result.m_it = result.m_rowIt->second.insert(std::make_pair(c, val)).first;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
Iterator find(const RowKey &r, const ColKey &c)
|
||
|
{
|
||
|
Iterator result(this);
|
||
|
result.m_rowIt = m_table.find(r);
|
||
|
if (result.m_rowIt == m_table.end())
|
||
|
return;
|
||
|
result.m_it = result.m_rowIt->second.find(c);
|
||
|
if (result.m_it == result.m_rowIt->second.end())
|
||
|
result.m_rowIt = m_table.end();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
Iterator erase(const RowKey &r, const ColKey &c)
|
||
|
{
|
||
|
Iterator it(find(r, c));
|
||
|
return erase(it);
|
||
|
}
|
||
|
|
||
|
Iterator erase(const Iterator &it)
|
||
|
{
|
||
|
Iterator result(it);
|
||
|
Row &row = it.m_rowIt->second;
|
||
|
++result.m_it;
|
||
|
row.erase(it.m_it);
|
||
|
if (result.m_it == row.end() && row.empty()) {
|
||
|
result.makeConsistent();
|
||
|
m_table.erase(it.m_rowIt);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
result.makeConsistent();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void erase(const ColKey &c)
|
||
|
{
|
||
|
ColIterator it(colBegin(c));
|
||
|
while (it) {
|
||
|
RowsIterator rowIt = it.m_rowIt;
|
||
|
rowIt->second.erase(it.m_it);
|
||
|
++it;
|
||
|
if (rowIt->second.empty())
|
||
|
m_table.erase(rowIt);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void erase(const RowKey &r)
|
||
|
{
|
||
|
m_table.erase(r);
|
||
|
}
|
||
|
|
||
|
void clear() { m_table.clear(); }
|
||
|
};
|
||
|
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
struct LockedResourceP {
|
||
|
TCacheResourceP m_resource;
|
||
|
|
||
|
LockedResourceP(const TCacheResourceP &resource) : m_resource(resource)
|
||
|
{
|
||
|
m_resource->addLock();
|
||
|
}
|
||
|
|
||
|
LockedResourceP(const LockedResourceP &resource) : m_resource(resource.m_resource)
|
||
|
{
|
||
|
m_resource->addLock();
|
||
|
}
|
||
|
|
||
|
~LockedResourceP()
|
||
|
{
|
||
|
m_resource->releaseLock();
|
||
|
}
|
||
|
|
||
|
LockedResourceP &operator=(const LockedResourceP &src)
|
||
|
{
|
||
|
src.m_resource->addLock();
|
||
|
if (m_resource)
|
||
|
m_resource->releaseLock();
|
||
|
m_resource = src.m_resource;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
operator bool() const { return m_resource; }
|
||
|
|
||
|
bool operator<(const LockedResourceP &resource) const { return m_resource < resource.m_resource; }
|
||
|
|
||
|
TCacheResource *operator->() const { return m_resource.getPointer(); }
|
||
|
TCacheResource &operator*() const { return *m_resource.getPointer(); }
|
||
|
};
|
||
|
|
||
|
typedef Table<std::string, int, std::set<LockedResourceP>> ResourcesTable;
|
||
|
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
class TPassiveCacheManager::ResourcesContainer
|
||
|
{
|
||
|
ResourcesTable m_resources;
|
||
|
|
||
|
public:
|
||
|
ResourcesContainer() {}
|
||
|
~ResourcesContainer() {}
|
||
|
|
||
|
ResourcesTable &getTable() { return m_resources; }
|
||
|
};
|
||
|
|
||
|
//*****************************************************************************************
|
||
|
// FxData implementation
|
||
|
//*****************************************************************************************
|
||
|
|
||
|
TPassiveCacheManager::FxData::FxData()
|
||
|
: m_storageFlag(0), m_passiveCacheId(0)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
TPassiveCacheManager::FxData::~FxData()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************************
|
||
|
// Manager generator
|
||
|
//*****************************************************************************************
|
||
|
|
||
|
//=======================================
|
||
|
// TPassiveCacheManagerGenerator
|
||
|
//---------------------------------------
|
||
|
|
||
|
class TPassiveCacheManagerGenerator : public TRenderResourceManagerGenerator
|
||
|
{
|
||
|
TRenderResourceManager *operator()(void)
|
||
|
{
|
||
|
//return new TPassiveCacheManager;
|
||
|
return TPassiveCacheManager::instance();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
MANAGER_FILESCOPE_DECLARATION_DEP(
|
||
|
TPassiveCacheManager,
|
||
|
TPassiveCacheManagerGenerator,
|
||
|
TFxCacheManager::deps())
|
||
|
|
||
|
//*****************************************************************************************
|
||
|
// Implementation
|
||
|
//*****************************************************************************************
|
||
|
|
||
|
TPassiveCacheManager::TPassiveCacheManager()
|
||
|
#ifdef USE_SQLITE_HDPOOL
|
||
|
: m_currStorageFlag(ON_DISK)
|
||
|
#else
|
||
|
: m_currStorageFlag(IN_MEMORY)
|
||
|
#endif
|
||
|
,
|
||
|
m_enabled(true), m_descriptorCallback(0), m_mutex(QMutex::Recursive), m_resources(new ResourcesContainer)
|
||
|
{
|
||
|
reset();
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
TPassiveCacheManager::~TPassiveCacheManager()
|
||
|
{
|
||
|
delete m_resources;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
TPassiveCacheManager *TPassiveCacheManager::instance()
|
||
|
{
|
||
|
static TPassiveCacheManager theInstance;
|
||
|
return &theInstance;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::setContextName(unsigned long renderId, const std::string &name)
|
||
|
{
|
||
|
QMutexLocker locker(&m_mutex);
|
||
|
|
||
|
//Retrieve the context data if already present
|
||
|
std::map<std::string, UCHAR>::iterator it = m_contextNames.find(name);
|
||
|
if (it == m_contextNames.end())
|
||
|
it = m_contextNames.insert(std::make_pair(name, 0)).first;
|
||
|
|
||
|
it->second = !it->second;
|
||
|
m_contextNamesByRenderId.insert(std::make_pair(renderId, name + "%" + ::toString(it->second)));
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
std::string TPassiveCacheManager::getContextName()
|
||
|
{
|
||
|
QMutexLocker locker(&m_mutex);
|
||
|
|
||
|
//First, search the context name
|
||
|
std::map<unsigned long, std::string>::iterator it =
|
||
|
m_contextNamesByRenderId.find(TRenderer::renderId());
|
||
|
|
||
|
if (it == m_contextNamesByRenderId.end())
|
||
|
return "";
|
||
|
|
||
|
return it->second;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::setEnabled(bool enabled)
|
||
|
{
|
||
|
m_enabled = enabled;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
bool TPassiveCacheManager::isEnabled() const
|
||
|
{
|
||
|
return m_enabled;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::setStorageMode(StorageFlag mode)
|
||
|
{
|
||
|
m_currStorageFlag = mode;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
TPassiveCacheManager::StorageFlag TPassiveCacheManager::getStorageMode() const
|
||
|
{
|
||
|
return m_currStorageFlag;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::setTreeDescriptor(TreeDescriptor callback)
|
||
|
{
|
||
|
m_descriptorCallback = callback;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------
|
||
|
|
||
|
int TPassiveCacheManager::getNewPassiveCacheId()
|
||
|
{
|
||
|
return ++m_currentPassiveCacheId;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------
|
||
|
|
||
|
int TPassiveCacheManager::updatePassiveCacheId(int id)
|
||
|
{
|
||
|
if (m_updatingPassiveCacheIds)
|
||
|
m_currentPassiveCacheId = tmax(m_currentPassiveCacheId, id);
|
||
|
else
|
||
|
id = getNewPassiveCacheId();
|
||
|
|
||
|
return id;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::onSceneLoaded()
|
||
|
{
|
||
|
m_updatingPassiveCacheIds = false;
|
||
|
|
||
|
//Initialize the fxs tree description. This was not possible before, as the
|
||
|
//scene was yet incomplete (in loading state).
|
||
|
#ifndef USE_SQLITE_HDPOOL
|
||
|
unsigned int count = m_fxDataVector.size();
|
||
|
for (unsigned int i = 0; i < count; ++i) {
|
||
|
FxData &data = m_fxDataVector[i];
|
||
|
(*m_descriptorCallback)(data.m_treeDescription, data.m_fx);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::touchFxData(int &idx)
|
||
|
{
|
||
|
if (idx >= 0)
|
||
|
return;
|
||
|
|
||
|
QMutexLocker locker(&m_mutex);
|
||
|
|
||
|
m_fxDataVector.push_back(FxData());
|
||
|
idx = m_fxDataVector.size() - 1;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------
|
||
|
|
||
|
int TPassiveCacheManager::declareCached(TFx *fx, int passiveCacheId)
|
||
|
{
|
||
|
int &idx = fx->getAttributes()->passiveCacheDataIdx();
|
||
|
touchFxData(idx);
|
||
|
|
||
|
FxData &data = m_fxDataVector[idx];
|
||
|
data.m_fx = fx;
|
||
|
data.m_storageFlag = m_currStorageFlag;
|
||
|
data.m_passiveCacheId = updatePassiveCacheId(passiveCacheId);
|
||
|
|
||
|
return idx;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::reset()
|
||
|
{
|
||
|
m_updatingPassiveCacheIds = true;
|
||
|
m_currentPassiveCacheId = 0;
|
||
|
m_fxDataVector.clear();
|
||
|
m_resources->getTable().clear();
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
bool TPassiveCacheManager::cacheEnabled(TFx *fx)
|
||
|
{
|
||
|
int idx = fx->getAttributes()->passiveCacheDataIdx();
|
||
|
if (idx < 0)
|
||
|
return false;
|
||
|
|
||
|
assert(idx < (int)m_fxDataVector.size());
|
||
|
|
||
|
QMutexLocker locker(&m_mutex);
|
||
|
|
||
|
return m_fxDataVector[idx].m_storageFlag > 0;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
int TPassiveCacheManager::getPassiveCacheId(TFx *fx)
|
||
|
{
|
||
|
int idx = fx->getAttributes()->passiveCacheDataIdx();
|
||
|
if (idx < 0)
|
||
|
return 0;
|
||
|
|
||
|
//This needs not be mutex locked
|
||
|
|
||
|
assert(idx < (int)m_fxDataVector.size());
|
||
|
return m_fxDataVector[idx].m_passiveCacheId;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
TPassiveCacheManager::StorageFlag TPassiveCacheManager::getStorageMode(TFx *fx)
|
||
|
{
|
||
|
int idx = fx->getAttributes()->passiveCacheDataIdx();
|
||
|
if (idx < 0)
|
||
|
return NONE;
|
||
|
|
||
|
QMutexLocker locker(&m_mutex);
|
||
|
|
||
|
return (StorageFlag)m_fxDataVector[idx].m_storageFlag;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::enableCache(TFx *fx)
|
||
|
{
|
||
|
int &idx = fx->getAttributes()->passiveCacheDataIdx();
|
||
|
touchFxData(idx);
|
||
|
|
||
|
FxData &data = m_fxDataVector[idx];
|
||
|
|
||
|
QMutexLocker locker(&m_mutex);
|
||
|
|
||
|
#ifdef DIAGNOSTICS
|
||
|
DIAGNOSTICS_STR("#activity.txt | " + QTime::currentTime().toString() + " " +
|
||
|
QString("Enable Cache"));
|
||
|
#endif
|
||
|
|
||
|
StorageFlag flag = getStorageMode();
|
||
|
if (flag) {
|
||
|
UCHAR &storedFlag = data.m_storageFlag;
|
||
|
UCHAR oldFlag = storedFlag;
|
||
|
|
||
|
storedFlag |= flag;
|
||
|
|
||
|
if (data.m_passiveCacheId == 0)
|
||
|
data.m_passiveCacheId = getNewPassiveCacheId();
|
||
|
|
||
|
if ((storedFlag & ON_DISK) && !(oldFlag & ON_DISK)) {
|
||
|
ResourcesTable::ColIterator it = m_resources->getTable().colBegin(data.m_passiveCacheId);
|
||
|
for (; it; ++it) {
|
||
|
std::set<LockedResourceP> &resources = *it;
|
||
|
|
||
|
std::set<LockedResourceP>::iterator jt;
|
||
|
for (jt = resources.begin(); jt != resources.end(); ++jt)
|
||
|
(*jt)->enableBackup();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifndef USE_SQLITE_HDPOOL
|
||
|
if ((storedFlag & IN_MEMORY) && !(oldFlag & IN_MEMORY)) {
|
||
|
data.m_fx = fx;
|
||
|
(*m_descriptorCallback)(data.m_treeDescription, data.m_fx);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::disableCache(TFx *fx)
|
||
|
{
|
||
|
int idx = fx->getAttributes()->passiveCacheDataIdx();
|
||
|
if (idx < 0)
|
||
|
return;
|
||
|
|
||
|
FxData &data = m_fxDataVector[idx];
|
||
|
|
||
|
QMutexLocker locker(&m_mutex);
|
||
|
|
||
|
#ifdef DIAGNOSTICS
|
||
|
DIAGNOSTICS_STR("#activity.txt | " + QTime::currentTime().toString() + " " +
|
||
|
QString("Disable Cache"));
|
||
|
#endif
|
||
|
|
||
|
StorageFlag flag = getStorageMode();
|
||
|
if (flag) {
|
||
|
UCHAR &storedFlag = data.m_storageFlag;
|
||
|
UCHAR oldFlag = storedFlag;
|
||
|
|
||
|
storedFlag &= ~flag;
|
||
|
|
||
|
if ((oldFlag & IN_MEMORY) && !(storedFlag & IN_MEMORY)) {
|
||
|
m_resources->getTable().erase(data.m_passiveCacheId);
|
||
|
|
||
|
data.m_fx = TFxP();
|
||
|
data.m_treeDescription = "";
|
||
|
}
|
||
|
|
||
|
#ifdef USE_SQLITE_HDPOOL
|
||
|
if ((oldFlag & ON_DISK) && !(storedFlag & ON_DISK))
|
||
|
TCacheResourcePool::instance()->releaseReferences("P" + QString::number(data.m_passiveCacheId));
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::toggleCache(TFx *fx)
|
||
|
{
|
||
|
int &idx = fx->getAttributes()->passiveCacheDataIdx();
|
||
|
touchFxData(idx);
|
||
|
|
||
|
FxData &data = m_fxDataVector[idx];
|
||
|
|
||
|
QMutexLocker locker(&m_mutex);
|
||
|
|
||
|
StorageFlag flag = getStorageMode();
|
||
|
if (flag) {
|
||
|
UCHAR &storedFlag = data.m_storageFlag;
|
||
|
UCHAR oldFlag = storedFlag;
|
||
|
|
||
|
storedFlag ^= flag;
|
||
|
|
||
|
#ifdef DIAGNOSTICS
|
||
|
DIAGNOSTICS_STR("#activity.txt | " + QTime::currentTime().toString() + " " +
|
||
|
QString("Toggle Cache (now ") + ((storedFlag & IN_MEMORY) ? "enabled)" : "disabled)"));
|
||
|
#endif
|
||
|
|
||
|
if (data.m_passiveCacheId == 0)
|
||
|
data.m_passiveCacheId = getNewPassiveCacheId();
|
||
|
|
||
|
if ((storedFlag & ON_DISK) && !(oldFlag & ON_DISK)) {
|
||
|
ResourcesTable::ColIterator it = m_resources->getTable().colBegin(data.m_passiveCacheId);
|
||
|
for (; it; ++it) {
|
||
|
std::set<LockedResourceP> &resources = *it;
|
||
|
|
||
|
std::set<LockedResourceP>::iterator jt;
|
||
|
for (jt = resources.begin(); jt != resources.end(); ++jt)
|
||
|
(*jt)->enableBackup();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Implementa la versione contraria - eliminazione dell'fx nell'hdPool...
|
||
|
//Metti anche questo in versione ritardata con flush... Il flush e' da unificare...
|
||
|
//e magari da spostare direttamente nell'hdPool
|
||
|
|
||
|
if ((oldFlag & IN_MEMORY) && !(storedFlag & IN_MEMORY)) {
|
||
|
m_resources->getTable().erase(data.m_passiveCacheId);
|
||
|
|
||
|
data.m_fx = TFxP();
|
||
|
data.m_treeDescription = "";
|
||
|
}
|
||
|
|
||
|
#ifndef USE_SQLITE_HDPOOL
|
||
|
if ((storedFlag & IN_MEMORY) && !(oldFlag & IN_MEMORY)) {
|
||
|
data.m_fx = fx;
|
||
|
(*m_descriptorCallback)(data.m_treeDescription, data.m_fx);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef USE_SQLITE_HDPOOL
|
||
|
if ((oldFlag & ON_DISK) && !(storedFlag & ON_DISK))
|
||
|
TCacheResourcePool::instance()->releaseReferences("P" + QString::number(data.m_passiveCacheId));
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::invalidateLevel(const std::string &levelName)
|
||
|
{
|
||
|
QMutexLocker locker(&m_mutex);
|
||
|
|
||
|
//Traverse the managed resources for passed levelName.
|
||
|
ResourcesTable &table = m_resources->getTable();
|
||
|
ResourcesTable::Iterator it = table.begin();
|
||
|
while (it) {
|
||
|
std::set<LockedResourceP> &resources = *it;
|
||
|
std::set<LockedResourceP>::iterator jt, kt;
|
||
|
for (jt = resources.begin(); jt != resources.end();) {
|
||
|
if ((*jt)->getName().find(levelName) != string::npos) {
|
||
|
kt = jt++;
|
||
|
it->erase(kt);
|
||
|
} else
|
||
|
++jt;
|
||
|
}
|
||
|
|
||
|
if (resources.empty())
|
||
|
it = table.erase(it);
|
||
|
else
|
||
|
++it;
|
||
|
}
|
||
|
|
||
|
#ifdef USE_SQLITE_HDPOOL
|
||
|
//Store the level name until the invalidation is forced
|
||
|
m_invalidatedLevels.insert(levelName);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::forceInvalidate()
|
||
|
{
|
||
|
#ifdef USE_SQLITE_HDPOOL
|
||
|
TCacheResourcePool *pool = TCacheResourcePool::instance();
|
||
|
|
||
|
//Clear all invalidated levels from the resource pool
|
||
|
std::set<std::string>::iterator it;
|
||
|
for (it = m_invalidatedLevels.begin(); it != m_invalidatedLevels.end(); ++it)
|
||
|
pool->clearKeyword(*it);
|
||
|
|
||
|
m_invalidatedLevels.clear();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
//Generate the fx's tree description. If it is contained in one of those
|
||
|
//stored with cached fxs, release their associated resources.
|
||
|
void TPassiveCacheManager::onFxChanged(const TFxP &fx)
|
||
|
{
|
||
|
#ifndef USE_SQLITE_HDPOOL
|
||
|
|
||
|
std::string fxTreeDescription;
|
||
|
(*m_descriptorCallback)(fxTreeDescription, fx);
|
||
|
|
||
|
unsigned int count = m_fxDataVector.size();
|
||
|
for (unsigned int i = 0; i < count; ++i) {
|
||
|
FxData &data = m_fxDataVector[i];
|
||
|
|
||
|
if (!data.m_fx)
|
||
|
continue;
|
||
|
|
||
|
if (data.m_treeDescription.find(fxTreeDescription) != std::string::npos)
|
||
|
m_resources->getTable().erase(data.m_passiveCacheId);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
//Regenerate the tree descriptions of cached fxs. If the new description does
|
||
|
//not match the previous one, release the associated resources.
|
||
|
void TPassiveCacheManager::onXsheetChanged()
|
||
|
{
|
||
|
#ifndef USE_SQLITE_HDPOOL
|
||
|
|
||
|
#ifdef DIAGNOSTICS
|
||
|
DIAGNOSTICS_STR("#activity.txt | " + QTime::currentTime().toString() + " XSheet changed");
|
||
|
#endif
|
||
|
|
||
|
unsigned int count = m_fxDataVector.size();
|
||
|
for (unsigned int i = 0; i < count; ++i) {
|
||
|
FxData &data = m_fxDataVector[i];
|
||
|
|
||
|
if (!data.m_fx)
|
||
|
continue;
|
||
|
|
||
|
std::string newTreeDescription;
|
||
|
(*m_descriptorCallback)(newTreeDescription, data.m_fx);
|
||
|
|
||
|
if (data.m_treeDescription != newTreeDescription) {
|
||
|
m_resources->getTable().erase(data.m_passiveCacheId);
|
||
|
data.m_treeDescription = newTreeDescription;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::getResource(
|
||
|
TCacheResourceP &resource, const string &alias,
|
||
|
const TFxP &fx, double frame, const TRenderSettings &rs,
|
||
|
ResourceDeclaration *resData)
|
||
|
{
|
||
|
if (!(m_enabled && fx && rs.m_userCachable))
|
||
|
return;
|
||
|
|
||
|
StorageFlag flag = getStorageMode(fx.getPointer());
|
||
|
if (flag == NONE)
|
||
|
return;
|
||
|
|
||
|
std::string contextName(getContextName());
|
||
|
if (contextName.empty())
|
||
|
return;
|
||
|
|
||
|
//Build a resource if none was passed.
|
||
|
if (!resource)
|
||
|
resource = TCacheResourceP(alias, true);
|
||
|
|
||
|
#ifdef USE_SQLITE_HDPOOL
|
||
|
if (flag & ON_DISK) {
|
||
|
resource->enableBackup();
|
||
|
|
||
|
int passiveCacheId = m_fxDataVector[fx->getAttributes()->passiveCacheDataIdx()].m_passiveCacheId;
|
||
|
TCacheResourcePool::instance()->addReference(resource, "P" + QString::number(passiveCacheId));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (flag & IN_MEMORY) {
|
||
|
QMutexLocker locker(&m_mutex);
|
||
|
|
||
|
int passiveCacheId = m_fxDataVector[fx->getAttributes()->passiveCacheDataIdx()].m_passiveCacheId;
|
||
|
m_resources->getTable().value(contextName, passiveCacheId).insert(resource);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::releaseContextNamesWithPrefix(const std::string &prefix)
|
||
|
{
|
||
|
QMutexLocker locker(&m_mutex);
|
||
|
|
||
|
#ifdef DIAGNOSTICS
|
||
|
DIAGNOSTICS_STR("#activity.txt | " + QTime::currentTime().toString() +
|
||
|
" Release Context Name (" + QString::fromStdString(prefix) + ")");
|
||
|
#endif
|
||
|
|
||
|
//Retrieve the context range
|
||
|
std::string prefixPlus1 = prefix;
|
||
|
prefixPlus1[prefix.size() - 1]++;
|
||
|
|
||
|
{
|
||
|
std::map<std::string, UCHAR>::iterator it, jt;
|
||
|
it = m_contextNames.lower_bound(prefix);
|
||
|
jt = m_contextNames.lower_bound(prefixPlus1);
|
||
|
m_contextNames.erase(it, jt);
|
||
|
}
|
||
|
|
||
|
//Transfer to temporary
|
||
|
ResourcesTable &table = m_resources->getTable();
|
||
|
std::map<std::string, ResourcesTable::Row> &rows = m_resources->getTable().rows();
|
||
|
|
||
|
std::map<std::string, ResourcesTable::Row>::iterator it, jt, kt;
|
||
|
it = rows.lower_bound(prefix);
|
||
|
jt = rows.lower_bound(prefixPlus1);
|
||
|
|
||
|
std::string temporaryName("T");
|
||
|
for (kt = it; kt != jt; ++kt) {
|
||
|
ResourcesTable::RowIterator lt(kt);
|
||
|
for (; lt; ++lt)
|
||
|
table.value(temporaryName, lt.col()).insert(lt->begin(), lt->end());
|
||
|
}
|
||
|
rows.erase(it, jt == rows.end() ? rows.lower_bound(prefixPlus1) : jt);
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::releaseOldResources()
|
||
|
{
|
||
|
QMutexLocker locker(&m_mutex);
|
||
|
|
||
|
//Release all the resources that were stored in the old render instance of the
|
||
|
//context, PLUS those of the temporary container.
|
||
|
//Resources that were held by the TPassiveCacheManager::getResource() procedure
|
||
|
//are now duplicated in a proper row of the table - and will not be freed.
|
||
|
std::string contextName(getContextName());
|
||
|
if (contextName.empty())
|
||
|
return;
|
||
|
|
||
|
char &lastChar = contextName[contextName.size() - 1];
|
||
|
lastChar = '0' + !(lastChar - '0');
|
||
|
|
||
|
ResourcesTable &table = m_resources->getTable();
|
||
|
table.erase(contextName);
|
||
|
table.erase("T");
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::onRenderInstanceStart(unsigned long renderId)
|
||
|
{
|
||
|
TFxCacheManagerDelegate::onRenderInstanceStart(renderId);
|
||
|
|
||
|
#ifdef USE_SQLITE_HDPOOL
|
||
|
//Force invalidation of levels before the render starts
|
||
|
forceInvalidate();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::onRenderInstanceEnd(unsigned long renderId)
|
||
|
{
|
||
|
QMutexLocker locker(&m_mutex);
|
||
|
|
||
|
releaseOldResources();
|
||
|
m_contextNamesByRenderId.erase(renderId);
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
void TPassiveCacheManager::onRenderStatusEnd(int renderStatus)
|
||
|
{
|
||
|
if (renderStatus == TRenderer::TESTRUN)
|
||
|
releaseOldResources();
|
||
|
}
|