2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "GUILargeTextureManager.h"
11 #include "ServiceBroker.h"
12 #include "TextureCache.h"
13 #include "commons/ilog.h"
14 #include "guilib/GUIComponent.h"
15 #include "guilib/Texture.h"
16 #include "utils/JobManager.h"
17 #include "utils/TimeUtils.h"
18 #include "utils/log.h"
19 #include "windowing/GraphicContext.h"
20 #include "windowing/WinSystem.h"
27 CImageLoader::CImageLoader(const std::string
& path
, const bool useCache
)
28 : m_path(path
), m_texture(nullptr)
30 m_use_cache
= useCache
;
33 CImageLoader::~CImageLoader() = default;
35 bool CImageLoader::DoWork()
37 bool needsChecking
= false;
40 std::string texturePath
= CServiceBroker::GetGUI()->GetTextureManager().GetTexturePath(m_path
);
41 if (texturePath
.empty())
45 loadPath
= CServiceBroker::GetTextureCache()->CheckCachedImage(texturePath
, needsChecking
);
47 loadPath
= texturePath
;
49 if (!loadPath
.empty())
51 // direct route - load the image
52 auto start
= std::chrono::steady_clock::now();
54 CTexture::LoadFromFile(loadPath
, CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(),
55 CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight());
57 auto end
= std::chrono::steady_clock::now();
58 auto duration
= std::chrono::duration_cast
<std::chrono::milliseconds
>(end
- start
);
60 if (duration
.count() > 100)
61 CLog::Log(LOGDEBUG
, "{} - took {} ms to load {}", __FUNCTION__
, duration
.count(), loadPath
);
66 CServiceBroker::GetTextureCache()->BackgroundCacheImage(texturePath
);
71 // Fallthrough on failure:
72 CLog::Log(LOGERROR
, "{} - Direct texture file loading failed for {}", __FUNCTION__
, loadPath
);
76 return false; // We're done
78 // not in our texture cache or it failed to load from it, so try and load directly and then cache the result
79 CServiceBroker::GetTextureCache()->CacheImage(texturePath
, &m_texture
);
80 return (m_texture
!= NULL
);
83 CGUILargeTextureManager::CLargeTexture::CLargeTexture(const std::string
&path
):
90 CGUILargeTextureManager::CLargeTexture::~CLargeTexture()
92 assert(m_refCount
== 0);
96 void CGUILargeTextureManager::CLargeTexture::AddRef()
101 bool CGUILargeTextureManager::CLargeTexture::DecrRef(bool deleteImmediately
)
107 if (deleteImmediately
)
110 m_timeToDelete
= CTimeUtils::GetFrameTime() + TIME_TO_DELETE
;
116 bool CGUILargeTextureManager::CLargeTexture::DeleteIfRequired(bool deleteImmediately
)
118 if (m_refCount
== 0 && (deleteImmediately
|| m_timeToDelete
< CTimeUtils::GetFrameTime()))
126 void CGUILargeTextureManager::CLargeTexture::SetTexture(std::unique_ptr
<CTexture
> texture
)
128 assert(!m_texture
.size());
131 const auto width
= texture
->GetWidth();
132 const auto height
= texture
->GetHeight();
133 m_texture
.Set(std::move(texture
), width
, height
);
137 CGUILargeTextureManager::CGUILargeTextureManager() = default;
139 CGUILargeTextureManager::~CGUILargeTextureManager() = default;
141 void CGUILargeTextureManager::CleanupUnusedImages(bool immediately
)
143 std::unique_lock
<CCriticalSection
> lock(m_listSection
);
144 // check for items to remove from allocated list, and remove
145 listIterator it
= m_allocated
.begin();
146 while (it
!= m_allocated
.end())
148 CLargeTexture
*image
= *it
;
149 if (image
->DeleteIfRequired(immediately
))
150 it
= m_allocated
.erase(it
);
156 // if available, increment reference count, and return the image.
157 // else, add to the queue list if appropriate.
158 bool CGUILargeTextureManager::GetImage(const std::string
&path
, CTextureArray
&texture
, bool firstRequest
, const bool useCache
)
160 std::unique_lock
<CCriticalSection
> lock(m_listSection
);
161 for (listIterator it
= m_allocated
.begin(); it
!= m_allocated
.end(); ++it
)
163 CLargeTexture
*image
= *it
;
164 if (image
->GetPath() == path
)
168 texture
= image
->GetTexture();
169 return texture
.size() > 0;
174 QueueImage(path
, useCache
);
179 void CGUILargeTextureManager::ReleaseImage(const std::string
&path
, bool immediately
)
181 std::unique_lock
<CCriticalSection
> lock(m_listSection
);
182 for (listIterator it
= m_allocated
.begin(); it
!= m_allocated
.end(); ++it
)
184 CLargeTexture
*image
= *it
;
185 if (image
->GetPath() == path
)
187 if (image
->DecrRef(immediately
) && immediately
)
188 m_allocated
.erase(it
);
192 for (queueIterator it
= m_queued
.begin(); it
!= m_queued
.end(); ++it
)
194 unsigned int id
= it
->first
;
195 CLargeTexture
*image
= it
->second
;
196 if (image
->GetPath() == path
&& image
->DecrRef(true))
199 CServiceBroker::GetJobManager()->CancelJob(id
);
206 // queue the image, and start the background loader if necessary
207 void CGUILargeTextureManager::QueueImage(const std::string
&path
, bool useCache
)
212 std::unique_lock
<CCriticalSection
> lock(m_listSection
);
213 for (queueIterator it
= m_queued
.begin(); it
!= m_queued
.end(); ++it
)
215 CLargeTexture
*image
= it
->second
;
216 if (image
->GetPath() == path
)
219 return; // already queued
224 CLargeTexture
*image
= new CLargeTexture(path
);
225 unsigned int jobID
= CServiceBroker::GetJobManager()->AddJob(new CImageLoader(path
, useCache
),
226 this, CJob::PRIORITY_NORMAL
);
227 m_queued
.emplace_back(jobID
, image
);
230 void CGUILargeTextureManager::OnJobComplete(unsigned int jobID
, bool success
, CJob
*job
)
232 // see if we still have this job id
233 std::unique_lock
<CCriticalSection
> lock(m_listSection
);
234 for (queueIterator it
= m_queued
.begin(); it
!= m_queued
.end(); ++it
)
236 if (it
->first
== jobID
)
238 CImageLoader
*loader
= static_cast<CImageLoader
*>(job
);
239 CLargeTexture
*image
= it
->second
;
240 image
->SetTexture(std::move(loader
->m_texture
));
241 loader
->m_texture
= NULL
; // we want to keep the texture, and jobs are auto-deleted.
243 m_allocated
.push_back(image
);