[WASAPI] fix stream types and frequencies enumeration
[xbmc.git] / xbmc / guilib / TextureManager.cpp
blobae002d0f3544106518fdcc60330745131bd7c35d
1 /*
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.
7 */
9 #include "TextureManager.h"
11 #include "ServiceBroker.h"
12 #include "Texture.h"
13 #include "URL.h"
14 #include "commons/ilog.h"
15 #include "filesystem/Directory.h"
16 #include "filesystem/File.h"
17 #include "guilib/TextureBundle.h"
18 #include "guilib/TextureFormats.h"
19 #include "utils/StringUtils.h"
20 #include "utils/URIUtils.h"
21 #include "utils/log.h"
22 #include "windowing/GraphicContext.h"
23 #include "windowing/WinSystem.h"
25 #include <mutex>
27 #ifdef _DEBUG_TEXTURES
28 #include "utils/TimeUtils.h"
29 #endif
30 #if defined(TARGET_DARWIN_IOS)
31 #define WIN_SYSTEM_CLASS CWinSystemIOS
32 #include "windowing/ios/WinSystemIOS.h" // for g_Windowing in CGUITextureManager::FreeUnusedTextures
33 #elif defined(TARGET_DARWIN_TVOS)
34 #define WIN_SYSTEM_CLASS CWinSystemTVOS
35 #include "windowing/tvos/WinSystemTVOS.h" // for g_Windowing in CGUITextureManager::FreeUnusedTextures
36 #endif
38 #if defined(HAS_GL) || defined(HAS_GLES)
39 #include "system_gl.h"
40 #endif
42 #include "FFmpegImage.h"
44 #include <algorithm>
45 #include <cassert>
46 #include <exception>
48 /************************************************************************/
49 /* */
50 /************************************************************************/
51 CTextureArray::CTextureArray(int width, int height, int loops, bool texCoordsArePixels)
53 m_width = width;
54 m_height = height;
55 m_loops = loops;
56 m_orientation = 0;
57 m_texWidth = 0;
58 m_texHeight = 0;
59 m_texCoordsArePixels = false;
62 CTextureArray::CTextureArray()
64 Reset();
67 CTextureArray::~CTextureArray() = default;
69 unsigned int CTextureArray::size() const
71 return m_textures.size();
75 void CTextureArray::Reset()
77 m_textures.clear();
78 m_delays.clear();
79 m_width = 0;
80 m_height = 0;
81 m_loops = 0;
82 m_orientation = 0;
83 m_texWidth = 0;
84 m_texHeight = 0;
85 m_texCoordsArePixels = false;
88 void CTextureArray::Add(std::shared_ptr<CTexture> texture, int delay)
90 if (!texture)
91 return;
93 m_texWidth = texture->GetTextureWidth();
94 m_texHeight = texture->GetTextureHeight();
95 m_texCoordsArePixels = false;
97 m_textures.emplace_back(std::move(texture));
98 m_delays.push_back(delay);
101 void CTextureArray::Set(std::shared_ptr<CTexture> texture, int width, int height)
103 assert(!m_textures.size()); // don't try and set a texture if we already have one!
104 m_width = width;
105 m_height = height;
106 m_orientation = texture ? texture->GetOrientation() : 0;
107 Add(std::move(texture), 2);
110 void CTextureArray::Free()
112 std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
113 Reset();
117 /************************************************************************/
118 /* */
119 /************************************************************************/
121 CTextureMap::CTextureMap()
123 m_referenceCount = 0;
124 m_memUsage = 0;
127 CTextureMap::CTextureMap(const std::string& textureName, int width, int height, int loops)
128 : m_texture(width, height, loops)
129 , m_textureName(textureName)
131 m_referenceCount = 0;
132 m_memUsage = 0;
135 CTextureMap::~CTextureMap()
137 FreeTexture();
140 bool CTextureMap::Release()
142 if (!m_texture.m_textures.size())
143 return true;
144 if (!m_referenceCount)
145 return true;
147 m_referenceCount--;
148 if (!m_referenceCount)
150 return true;
152 return false;
155 const std::string& CTextureMap::GetName() const
157 return m_textureName;
160 const CTextureArray& CTextureMap::GetTexture()
162 m_referenceCount++;
163 return m_texture;
166 void CTextureMap::Dump() const
168 if (!m_referenceCount)
169 return; // nothing to see here
171 CLog::Log(LOGDEBUG, "{0}: texture:{1} has {2} frames {3} refcount", __FUNCTION__, m_textureName,
172 m_texture.m_textures.size(), m_referenceCount);
175 unsigned int CTextureMap::GetMemoryUsage() const
177 return m_memUsage;
180 void CTextureMap::Flush()
182 if (!m_referenceCount)
183 FreeTexture();
187 void CTextureMap::FreeTexture()
189 m_texture.Free();
192 void CTextureMap::SetHeight(int height)
194 m_texture.m_height = height;
197 void CTextureMap::SetWidth(int height)
199 m_texture.m_width = height;
202 bool CTextureMap::IsEmpty() const
204 return m_texture.m_textures.empty();
207 void CTextureMap::Add(std::unique_ptr<CTexture> texture, int delay)
209 if (texture)
210 m_memUsage += sizeof(CTexture) + (texture->GetTextureWidth() * texture->GetTextureHeight() * 4);
212 m_texture.Add(std::move(texture), delay);
215 /************************************************************************/
216 /* */
217 /************************************************************************/
218 CGUITextureManager::CGUITextureManager(void)
220 // we set the theme bundle to be the first bundle (thus prioritizing it)
221 m_TexBundle[0].SetThemeBundle(true);
224 CGUITextureManager::~CGUITextureManager(void)
226 Cleanup();
229 /************************************************************************/
230 /* */
231 /************************************************************************/
232 bool CGUITextureManager::CanLoad(const std::string &texturePath)
234 if (texturePath.empty())
235 return false;
237 if (!CURL::IsFullPath(texturePath))
238 return true; // assume we have it
240 // we can't (or shouldn't) be loading from remote paths, so check these
241 return URIUtils::IsHD(texturePath);
244 bool CGUITextureManager::HasTexture(const std::string &textureName, std::string *path, int *bundle, int *size)
246 std::unique_lock<CCriticalSection> lock(m_section);
248 // default values
249 if (bundle) *bundle = -1;
250 if (size) *size = 0;
251 if (path) *path = textureName;
253 if (textureName.empty())
254 return false;
256 if (!CanLoad(textureName))
257 return false;
259 // Check our loaded and bundled textures - we store in bundles using \\.
260 std::string bundledName = CTextureBundle::Normalize(textureName);
261 for (int i = 0; i < (int)m_vecTextures.size(); ++i)
263 CTextureMap *pMap = m_vecTextures[i];
264 if (pMap->GetName() == textureName)
266 if (size) *size = 1;
267 return true;
271 for (int i = 0; i < 2; i++)
273 if (m_TexBundle[i].HasFile(bundledName))
275 if (bundle) *bundle = i;
276 return true;
280 std::string fullPath = GetTexturePath(textureName);
281 if (path)
282 *path = fullPath;
284 return !fullPath.empty();
287 const CTextureArray& CGUITextureManager::Load(const std::string& strTextureName, bool checkBundleOnly /*= false */)
289 std::string strPath;
290 static CTextureArray emptyTexture;
291 int bundle = -1;
292 int size = 0;
294 if (strTextureName.empty())
295 return emptyTexture;
297 if (!HasTexture(strTextureName, &strPath, &bundle, &size))
298 return emptyTexture;
300 if (size) // we found the texture
302 for (int i = 0; i < (int)m_vecTextures.size(); ++i)
304 CTextureMap *pMap = m_vecTextures[i];
305 if (pMap->GetName() == strTextureName)
307 //CLog::Log(LOGDEBUG, "Total memusage {}", GetMemoryUsage());
308 return pMap->GetTexture();
311 // Whoops, not there.
312 return emptyTexture;
315 for (auto i = m_unusedTextures.begin(); i != m_unusedTextures.end(); ++i)
317 CTextureMap* pMap = i->first;
319 auto timestamp = i->second.time_since_epoch();
320 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(timestamp);
322 if (pMap->GetName() == strTextureName && duration.count() > 0)
324 m_vecTextures.push_back(pMap);
325 m_unusedTextures.erase(i);
326 return pMap->GetTexture();
330 if (checkBundleOnly && bundle == -1)
331 return emptyTexture;
333 //Lock here, we will do stuff that could break rendering
334 std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
336 #ifdef _DEBUG_TEXTURES
337 const auto start = std::chrono::steady_clock::now();
338 #endif
340 if (bundle >= 0 && StringUtils::EndsWithNoCase(strPath, ".gif"))
342 CTextureMap* pMap = nullptr;
343 std::optional<CTextureBundleXBT::Animation> animation =
344 m_TexBundle[bundle].LoadAnim(strTextureName);
345 if (!animation)
347 CLog::Log(LOGERROR, "Texture manager unable to load bundled file: {}", strTextureName);
348 return emptyTexture;
351 int nLoops = animation.value().loops;
352 int width = animation.value().width;
353 int height = animation.value().height;
355 unsigned int maxWidth = 0;
356 unsigned int maxHeight = 0;
357 pMap = new CTextureMap(strTextureName, width, height, nLoops);
358 for (auto& texture : animation.value().textures)
360 maxWidth = std::max(maxWidth, texture.first->GetWidth());
361 maxHeight = std::max(maxHeight, texture.first->GetHeight());
362 pMap->Add(std::move(texture.first), texture.second);
365 pMap->SetWidth((int)maxWidth);
366 pMap->SetHeight((int)maxHeight);
368 m_vecTextures.push_back(pMap);
369 return pMap->GetTexture();
371 else if (StringUtils::EndsWithNoCase(strPath, ".gif") ||
372 StringUtils::EndsWithNoCase(strPath, ".apng"))
374 std::string mimeType;
375 if (StringUtils::EndsWithNoCase(strPath, ".gif"))
376 mimeType = "image/gif";
377 else if (StringUtils::EndsWithNoCase(strPath, ".apng"))
378 mimeType = "image/apng";
380 XFILE::CFile file;
381 std::vector<uint8_t> buf;
382 CFFmpegImage anim(mimeType);
384 if (file.LoadFile(strPath, buf) <= 0 || !anim.Initialize(buf.data(), buf.size()))
386 CLog::Log(LOGERROR, "Texture manager unable to load file: {}", CURL::GetRedacted(strPath));
387 file.Close();
388 return emptyTexture;
391 CTextureMap* pMap = new CTextureMap(strTextureName, 0, 0, 0);
392 unsigned int maxWidth = 0;
393 unsigned int maxHeight = 0;
394 uint64_t maxMemoryUsage = 91238400;// 1920*1080*4*11 bytes, i.e, a total of approx. 12 full hd frames
396 auto frame = anim.ReadFrame();
397 while (frame)
399 std::unique_ptr<CTexture> glTexture = CTexture::CreateTexture();
400 if (glTexture)
402 glTexture->LoadFromMemory(anim.Width(), anim.Height(), frame->GetPitch(), XB_FMT_A8R8G8B8, true, frame->m_pImage);
403 maxWidth = std::max(maxWidth, glTexture->GetWidth());
404 maxHeight = std::max(maxHeight, glTexture->GetHeight());
405 pMap->Add(std::move(glTexture), frame->m_delay);
408 if (pMap->GetMemoryUsage() <= maxMemoryUsage)
410 frame = anim.ReadFrame();
412 else
414 CLog::Log(LOGDEBUG, "Memory limit ({} bytes) exceeded, {} frames extracted from file : {}",
415 (maxMemoryUsage / 11) * 12, pMap->GetTexture().size(),
416 CURL::GetRedacted(strPath));
417 break;
421 pMap->SetWidth((int)maxWidth);
422 pMap->SetHeight((int)maxHeight);
424 file.Close();
426 m_vecTextures.push_back(pMap);
427 return pMap->GetTexture();
430 std::unique_ptr<CTexture> pTexture;
431 int width = 0, height = 0;
432 if (bundle >= 0)
434 std::optional<CTextureBundleXBT::Texture> texture =
435 m_TexBundle[bundle].LoadTexture(strTextureName);
436 if (!texture)
438 CLog::Log(LOGERROR, "Texture manager unable to load bundled file: {}", strTextureName);
439 return emptyTexture;
442 pTexture = std::move(texture.value().texture);
443 width = texture.value().width;
444 height = texture.value().height;
446 else
448 pTexture = CTexture::LoadFromFile(strPath);
449 if (!pTexture)
450 return emptyTexture;
451 width = pTexture->GetWidth();
452 height = pTexture->GetHeight();
455 if (!pTexture) return emptyTexture;
457 CTextureMap* pMap = new CTextureMap(strTextureName, width, height, 0);
458 pMap->Add(std::move(pTexture), 100);
459 m_vecTextures.push_back(pMap);
461 #ifdef _DEBUG_TEXTURES
462 const auto end = std::chrono::steady_clock::now();
463 const std::chrono::duration<double, std::milli> duration = end - start;
464 CLog::Log(LOGDEBUG, "Load {}: {:.3f} ms {}", strPath, duration.count(),
465 (bundle >= 0) ? "(bundled)" : "");
466 #endif
468 return pMap->GetTexture();
472 void CGUITextureManager::ReleaseTexture(const std::string& strTextureName, bool immediately /*= false */)
474 std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
476 ivecTextures i;
477 i = m_vecTextures.begin();
478 while (i != m_vecTextures.end())
480 CTextureMap* pMap = *i;
481 if (pMap->GetName() == strTextureName)
483 if (pMap->Release())
485 //CLog::Log(LOGINFO, " cleanup:{}", strTextureName);
486 // add to our textures to free
487 std::chrono::time_point<std::chrono::steady_clock> timestamp;
489 if (!immediately)
490 timestamp = std::chrono::steady_clock::now();
492 m_unusedTextures.emplace_back(pMap, timestamp);
493 i = m_vecTextures.erase(i);
495 return;
497 ++i;
499 CLog::Log(LOGWARNING, "{}: Unable to release texture {}", __FUNCTION__, strTextureName);
502 void CGUITextureManager::FreeUnusedTextures(unsigned int timeDelay)
504 std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
505 for (auto i = m_unusedTextures.begin(); i != m_unusedTextures.end();)
507 auto now = std::chrono::steady_clock::now();
508 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now - i->second);
510 if (duration.count() >= timeDelay)
512 delete i->first;
513 i = m_unusedTextures.erase(i);
515 else
516 ++i;
519 #if defined(HAS_GL) || defined(HAS_GLES)
520 for (unsigned int i = 0; i < m_unusedHwTextures.size(); ++i)
522 // on ios/tvos the hw textures might be deleted from the os
523 // when XBMC is backgrounded (e.x. for backgrounded music playback)
524 // sanity check before delete in that case.
525 #if defined(TARGET_DARWIN_EMBEDDED)
526 auto winSystem = dynamic_cast<WIN_SYSTEM_CLASS*>(CServiceBroker::GetWinSystem());
527 if (!winSystem->IsBackgrounded() || glIsTexture(m_unusedHwTextures[i]))
528 #endif
529 glDeleteTextures(1, (GLuint*) &m_unusedHwTextures[i]);
531 #endif
532 m_unusedHwTextures.clear();
535 void CGUITextureManager::ReleaseHwTexture(unsigned int texture)
537 std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
538 m_unusedHwTextures.push_back(texture);
541 void CGUITextureManager::Cleanup()
543 std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
545 ivecTextures i;
546 i = m_vecTextures.begin();
547 while (i != m_vecTextures.end())
549 CTextureMap* pMap = *i;
550 CLog::Log(LOGWARNING, "{}: Having to cleanup texture {}", __FUNCTION__, pMap->GetName());
551 delete pMap;
552 i = m_vecTextures.erase(i);
554 m_TexBundle[0].Close();
555 m_TexBundle[1].Close();
556 m_TexBundle[0] = CTextureBundle(true);
557 m_TexBundle[1] = CTextureBundle();
558 FreeUnusedTextures();
561 void CGUITextureManager::Dump() const
563 CLog::Log(LOGDEBUG, "{0}: total texturemaps size: {1}", __FUNCTION__, m_vecTextures.size());
565 for (int i = 0; i < (int)m_vecTextures.size(); ++i)
567 const CTextureMap* pMap = m_vecTextures[i];
568 if (!pMap->IsEmpty())
569 pMap->Dump();
573 void CGUITextureManager::Flush()
575 std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
577 ivecTextures i;
578 i = m_vecTextures.begin();
579 while (i != m_vecTextures.end())
581 CTextureMap* pMap = *i;
582 pMap->Flush();
583 if (pMap->IsEmpty() )
585 delete pMap;
586 i = m_vecTextures.erase(i);
588 else
590 ++i;
595 unsigned int CGUITextureManager::GetMemoryUsage() const
597 unsigned int memUsage = 0;
598 for (int i = 0; i < (int)m_vecTextures.size(); ++i)
600 memUsage += m_vecTextures[i]->GetMemoryUsage();
602 return memUsage;
605 void CGUITextureManager::SetTexturePath(const std::string &texturePath)
607 std::unique_lock<CCriticalSection> lock(m_section);
608 m_texturePaths.clear();
609 AddTexturePath(texturePath);
612 void CGUITextureManager::AddTexturePath(const std::string &texturePath)
614 std::unique_lock<CCriticalSection> lock(m_section);
615 if (!texturePath.empty())
616 m_texturePaths.push_back(texturePath);
619 void CGUITextureManager::RemoveTexturePath(const std::string &texturePath)
621 std::unique_lock<CCriticalSection> lock(m_section);
622 for (std::vector<std::string>::iterator it = m_texturePaths.begin(); it != m_texturePaths.end(); ++it)
624 if (*it == texturePath)
626 m_texturePaths.erase(it);
627 return;
632 std::string CGUITextureManager::GetTexturePath(const std::string &textureName, bool directory /* = false */)
634 if (CURL::IsFullPath(textureName))
635 return textureName;
636 else
637 { // texture doesn't include the full path, so check all fallbacks
638 std::unique_lock<CCriticalSection> lock(m_section);
639 for (const std::string& it : m_texturePaths)
641 std::string path = URIUtils::AddFileToFolder(it, "media", textureName);
642 if (directory)
644 if (XFILE::CDirectory::Exists(path))
645 return path;
647 else
649 if (XFILE::CFile::Exists(path))
650 return path;
655 CLog::Log(LOGDEBUG, "[Warning] CGUITextureManager::GetTexturePath: could not find texture '{}'",
656 textureName);
657 return "";
660 std::vector<std::string> CGUITextureManager::GetBundledTexturesFromPath(
661 const std::string& texturePath)
663 std::vector<std::string> items = m_TexBundle[0].GetTexturesFromPath(texturePath);
664 if (items.empty())
665 items = m_TexBundle[1].GetTexturesFromPath(texturePath);
666 return items;