1 #include <memory>
2 
3 // TnzCore includes
4 #include "tgl.h"
5 
6 // TnzExt includes
7 #include "ext/meshtexturizer.h"
8 
9 // tcg includes
10 #include "tcg/tcg_list.h"
11 
12 // Qt includes
13 #include <QString>
14 #include <QCache>
15 #include <QMutex>
16 #include <QMutexLocker>
17 
18 #include "ext/ttexturesstorage.h"
19 
20 //***************************************************************************************
21 //    Local namespace - structures
22 //***************************************************************************************
23 
24 struct TexturesContainer {
25   MeshTexturizer
26       m_texturizer;  //!< The mesh texturizer - actual textures container
27   tcg::list<QString> m_keys;  //!< Keys in the storage
28 
29 public:
TexturesContainerTexturesContainer30   TexturesContainer() {}
31 
32 private:
33   TexturesContainer(const TexturesContainer &);
34   TexturesContainer &operator=(const TexturesContainer &);
35 };
36 
37 //***************************************************************************************
38 //    Local namespace - variables
39 //***************************************************************************************
40 
41 namespace {
42 
43 QMutex l_mutex(QMutex::Recursive);  // A mutex is needed to synchronize access
44                                     // to the following objects
45 
46 std::map<int, TexturesContainer *>
47     l_texturesContainers;  // Texture Containers by display lists space id
48 QCache<QString, DrawableTextureDataP> l_objects(500 * 1024);  // 500 MB cache
49                                                               // for now - NOTE:
50                                                               // MUST be
51                                                               // allocated
52                                                               // before the
53                                                               // following
54 
55 }  // namespace
56 
57 //***************************************************************************************
58 //    Local namespace - global functions
59 //***************************************************************************************
60 
61 namespace {
62 
textureString(int dlSpaceId,const std::string & texId)63 inline QString textureString(int dlSpaceId, const std::string &texId) {
64   return QString::number(dlSpaceId) + "_" + QString::fromStdString(texId);
65 }
66 
67 //-------------------------------------------------------------------------------------
68 
deleteTexturesContainer(const std::pair<int,TexturesContainer * > & pair)69 inline void deleteTexturesContainer(
70     const std::pair<int, TexturesContainer *> &pair) {
71   delete pair.second;
72 }
73 }
74 
75 //***************************************************************************************
76 //    DrawableTextureData implementation
77 //***************************************************************************************
78 
~DrawableTextureData()79 DrawableTextureData::~DrawableTextureData() {
80   QMutexLocker locker(&l_mutex);
81 
82   TexturesContainer *texContainer = l_texturesContainers[m_dlSpaceId];
83 
84   if (m_dlSpaceId >= 0) {
85     // Load the container's display lists space (remember current OpenGL
86     // context, too)
87     TGLDisplayListsProxy *proxy =
88         TGLDisplayListsManager::instance()->dlProxy(m_dlSpaceId);
89 
90     TGlContext currentContext = tglGetCurrentContext();
91 
92     // Unbind the textures
93     {
94       QMutexLocker locker(proxy->mutex());
95 
96       proxy->makeCurrent();
97       texContainer->m_texturizer.unbindTexture(m_texId);
98     }
99 
100     // Restore OpenGL context - equivalent to tglDoneCurrent if currentContext
101     // == TGlContext()
102     tglMakeCurrent(currentContext);
103   } else
104     // Temporary - use current OpenGL context directly
105     texContainer->m_texturizer.unbindTexture(m_texId);
106 
107   texContainer->m_keys.erase(m_objIdx);
108 }
109 
110 //***************************************************************************************
111 //    TTexturesStorage implementation
112 //***************************************************************************************
113 
TTexturesStorage()114 TTexturesStorage::TTexturesStorage() {
115   // This singleton is dependent on TGLDisplayListsManager
116   TGLDisplayListsManager::instance()->addObserver(this);
117 }
118 
119 //-------------------------------------------------------------------------------------
120 
~TTexturesStorage()121 TTexturesStorage::~TTexturesStorage() {
122   l_objects.clear();
123   std::for_each(l_texturesContainers.begin(), l_texturesContainers.end(),
124                 deleteTexturesContainer);
125 }
126 
127 //-------------------------------------------------------------------------------------
128 
instance()129 TTexturesStorage *TTexturesStorage::instance() {
130   static TTexturesStorage theInstance;
131   return &theInstance;
132 }
133 
134 //-------------------------------------------------------------------------------------
135 
loadTexture(const std::string & textureId,const TRaster32P & ras,const TRectD & geometry)136 DrawableTextureDataP TTexturesStorage::loadTexture(const std::string &textureId,
137                                                    const TRaster32P &ras,
138                                                    const TRectD &geometry) {
139   // Try to retrieve the proxy associated to current OpenGL context
140   TGlContext currentContext = tglGetCurrentContext();
141   int dlSpaceId =
142       TGLDisplayListsManager::instance()->displayListsSpaceId(currentContext);
143 
144   QString texString(::textureString(dlSpaceId, textureId));
145 
146   // Deal with containers
147   QMutexLocker locker(&l_mutex);
148 
149   // If necessary, allocate a textures container
150   std::map<int, TexturesContainer *>::iterator it =
151       l_texturesContainers.find(dlSpaceId);
152   if (it == l_texturesContainers.end())
153     it = l_texturesContainers
154              .insert(std::make_pair(dlSpaceId, new TexturesContainer))
155              .first;
156 
157   MeshTexturizer &texturizer = it->second->m_texturizer;
158 
159   DrawableTextureDataP dataPtr = std::make_shared<DrawableTextureData>();
160   DrawableTextureData *data    = dataPtr.get();
161 
162   data->m_dlSpaceId   = dlSpaceId;
163   data->m_texId       = texturizer.bindTexture(ras, geometry);
164   data->m_objIdx      = it->second->m_keys.push_back(texString);
165   data->m_textureData = texturizer.getTextureData(data->m_texId);
166 
167   l_objects.insert(texString, new DrawableTextureDataP(dataPtr),
168                    (ras->getLx() * ras->getLy() * ras->getPixelSize()) >> 10);
169 
170   if (dlSpaceId < 0) {
171     // obj is a temporary. It was pushed in the cache to make space for it -
172     // however, it must not be
173     // stored. Remove it now.
174     l_objects.remove(texString);
175   }
176 
177   return dataPtr;
178 }
179 
180 //-------------------------------------------------------------------------------------
181 
unloadTexture(const std::string & textureId)182 void TTexturesStorage::unloadTexture(const std::string &textureId) {
183   QMutexLocker locker(&l_mutex);
184 
185   // Remove the specified texture from ALL the display lists spaces
186   std::map<int, TexturesContainer *>::iterator it,
187       iEnd(l_texturesContainers.end());
188   for (it = l_texturesContainers.begin(); it != iEnd; ++it)
189     l_objects.remove(::textureString(it->first, textureId));
190 }
191 
192 //-----------------------------------------------------------------------------------
193 
onDisplayListDestroyed(int dlSpaceId)194 void TTexturesStorage::onDisplayListDestroyed(int dlSpaceId) {
195   QMutexLocker locker(&l_mutex);
196 
197   // Remove the textures container associated with dlSpaceId
198   std::map<int, TexturesContainer *>::iterator it =
199       l_texturesContainers.find(dlSpaceId);
200   if (it == l_texturesContainers.end()) return;
201 
202   tcg::list<QString>::iterator st, sEnd(it->second->m_keys.end());
203 
204   for (st = it->second->m_keys.begin(); st != sEnd;)  // Note that the increment
205                                                       // is performed BEFORE the
206                                                       // texture is removed.
207     l_objects.remove(*st++);  // This is because texture removal may destroy the
208                               // key being addressed,
209                               // whose iterator would then be invalidated.
210   delete it->second;
211   l_texturesContainers.erase(it);
212 }
213 
214 //-------------------------------------------------------------------------------------
215 
getTextureData(const std::string & textureId)216 DrawableTextureDataP TTexturesStorage::getTextureData(
217     const std::string &textureId) {
218   // Get current display lists space
219   TGlContext currentContext = tglGetCurrentContext();
220   int dlSpaceId =
221       TGLDisplayListsManager::instance()->displayListsSpaceId(currentContext);
222 
223   // If there is no known associated display lists space, the texture cannot be
224   // stored.
225   if (dlSpaceId < 0) return DrawableTextureDataP();
226 
227   QMutexLocker locker(&l_mutex);
228 
229   // Search the texture object
230   QString texString(::textureString(dlSpaceId, textureId));
231   if (!l_objects.contains(texString)) return DrawableTextureDataP();
232 
233   return *l_objects.object(texString);
234 }
235