VTB: release CVBuffer after it actually has been rendered
[xbmc.git] / xbmc / TextureCache.cpp
blob0ef1de03510d35c530a4fb7a18918a1f1c78e347
1 /*
2 * Copyright (C) 2005-2013 Team XBMC
3 * http://xbmc.org
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "TextureCache.h"
22 #include "TextureCacheJob.h"
23 #include "filesystem/File.h"
24 #include "profiles/ProfilesManager.h"
25 #include "threads/SingleLock.h"
26 #include "utils/Crc32.h"
27 #include "settings/AdvancedSettings.h"
28 #include "utils/log.h"
29 #include "utils/URIUtils.h"
30 #include "utils/StringUtils.h"
31 #include "URL.h"
33 using namespace XFILE;
35 CTextureCache &CTextureCache::GetInstance()
37 static CTextureCache s_cache;
38 return s_cache;
41 CTextureCache::CTextureCache() : CJobQueue(false, 1, CJob::PRIORITY_LOW_PAUSABLE)
45 CTextureCache::~CTextureCache()
49 void CTextureCache::Initialize()
51 CSingleLock lock(m_databaseSection);
52 if (!m_database.IsOpen())
53 m_database.Open();
56 void CTextureCache::Deinitialize()
58 CancelJobs();
59 CSingleLock lock(m_databaseSection);
60 m_database.Close();
63 bool CTextureCache::IsCachedImage(const std::string &url) const
65 if (url != "-" && !CURL::IsFullPath(url))
66 return true;
67 if (URIUtils::PathHasParent(url, "special://skin", true) ||
68 URIUtils::PathHasParent(url, "special://temp", true) ||
69 URIUtils::PathHasParent(url, "resource://", true) ||
70 URIUtils::PathHasParent(url, "androidapp://", true) ||
71 URIUtils::PathHasParent(url, CProfilesManager::GetInstance().GetThumbnailsFolder(), true))
72 return true;
73 return false;
76 bool CTextureCache::HasCachedImage(const std::string &url)
78 CTextureDetails details;
79 std::string cachedImage(GetCachedImage(url, details));
80 return (!cachedImage.empty() && cachedImage != url);
83 std::string CTextureCache::GetCachedImage(const std::string &image, CTextureDetails &details, bool trackUsage)
85 std::string url = CTextureUtils::UnwrapImageURL(image);
87 if (IsCachedImage(url))
88 return url;
90 // lookup the item in the database
91 if (GetCachedTexture(url, details))
93 if (trackUsage)
94 IncrementUseCount(details);
95 return GetCachedPath(details.file);
97 return "";
100 bool CTextureCache::CanCacheImageURL(const CURL &url)
102 return (url.GetUserName().empty() || url.GetUserName() == "music");
105 std::string CTextureCache::CheckCachedImage(const std::string &url, bool &needsRecaching)
107 CTextureDetails details;
108 std::string path(GetCachedImage(url, details, true));
109 needsRecaching = !details.hash.empty();
110 if (!path.empty())
111 return path;
112 return "";
115 void CTextureCache::BackgroundCacheImage(const std::string &url)
117 if (url.empty())
118 return;
120 CTextureDetails details;
121 std::string path(GetCachedImage(url, details));
122 if (!path.empty() && details.hash.empty())
123 return; // image is already cached and doesn't need to be checked further
125 path = CTextureUtils::UnwrapImageURL(url);
126 if (path.empty())
127 return;
129 // needs (re)caching
130 AddJob(new CTextureCacheJob(path, details.hash));
133 std::string CTextureCache::CacheImage(const std::string &image, CBaseTexture **texture /* = NULL */, CTextureDetails *details /* = NULL */)
135 std::string url = CTextureUtils::UnwrapImageURL(image);
136 if (url.empty())
137 return "";
139 CSingleLock lock(m_processingSection);
140 if (m_processinglist.find(url) == m_processinglist.end())
142 m_processinglist.insert(url);
143 lock.Leave();
144 // cache the texture directly
145 CTextureCacheJob job(url);
146 bool success = job.CacheTexture(texture);
147 OnCachingComplete(success, &job);
148 if (success && details)
149 *details = job.m_details;
150 return success ? GetCachedPath(job.m_details.file) : "";
152 lock.Leave();
154 // wait for currently processing job to end.
155 while (true)
157 m_completeEvent.WaitMSec(1000);
159 CSingleLock lock(m_processingSection);
160 if (m_processinglist.find(url) == m_processinglist.end())
161 break;
164 CTextureDetails tempDetails;
165 if (!details)
166 details = &tempDetails;
167 return GetCachedImage(url, *details, true);
170 bool CTextureCache::CacheImage(const std::string &image, CTextureDetails &details)
172 std::string path = GetCachedImage(image, details);
173 if (path.empty()) // not cached
174 path = CacheImage(image, NULL, &details);
176 return !path.empty();
179 void CTextureCache::ClearCachedImage(const std::string &url, bool deleteSource /*= false */)
181 //! @todo This can be removed when the texture cache covers everything.
182 std::string path = deleteSource ? url : "";
183 std::string cachedFile;
184 if (ClearCachedTexture(url, cachedFile))
185 path = GetCachedPath(cachedFile);
186 if (CFile::Exists(path))
187 CFile::Delete(path);
188 path = URIUtils::ReplaceExtension(path, ".dds");
189 if (CFile::Exists(path))
190 CFile::Delete(path);
193 bool CTextureCache::ClearCachedImage(int id)
195 std::string cachedFile;
196 if (ClearCachedTexture(id, cachedFile))
198 cachedFile = GetCachedPath(cachedFile);
199 if (CFile::Exists(cachedFile))
200 CFile::Delete(cachedFile);
201 cachedFile = URIUtils::ReplaceExtension(cachedFile, ".dds");
202 if (CFile::Exists(cachedFile))
203 CFile::Delete(cachedFile);
204 return true;
206 return false;
209 bool CTextureCache::GetCachedTexture(const std::string &url, CTextureDetails &details)
211 CSingleLock lock(m_databaseSection);
212 return m_database.GetCachedTexture(url, details);
215 bool CTextureCache::AddCachedTexture(const std::string &url, const CTextureDetails &details)
217 CSingleLock lock(m_databaseSection);
218 return m_database.AddCachedTexture(url, details);
221 void CTextureCache::IncrementUseCount(const CTextureDetails &details)
223 static const size_t count_before_update = 100;
224 CSingleLock lock(m_useCountSection);
225 m_useCounts.reserve(count_before_update);
226 m_useCounts.push_back(details);
227 if (m_useCounts.size() >= count_before_update)
229 AddJob(new CTextureUseCountJob(m_useCounts));
230 m_useCounts.clear();
234 bool CTextureCache::SetCachedTextureValid(const std::string &url, bool updateable)
236 CSingleLock lock(m_databaseSection);
237 return m_database.SetCachedTextureValid(url, updateable);
240 bool CTextureCache::ClearCachedTexture(const std::string &url, std::string &cachedURL)
242 CSingleLock lock(m_databaseSection);
243 return m_database.ClearCachedTexture(url, cachedURL);
246 bool CTextureCache::ClearCachedTexture(int id, std::string &cachedURL)
248 CSingleLock lock(m_databaseSection);
249 return m_database.ClearCachedTexture(id, cachedURL);
252 std::string CTextureCache::GetCacheFile(const std::string &url)
254 auto crc = Crc32::ComputeFromLowerCase(url);
255 std::string hex = StringUtils::Format("%08x", crc);
256 std::string hash = StringUtils::Format("%c/%s", hex[0], hex.c_str());
257 return hash;
260 std::string CTextureCache::GetCachedPath(const std::string &file)
262 return URIUtils::AddFileToFolder(CProfilesManager::GetInstance().GetThumbnailsFolder(), file);
265 void CTextureCache::OnCachingComplete(bool success, CTextureCacheJob *job)
267 if (success)
269 if (job->m_oldHash == job->m_details.hash)
270 SetCachedTextureValid(job->m_url, job->m_details.updateable);
271 else
272 AddCachedTexture(job->m_url, job->m_details);
275 { // remove from our processing list
276 CSingleLock lock(m_processingSection);
277 std::set<std::string>::iterator i = m_processinglist.find(job->m_url);
278 if (i != m_processinglist.end())
279 m_processinglist.erase(i);
282 m_completeEvent.Set();
285 void CTextureCache::OnJobComplete(unsigned int jobID, bool success, CJob *job)
287 if (strcmp(job->GetType(), kJobTypeCacheImage) == 0)
288 OnCachingComplete(success, (CTextureCacheJob *)job);
289 return CJobQueue::OnJobComplete(jobID, success, job);
292 void CTextureCache::OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job)
294 if (strcmp(job->GetType(), kJobTypeCacheImage) == 0 && !progress)
295 { // check our processing list
297 CSingleLock lock(m_processingSection);
298 const CTextureCacheJob *cacheJob = (CTextureCacheJob *)job;
299 std::set<std::string>::iterator i = m_processinglist.find(cacheJob->m_url);
300 if (i == m_processinglist.end())
302 m_processinglist.insert(cacheJob->m_url);
303 return;
306 CancelJob(job);
308 else
309 CJobQueue::OnJobProgress(jobID, progress, total, job);
312 bool CTextureCache::Export(const std::string &image, const std::string &destination, bool overwrite)
314 CTextureDetails details;
315 std::string cachedImage(GetCachedImage(image, details));
316 if (!cachedImage.empty())
318 std::string dest = destination + URIUtils::GetExtension(cachedImage);
319 if (overwrite || !CFile::Exists(dest))
321 if (CFile::Copy(cachedImage, dest))
322 return true;
323 CLog::Log(LOGERROR, "%s failed exporting '%s' to '%s'", __FUNCTION__, cachedImage.c_str(), dest.c_str());
326 return false;
329 bool CTextureCache::Export(const std::string &image, const std::string &destination)
331 CTextureDetails details;
332 std::string cachedImage(GetCachedImage(image, details));
333 if (!cachedImage.empty())
335 if (CFile::Copy(cachedImage, destination))
336 return true;
337 CLog::Log(LOGERROR, "%s failed exporting '%s' to '%s'", __FUNCTION__, cachedImage.c_str(), destination.c_str());
339 return false;