Blob Blame Raw
#include <memory>

// TnzCore includes
#include "tgl.h"

// TnzExt includes
#include "ext/meshtexturizer.h"

// tcg includes
#include "tcg/tcg_list.h"

// Qt includes
#include <QString>
#include <QCache>
#include <QMutex>
#include <QMutexLocker>

#include "ext/ttexturesstorage.h"

//***************************************************************************************
//    Local namespace - structures
//***************************************************************************************

struct TexturesContainer {
	MeshTexturizer m_texturizer; //!< The mesh texturizer - actual textures container
	tcg::list<QString> m_keys;   //!< Keys in the storage

public:
	TexturesContainer() {}

private:
	TexturesContainer(const TexturesContainer &);
	TexturesContainer &operator=(const TexturesContainer &);
};

//***************************************************************************************
//    Local namespace - variables
//***************************************************************************************

namespace
{

QMutex l_mutex(QMutex::Recursive); // A mutex is needed to synchronize access to the following objects

std::map<int, TexturesContainer *> l_texturesContainers;	 // Texture Containers by display lists space id
QCache<QString, DrawableTextureDataP> l_objects(500 * 1024); // 500 MB cache for now - NOTE: MUST be allocated before the following

} // namespace

//***************************************************************************************
//    Local namespace - global functions
//***************************************************************************************

namespace
{

inline QString textureString(int dlSpaceId, const std::string &texId)
{
	return QString::number(dlSpaceId) + "_" + QString::fromStdString(texId);
}

//-------------------------------------------------------------------------------------

inline void deleteTexturesContainer(const std::pair<int, TexturesContainer *> &pair)
{
	delete pair.second;
}
}

//***************************************************************************************
//    DrawableTextureData implementation
//***************************************************************************************

DrawableTextureData::~DrawableTextureData()
{
	QMutexLocker locker(&l_mutex);

	TexturesContainer *texContainer = l_texturesContainers[m_dlSpaceId];

	if (m_dlSpaceId >= 0) {
		// Load the container's display lists space (remember current OpenGL context, too)
		TGLDisplayListsProxy *proxy = TGLDisplayListsManager::instance()->dlProxy(m_dlSpaceId);

		TGlContext currentContext = tglGetCurrentContext();

		// Unbind the textures
		{
			QMutexLocker locker(proxy->mutex());

			proxy->makeCurrent();
			texContainer->m_texturizer.unbindTexture(m_texId);
		}

		// Restore OpenGL context - equivalent to tglDoneCurrent if currentContext == TGlContext()
		tglMakeCurrent(currentContext);
	} else
		// Temporary - use current OpenGL context directly
		texContainer->m_texturizer.unbindTexture(m_texId);

	texContainer->m_keys.erase(m_objIdx);
}

//***************************************************************************************
//    TTexturesStorage implementation
//***************************************************************************************

TTexturesStorage::TTexturesStorage()
{
	// This singleton is dependent on TGLDisplayListsManager
	TGLDisplayListsManager::instance()->addObserver(this);
}

//-------------------------------------------------------------------------------------

TTexturesStorage::~TTexturesStorage()
{
	l_objects.clear();
	std::for_each(l_texturesContainers.begin(), l_texturesContainers.end(), deleteTexturesContainer);
}

//-------------------------------------------------------------------------------------

TTexturesStorage *TTexturesStorage::instance()
{
	static TTexturesStorage theInstance;
	return &theInstance;
}

//-------------------------------------------------------------------------------------

DrawableTextureDataP TTexturesStorage::loadTexture(
	const std::string &textureId, const TRaster32P &ras, const TRectD &geometry)
{
	// Try to retrieve the proxy associated to current OpenGL context
	TGlContext currentContext = tglGetCurrentContext();
	int dlSpaceId = TGLDisplayListsManager::instance()->displayListsSpaceId(currentContext);

	QString texString(::textureString(dlSpaceId, textureId));

	// Deal with containers
	QMutexLocker locker(&l_mutex);

	// If necessary, allocate a textures container
	std::map<int, TexturesContainer *>::iterator it = l_texturesContainers.find(dlSpaceId);
	if (it == l_texturesContainers.end())
		it = l_texturesContainers.insert(std::make_pair(dlSpaceId, new TexturesContainer)).first;

	MeshTexturizer &texturizer = it->second->m_texturizer;

	DrawableTextureDataP dataPtr = std::make_shared<DrawableTextureData>();
	DrawableTextureData *data = dataPtr.get();

	data->m_dlSpaceId = dlSpaceId;
	data->m_texId = texturizer.bindTexture(ras, geometry);
	data->m_objIdx = it->second->m_keys.push_back(texString);
	data->m_textureData = texturizer.getTextureData(data->m_texId);

	l_objects.insert(texString, new DrawableTextureDataP(dataPtr),
					 (ras->getLx() * ras->getLy() * ras->getPixelSize()) >> 10);

	if (dlSpaceId < 0) {
		// obj is a temporary. It was pushed in the cache to make space for it - however, it must not be
		// stored. Remove it now.
		l_objects.remove(texString);
	}

	return dataPtr;
}

//-------------------------------------------------------------------------------------

void TTexturesStorage::unloadTexture(const std::string &textureId)
{
	QMutexLocker locker(&l_mutex);

	// Remove the specified texture from ALL the display lists spaces
	std::map<int, TexturesContainer *>::iterator it, iEnd(l_texturesContainers.end());
	for (it = l_texturesContainers.begin(); it != iEnd; ++it)
		l_objects.remove(::textureString(it->first, textureId));
}

//-----------------------------------------------------------------------------------

void TTexturesStorage::onDisplayListDestroyed(int dlSpaceId)
{
	QMutexLocker locker(&l_mutex);

	// Remove the textures container associated with dlSpaceId
	std::map<int, TexturesContainer *>::iterator it = l_texturesContainers.find(dlSpaceId);
	if (it == l_texturesContainers.end())
		return;

	tcg::list<QString>::iterator st, sEnd(it->second->m_keys.end());

	for (st = it->second->m_keys.begin(); st != sEnd;) // Note that the increment is performed BEFORE the texture is removed.
		l_objects.remove(*st++);					   // This is because texture removal may destroy the key being addressed,
													   // whose iterator would then be invalidated.
	delete it->second;
	l_texturesContainers.erase(it);
}

//-------------------------------------------------------------------------------------

DrawableTextureDataP TTexturesStorage::getTextureData(const std::string &textureId)
{
	// Get current display lists space
	TGlContext currentContext = tglGetCurrentContext();
	int dlSpaceId = TGLDisplayListsManager::instance()->displayListsSpaceId(currentContext);

	// If there is no known associated display lists space, the texture cannot be stored.
	if (dlSpaceId < 0)
		return DrawableTextureDataP();

	QMutexLocker locker(&l_mutex);

	// Search the texture object
	QString texString(::textureString(dlSpaceId, textureId));
	if (!l_objects.contains(texString))
		return DrawableTextureDataP();

	return *l_objects.object(texString);
}