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 "settings/AdvancedSettings.h"
17 #include "settings/SettingsComponent.h"
18 #include "utils/JobManager.h"
19 #include "utils/TimeUtils.h"
20 #include "utils/log.h"
21 #include "windowing/GraphicContext.h"
22 #include "windowing/WinSystem.h"
29 CImageLoader::CImageLoader(const std::string
& path
, const bool useCache
)
30 : m_path(path
), m_texture(nullptr)
32 m_use_cache
= useCache
;
35 CImageLoader::~CImageLoader() = default;
37 bool CImageLoader::DoWork()
39 bool needsChecking
= false;
42 std::string texturePath
= CServiceBroker::GetGUI()->GetTextureManager().GetTexturePath(m_path
);
43 if (texturePath
.empty())
47 loadPath
= CServiceBroker::GetTextureCache()->CheckCachedImage(texturePath
, needsChecking
);
49 loadPath
= texturePath
;
51 if (!loadPath
.empty())
53 // direct route - load the image
54 auto start
= std::chrono::steady_clock::now();
56 CTexture::LoadFromFile(loadPath
, CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(),
57 CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight());
59 auto end
= std::chrono::steady_clock::now();
60 auto duration
= std::chrono::duration_cast
<std::chrono::milliseconds
>(end
- start
);
62 if (duration
.count() > 100)
63 CLog::Log(LOGDEBUG
, "{} - took {} ms to load {}", __FUNCTION__
, duration
.count(), loadPath
);
68 CServiceBroker::GetTextureCache()->BackgroundCacheImage(texturePath
);
70 if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_guiAsyncTextureUpload
)
71 m_texture
->LoadToGPUAsync();
76 // Fallthrough on failure:
77 CLog::Log(LOGERROR
, "{} - Direct texture file loading failed for {}", __FUNCTION__
, loadPath
);
81 return false; // We're done
83 // not in our texture cache or it failed to load from it, so try and load directly and then cache the result
84 CServiceBroker::GetTextureCache()->CacheImage(texturePath
, &m_texture
);
89 if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_guiAsyncTextureUpload
)
90 m_texture
->LoadToGPUAsync();
95 CGUILargeTextureManager::CLargeTexture::CLargeTexture(const std::string
&path
):
102 CGUILargeTextureManager::CLargeTexture::~CLargeTexture()
104 assert(m_refCount
== 0);
108 void CGUILargeTextureManager::CLargeTexture::AddRef()
113 bool CGUILargeTextureManager::CLargeTexture::DecrRef(bool deleteImmediately
)
119 if (deleteImmediately
)
122 m_timeToDelete
= CTimeUtils::GetFrameTime() + TIME_TO_DELETE
;
128 bool CGUILargeTextureManager::CLargeTexture::DeleteIfRequired(bool deleteImmediately
)
130 if (m_refCount
== 0 && (deleteImmediately
|| m_timeToDelete
< CTimeUtils::GetFrameTime()))
138 void CGUILargeTextureManager::CLargeTexture::SetTexture(std::unique_ptr
<CTexture
> texture
)
140 assert(!m_texture
.size());
143 const auto width
= texture
->GetWidth();
144 const auto height
= texture
->GetHeight();
145 m_texture
.Set(std::move(texture
), width
, height
);
149 CGUILargeTextureManager::CGUILargeTextureManager() = default;
151 CGUILargeTextureManager::~CGUILargeTextureManager() = default;
153 void CGUILargeTextureManager::CleanupUnusedImages(bool immediately
)
155 std::unique_lock
<CCriticalSection
> lock(m_listSection
);
156 // check for items to remove from allocated list, and remove
157 listIterator it
= m_allocated
.begin();
158 while (it
!= m_allocated
.end())
160 CLargeTexture
*image
= *it
;
161 if (image
->DeleteIfRequired(immediately
))
162 it
= m_allocated
.erase(it
);
168 // if available, increment reference count, and return the image.
169 // else, add to the queue list if appropriate.
170 bool CGUILargeTextureManager::GetImage(const std::string
&path
, CTextureArray
&texture
, bool firstRequest
, const bool useCache
)
172 std::unique_lock
<CCriticalSection
> lock(m_listSection
);
173 for (listIterator it
= m_allocated
.begin(); it
!= m_allocated
.end(); ++it
)
175 CLargeTexture
*image
= *it
;
176 if (image
->GetPath() == path
)
180 texture
= image
->GetTexture();
181 return texture
.size() > 0;
186 QueueImage(path
, useCache
);
191 void CGUILargeTextureManager::ReleaseImage(const std::string
&path
, bool immediately
)
193 std::unique_lock
<CCriticalSection
> lock(m_listSection
);
194 for (listIterator it
= m_allocated
.begin(); it
!= m_allocated
.end(); ++it
)
196 CLargeTexture
*image
= *it
;
197 if (image
->GetPath() == path
)
199 if (image
->DecrRef(immediately
) && immediately
)
200 m_allocated
.erase(it
);
204 for (queueIterator it
= m_queued
.begin(); it
!= m_queued
.end(); ++it
)
206 unsigned int id
= it
->first
;
207 CLargeTexture
*image
= it
->second
;
208 if (image
->GetPath() == path
&& image
->DecrRef(true))
211 CServiceBroker::GetJobManager()->CancelJob(id
);
218 // queue the image, and start the background loader if necessary
219 void CGUILargeTextureManager::QueueImage(const std::string
&path
, bool useCache
)
224 std::unique_lock
<CCriticalSection
> lock(m_listSection
);
225 for (queueIterator it
= m_queued
.begin(); it
!= m_queued
.end(); ++it
)
227 CLargeTexture
*image
= it
->second
;
228 if (image
->GetPath() == path
)
231 return; // already queued
236 CLargeTexture
*image
= new CLargeTexture(path
);
237 unsigned int jobID
= CServiceBroker::GetJobManager()->AddJob(new CImageLoader(path
, useCache
),
238 this, CJob::PRIORITY_NORMAL
);
239 m_queued
.emplace_back(jobID
, image
);
242 void CGUILargeTextureManager::OnJobComplete(unsigned int jobID
, bool success
, CJob
*job
)
244 // see if we still have this job id
245 std::unique_lock
<CCriticalSection
> lock(m_listSection
);
246 for (queueIterator it
= m_queued
.begin(); it
!= m_queued
.end(); ++it
)
248 if (it
->first
== jobID
)
250 CImageLoader
*loader
= static_cast<CImageLoader
*>(job
);
251 CLargeTexture
*image
= it
->second
;
252 image
->SetTexture(std::move(loader
->m_texture
));
253 loader
->m_texture
= NULL
; // we want to keep the texture, and jobs are auto-deleted.
255 m_allocated
.push_back(image
);