LCOV - code coverage report
Current view: top level - source/graphics - TextureManager.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 236 507 46.5 %
Date: 2023-01-19 00:18:29 Functions: 34 71 47.9 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "TextureManager.h"
      21             : 
      22             : #include "graphics/Color.h"
      23             : #include "graphics/TextureConverter.h"
      24             : #include "lib/allocators/shared_ptr.h"
      25             : #include "lib/bits.h"
      26             : #include "lib/file/vfs/vfs_tree.h"
      27             : #include "lib/hash.h"
      28             : #include "lib/timer.h"
      29             : #include "maths/MathUtil.h"
      30             : #include "maths/MD5.h"
      31             : #include "ps/CacheLoader.h"
      32             : #include "ps/CLogger.h"
      33             : #include "ps/ConfigDB.h"
      34             : #include "ps/Filesystem.h"
      35             : #include "ps/Profile.h"
      36             : #include "ps/Util.h"
      37             : #include "renderer/backend/IDevice.h"
      38             : #include "renderer/Renderer.h"
      39             : 
      40             : #include <algorithm>
      41             : #include <boost/filesystem.hpp>
      42             : #include <iomanip>
      43             : #include <set>
      44             : #include <sstream>
      45             : #include <unordered_map>
      46             : #include <unordered_set>
      47             : 
      48             : namespace
      49             : {
      50             : 
      51           6 : Renderer::Backend::Format ChooseFormatAndTransformTextureDataIfNeeded(Tex& textureData, const bool hasS3TC)
      52             : {
      53           6 :     const bool alpha = (textureData.m_Flags & TEX_ALPHA) != 0;
      54           6 :     const bool grey = (textureData.m_Flags & TEX_GREY) != 0;
      55           6 :     const size_t dxt = textureData.m_Flags & TEX_DXT;
      56             : 
      57             :     // Some backends don't support BGR as an internal format (like GLES).
      58             :     // TODO: add a check that the format is internally supported.
      59           6 :     if ((textureData.m_Flags & TEX_BGR) != 0)
      60             :     {
      61           0 :         LOGWARNING("Using slow path to convert BGR texture.");
      62           0 :         textureData.transform_to(textureData.m_Flags & ~TEX_BGR);
      63             :     }
      64             : 
      65           6 :     if (dxt)
      66             :     {
      67           6 :         if (hasS3TC)
      68             :         {
      69           6 :             switch (dxt)
      70             :             {
      71           0 :             case DXT1A:
      72           0 :                 return Renderer::Backend::Format::BC1_RGBA_UNORM;
      73           6 :             case 1:
      74           6 :                 return Renderer::Backend::Format::BC1_RGB_UNORM;
      75           0 :             case 3:
      76           0 :                 return Renderer::Backend::Format::BC2_UNORM;
      77           0 :             case 5:
      78           0 :                 return Renderer::Backend::Format::BC3_UNORM;
      79           0 :             default:
      80           0 :                 LOGERROR("Unknown DXT compression.");
      81           0 :                 return Renderer::Backend::Format::UNDEFINED;
      82             :             }
      83             :         }
      84             :         else
      85           0 :             textureData.transform_to(textureData.m_Flags & ~TEX_DXT);
      86             :     }
      87             : 
      88           0 :     switch (textureData.m_Bpp)
      89             :     {
      90           0 :     case 8:
      91           0 :         ENSURE(grey);
      92           0 :         return Renderer::Backend::Format::L8_UNORM;
      93           0 :     case 24:
      94           0 :         ENSURE(!alpha);
      95           0 :         return Renderer::Backend::Format::R8G8B8_UNORM;
      96           0 :     case 32:
      97           0 :         ENSURE(alpha);
      98           0 :         return Renderer::Backend::Format::R8G8B8A8_UNORM;
      99           0 :     default:
     100           0 :         LOGERROR("Unsupported BPP: %zu", textureData.m_Bpp);
     101             :     }
     102             : 
     103           0 :     return Renderer::Backend::Format::UNDEFINED;
     104             : }
     105             : 
     106             : } // anonymous namespace
     107             : 
     108         108 : class CPredefinedTexture
     109             : {
     110             : public:
     111          12 :     const CTexturePtr& GetTexture()
     112             :     {
     113          12 :         return m_Texture;
     114             :     }
     115             : 
     116          54 :     void CreateTexture(
     117             :         std::unique_ptr<Renderer::Backend::ITexture> backendTexture,
     118             :         CTextureManagerImpl* textureManager)
     119             :     {
     120          54 :         Renderer::Backend::ITexture* fallback = backendTexture.get();
     121         108 :         CTextureProperties props(VfsPath{});
     122         162 :         m_Texture = CTexturePtr(new CTexture(
     123         162 :             std::move(backendTexture), fallback, props, textureManager));
     124          54 :         m_Texture->m_State = CTexture::UPLOADED;
     125          54 :         m_Texture->m_Self = m_Texture;
     126          54 :     }
     127             : 
     128             : private:
     129             :     CTexturePtr m_Texture;
     130             : };
     131             : 
     132          36 : class CSingleColorTexture final : public CPredefinedTexture
     133             : {
     134             : public:
     135          36 :     CSingleColorTexture(const CColor& color, Renderer::Backend::IDevice* device,
     136             :         CTextureManagerImpl* textureManager)
     137          36 :         : m_Color(color)
     138             :     {
     139          72 :         std::stringstream textureName;
     140          36 :         textureName << "SingleColorTexture (";
     141          36 :         textureName << "R:" << m_Color.r << ", ";
     142          36 :         textureName << "G:" << m_Color.g << ", ";
     143          36 :         textureName << "B:" << m_Color.b << ", ";
     144          36 :         textureName << "A:" << m_Color.a << ")";
     145             : 
     146             :         std::unique_ptr<Renderer::Backend::ITexture> backendTexture =
     147             :             device->CreateTexture2D(
     148          72 :                 textureName.str().c_str(),
     149             :                 Renderer::Backend::ITexture::Usage::TRANSFER_DST |
     150             :                     Renderer::Backend::ITexture::Usage::SAMPLED,
     151             :                 Renderer::Backend::Format::R8G8B8A8_UNORM,
     152          72 :                 1, 1, Renderer::Backend::Sampler::MakeDefaultSampler(
     153             :                     Renderer::Backend::Sampler::Filter::LINEAR,
     154         108 :                     Renderer::Backend::Sampler::AddressMode::REPEAT));
     155          36 :         CreateTexture(std::move(backendTexture), textureManager);
     156          36 :     }
     157             : 
     158           0 :     void Upload(Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     159             :     {
     160           0 :         if (!GetTexture() || !GetTexture()->GetBackendTexture())
     161           0 :             return;
     162             : 
     163           0 :         const SColor4ub color32 = m_Color.AsSColor4ub();
     164             :         // Construct 1x1 32-bit texture
     165             :         const u8 data[4] =
     166             :         {
     167           0 :             color32.R,
     168           0 :             color32.G,
     169           0 :             color32.B,
     170           0 :             color32.A
     171           0 :         };
     172           0 :         deviceCommandContext->UploadTexture(GetTexture()->GetBackendTexture(),
     173           0 :             Renderer::Backend::Format::R8G8B8A8_UNORM, data, std::size(data));
     174             :     }
     175             : 
     176             : private:
     177             :     CColor m_Color;
     178             : };
     179             : 
     180           9 : class CSingleColorTextureCube final : public CPredefinedTexture
     181             : {
     182             : public:
     183           9 :     CSingleColorTextureCube(const CColor& color, Renderer::Backend::IDevice* device,
     184             :         CTextureManagerImpl* textureManager)
     185           9 :         : m_Color(color)
     186             :     {
     187          18 :         std::stringstream textureName;
     188           9 :         textureName << "SingleColorTextureCube (";
     189           9 :         textureName << "R:" << m_Color.r << ", ";
     190           9 :         textureName << "G:" << m_Color.g << ", ";
     191           9 :         textureName << "B:" << m_Color.b << ", ";
     192           9 :         textureName << "A:" << m_Color.a << ")";
     193             : 
     194             :         std::unique_ptr<Renderer::Backend::ITexture> backendTexture =
     195             :             device->CreateTexture(
     196          18 :                 textureName.str().c_str(), Renderer::Backend::ITexture::Type::TEXTURE_CUBE,
     197             :                 Renderer::Backend::ITexture::Usage::TRANSFER_DST |
     198             :                     Renderer::Backend::ITexture::Usage::SAMPLED,
     199             :                 Renderer::Backend::Format::R8G8B8A8_UNORM,
     200          18 :                 1, 1, Renderer::Backend::Sampler::MakeDefaultSampler(
     201             :                     Renderer::Backend::Sampler::Filter::LINEAR,
     202          27 :                     Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE), 1, 1);
     203           9 :         CreateTexture(std::move(backendTexture), textureManager);
     204           9 :     }
     205             : 
     206           0 :     void Upload(Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     207             :     {
     208           0 :         if (!GetTexture() || !GetTexture()->GetBackendTexture())
     209           0 :             return;
     210             : 
     211           0 :         const SColor4ub color32 = m_Color.AsSColor4ub();
     212             :         // Construct 1x1 32-bit texture
     213             :         const u8 data[4] =
     214             :         {
     215           0 :             color32.R,
     216           0 :             color32.G,
     217           0 :             color32.B,
     218           0 :             color32.A
     219           0 :         };
     220             : 
     221           0 :         for (size_t face = 0; face < 6; ++face)
     222             :         {
     223           0 :             deviceCommandContext->UploadTexture(
     224           0 :                 GetTexture()->GetBackendTexture(), Renderer::Backend::Format::R8G8B8A8_UNORM,
     225           0 :                 data, std::size(data), 0, face);
     226             :         }
     227             :     }
     228             : 
     229             : private:
     230             :     CColor m_Color;
     231             : };
     232             : 
     233           9 : class CGradientTexture final : public CPredefinedTexture
     234             : {
     235             : public:
     236             :     static const uint32_t WIDTH = 256;
     237             :     static const uint32_t NUMBER_OF_LEVELS = 9;
     238             : 
     239           9 :     CGradientTexture(const CColor& colorFrom, const CColor& colorTo,
     240             :         Renderer::Backend::IDevice* device, CTextureManagerImpl* textureManager)
     241           9 :         : m_ColorFrom(colorFrom), m_ColorTo(colorTo)
     242             :     {
     243          18 :         std::stringstream textureName;
     244           9 :         textureName << "GradientTexture";
     245           9 :         textureName << " From (";
     246           9 :         textureName << "R:" << m_ColorFrom.r << ", ";
     247           9 :         textureName << "G:" << m_ColorFrom.g << ", ";
     248           9 :         textureName << "B:" << m_ColorFrom.b << ", ";
     249           9 :         textureName << "A:" << m_ColorFrom.a << ")";
     250           9 :         textureName << " To (";
     251           9 :         textureName << "R:" << m_ColorTo.r << ", ";
     252           9 :         textureName << "G:" << m_ColorTo.g << ", ";
     253           9 :         textureName << "B:" << m_ColorTo.b << ", ";
     254           9 :         textureName << "A:" << m_ColorTo.a << ")";
     255             : 
     256             :         std::unique_ptr<Renderer::Backend::ITexture> backendTexture =
     257             :             device->CreateTexture2D(
     258          18 :                 textureName.str().c_str(),
     259             :                 Renderer::Backend::ITexture::Usage::TRANSFER_DST |
     260             :                     Renderer::Backend::ITexture::Usage::SAMPLED,
     261             :                 Renderer::Backend::Format::R8G8B8A8_UNORM,
     262          18 :                 WIDTH, 1, Renderer::Backend::Sampler::MakeDefaultSampler(
     263             :                     Renderer::Backend::Sampler::Filter::LINEAR,
     264             :                     Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE),
     265          27 :                 NUMBER_OF_LEVELS);
     266           9 :         CreateTexture(std::move(backendTexture), textureManager);
     267           9 :     }
     268             : 
     269           0 :     void Upload(Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     270             :     {
     271           0 :         if (!GetTexture() || !GetTexture()->GetBackendTexture())
     272           0 :             return;
     273             : 
     274             :         std::array<std::array<u8, 4>, WIDTH> data;
     275           0 :         for (uint32_t x = 0; x < WIDTH; ++x)
     276             :         {
     277           0 :             const float t = static_cast<float>(x) / (WIDTH - 1);
     278             :             const CColor color(
     279             :                 Interpolate(m_ColorFrom.r, m_ColorTo.r, t),
     280             :                 Interpolate(m_ColorFrom.g, m_ColorTo.g, t),
     281             :                 Interpolate(m_ColorFrom.b, m_ColorTo.b, t),
     282           0 :                 Interpolate(m_ColorFrom.a, m_ColorTo.a, t));
     283           0 :             const SColor4ub color32 = color.AsSColor4ub();
     284           0 :             data[x][0] = color32.R;
     285           0 :             data[x][1] = color32.G;
     286           0 :             data[x][2] = color32.B;
     287           0 :             data[x][3] = color32.A;
     288             :         }
     289           0 :         for (uint32_t level = 0; level < NUMBER_OF_LEVELS; ++level)
     290             :         {
     291           0 :             deviceCommandContext->UploadTexture(GetTexture()->GetBackendTexture(),
     292           0 :                 Renderer::Backend::Format::R8G8B8A8_UNORM, data.data(), (WIDTH >> level) * data[0].size(), level);
     293             :             // Prepare data for the next level.
     294           0 :             const uint32_t nextLevelWidth = (WIDTH >> (level + 1));
     295           0 :             if (nextLevelWidth > 0)
     296             :             {
     297           0 :                 for (uint32_t x = 0; x < nextLevelWidth; ++x)
     298           0 :                     data[x] = data[(x << 1)];
     299             :                 // Border values should be the same.
     300           0 :                 data[nextLevelWidth - 1] = data[(WIDTH >> level) - 1];
     301             :             }
     302             :         }
     303             :     }
     304             : 
     305             : private:
     306             :     CColor m_ColorFrom, m_ColorTo;
     307             : };
     308             : 
     309             : struct TPhash
     310             : {
     311          23 :     std::size_t operator()(const CTextureProperties& textureProperties) const
     312             :     {
     313          23 :         std::size_t seed = 0;
     314          23 :         hash_combine(seed, m_PathHash(textureProperties.m_Path));
     315          23 :         hash_combine(seed, textureProperties.m_AddressModeU);
     316          23 :         hash_combine(seed, textureProperties.m_AddressModeV);
     317          23 :         hash_combine(seed, textureProperties.m_AnisotropicFilterEnabled);
     318          23 :         hash_combine(seed, textureProperties.m_FormatOverride);
     319          23 :         hash_combine(seed, textureProperties.m_IgnoreQuality);
     320          23 :         return seed;
     321             :     }
     322             : 
     323          23 :     std::size_t operator()(const CTexturePtr& texture) const
     324             :     {
     325          23 :         return this->operator()(texture->m_Properties);
     326             :     }
     327             : 
     328             : private:
     329             :     std::hash<Path> m_PathHash;
     330             : };
     331             : 
     332             : struct TPequal_to
     333             : {
     334           1 :     bool operator()(const CTextureProperties& lhs, const CTextureProperties& rhs) const
     335             :     {
     336             :         return
     337           2 :             lhs.m_Path == rhs.m_Path &&
     338           2 :             lhs.m_AddressModeU == rhs.m_AddressModeU &&
     339           2 :             lhs.m_AddressModeV == rhs.m_AddressModeV &&
     340           2 :             lhs.m_AnisotropicFilterEnabled == rhs.m_AnisotropicFilterEnabled &&
     341           3 :             lhs.m_FormatOverride == rhs.m_FormatOverride &&
     342           2 :             lhs.m_IgnoreQuality == rhs.m_IgnoreQuality;
     343             :     }
     344             : 
     345           1 :     bool operator()(const CTexturePtr& lhs, const CTexturePtr& rhs) const
     346             :     {
     347           1 :         return this->operator()(lhs->m_Properties, rhs->m_Properties);
     348             :     }
     349             : };
     350             : 
     351             : class CTextureManagerImpl
     352             : {
     353             :     friend class CTexture;
     354             : public:
     355           9 :     CTextureManagerImpl(PIVFS vfs, bool highQuality, Renderer::Backend::IDevice* device) :
     356             :         m_VFS(vfs), m_CacheLoader(vfs, L".dds"), m_Device(device),
     357             :         m_TextureConverter(vfs, highQuality),
     358          18 :         m_DefaultTexture(CColor(0.25f, 0.25f, 0.25f, 1.0f), device, this),
     359          18 :         m_ErrorTexture(CColor(1.0f, 0.0f, 1.0f, 1.0f), device, this),
     360          18 :         m_WhiteTexture(CColor(1.0f, 1.0f, 1.0f, 1.0f), device, this),
     361          18 :         m_TransparentTexture(CColor(0.0f, 0.0f, 0.0f, 0.0f), device, this),
     362             :         m_AlphaGradientTexture(
     363          18 :             CColor(1.0f, 1.0f, 1.0f, 0.0f), CColor(1.0f, 1.0f, 1.0f, 1.0f), device, this),
     364          99 :         m_BlackTextureCube(CColor(0.0f, 0.0f, 0.0f, 1.0f), device, this)
     365             :     {
     366             :         // Allow hotloading of textures
     367           9 :         RegisterFileReloadFunc(ReloadChangedFileCB, this);
     368             : 
     369           9 :         m_HasS3TC =
     370          18 :             m_Device->IsTextureFormatSupported(Renderer::Backend::Format::BC1_RGB_UNORM) &&
     371          18 :             m_Device->IsTextureFormatSupported(Renderer::Backend::Format::BC1_RGBA_UNORM) &&
     372          27 :             m_Device->IsTextureFormatSupported(Renderer::Backend::Format::BC2_UNORM) &&
     373           9 :             m_Device->IsTextureFormatSupported(Renderer::Backend::Format::BC3_UNORM);
     374           9 :     }
     375             : 
     376           9 :     ~CTextureManagerImpl()
     377           9 :     {
     378           9 :         UnregisterFileReloadFunc(ReloadChangedFileCB, this);
     379           9 :     }
     380             : 
     381           0 :     const CTexturePtr& GetErrorTexture()
     382             :     {
     383           0 :         return m_ErrorTexture.GetTexture();
     384             :     }
     385             : 
     386           0 :     const CTexturePtr& GetWhiteTexture()
     387             :     {
     388           0 :         return m_WhiteTexture.GetTexture();
     389             :     }
     390             : 
     391           0 :     const CTexturePtr& GetTransparentTexture()
     392             :     {
     393           0 :         return m_TransparentTexture.GetTexture();
     394             :     }
     395             : 
     396           0 :     const CTexturePtr& GetAlphaGradientTexture()
     397             :     {
     398           0 :         return m_AlphaGradientTexture.GetTexture();
     399             :     }
     400             : 
     401           0 :     const CTexturePtr& GetBlackTextureCube()
     402             :     {
     403           0 :         return m_BlackTextureCube.GetTexture();
     404             :     }
     405             : 
     406             :     /**
     407             :      * See CTextureManager::CreateTexture
     408             :      */
     409          12 :     CTexturePtr CreateTexture(const CTextureProperties& props)
     410             :     {
     411             :         // Construct a new default texture with the given properties to use as the search key
     412             :         CTexturePtr texture(new CTexture(
     413          24 :             nullptr, m_DefaultTexture.GetTexture()->GetBackendTexture(), props, this));
     414             : 
     415             :         // Try to find an existing texture with the given properties
     416          12 :         TextureCache::iterator it = m_TextureCache.find(texture);
     417          12 :         if (it != m_TextureCache.end())
     418           1 :             return *it;
     419             : 
     420             :         // Can't find an existing texture - finish setting up this new texture
     421          11 :         texture->m_Self = texture;
     422          11 :         m_TextureCache.insert(texture);
     423          11 :         m_HotloadFiles[props.m_Path].insert(texture);
     424             : 
     425          11 :         return texture;
     426             :     }
     427             : 
     428           0 :     CTexturePtr WrapBackendTexture(
     429             :         std::unique_ptr<Renderer::Backend::ITexture> backendTexture)
     430             :     {
     431           0 :         ENSURE(backendTexture);
     432           0 :         Renderer::Backend::ITexture* fallback = backendTexture.get();
     433             : 
     434           0 :         CTextureProperties props(VfsPath{});
     435             :         CTexturePtr texture(new CTexture(
     436           0 :             std::move(backendTexture), fallback, props, this));
     437           0 :         texture->m_State = CTexture::UPLOADED;
     438           0 :         texture->m_Self = texture;
     439           0 :         return texture;
     440             :     }
     441             : 
     442             :     /**
     443             :      * Load the given file into the texture object and upload it to OpenGL.
     444             :      * Assumes the file already exists.
     445             :      */
     446           6 :     void LoadTexture(const CTexturePtr& texture, const VfsPath& path)
     447             :     {
     448          12 :         PROFILE2("load texture");
     449           6 :         PROFILE2_ATTR("name: %ls", path.string().c_str());
     450             : 
     451          12 :         std::shared_ptr<u8> fileData;
     452             :         size_t fileSize;
     453           6 :         const Status loadStatus = m_VFS->LoadFile(path, fileData, fileSize);
     454           6 :         if (loadStatus != INFO::OK)
     455             :         {
     456           0 :             LOGERROR("Texture failed to load; \"%s\" %s",
     457             :                 texture->m_Properties.m_Path.string8(), GetStatusAsString(loadStatus).c_str());
     458           0 :             texture->ResetBackendTexture(
     459           0 :                 nullptr, m_ErrorTexture.GetTexture()->GetBackendTexture());
     460           0 :             texture->m_TextureData.reset();
     461           0 :             return;
     462             :         }
     463             : 
     464           6 :         texture->m_TextureData = std::make_unique<Tex>();
     465           6 :         Tex& textureData = *texture->m_TextureData;
     466           6 :         const Status decodeStatus = textureData.decode(fileData, fileSize);
     467           6 :         if (decodeStatus != INFO::OK)
     468             :         {
     469           0 :             LOGERROR("Texture failed to decode; \"%s\" %s",
     470             :                 texture->m_Properties.m_Path.string8(), GetStatusAsString(decodeStatus).c_str());
     471           0 :             texture->ResetBackendTexture(
     472           0 :                 nullptr, m_ErrorTexture.GetTexture()->GetBackendTexture());
     473           0 :             texture->m_TextureData.reset();
     474           0 :             return;
     475             :         }
     476             : 
     477           6 :         if (!is_pow2(textureData.m_Width) || !is_pow2(textureData.m_Height))
     478             :         {
     479           0 :             LOGERROR("Texture should have width and height be power of two; \"%s\" %zux%zu",
     480             :                 texture->m_Properties.m_Path.string8(), textureData.m_Width, textureData.m_Height);
     481           0 :             texture->ResetBackendTexture(
     482           0 :                 nullptr, m_ErrorTexture.GetTexture()->GetBackendTexture());
     483           0 :             texture->m_TextureData.reset();
     484           0 :             return;
     485             :         }
     486             : 
     487             :         // Initialise base color from the texture
     488           6 :         texture->m_BaseColor = textureData.get_average_color();
     489             : 
     490           6 :         Renderer::Backend::Format format = Renderer::Backend::Format::UNDEFINED;
     491           6 :         if (texture->m_Properties.m_FormatOverride != Renderer::Backend::Format::UNDEFINED)
     492             :         {
     493           0 :             format = texture->m_Properties.m_FormatOverride;
     494             :             // TODO: it'd be good to remove the override hack and provide information
     495             :             // via XML.
     496           0 :             ENSURE((textureData.m_Flags & TEX_DXT) == 0);
     497           0 :             if (format == Renderer::Backend::Format::A8_UNORM)
     498             :             {
     499           0 :                 ENSURE(textureData.m_Bpp == 8 && (textureData.m_Flags & TEX_GREY));
     500             :             }
     501           0 :             else if (format == Renderer::Backend::Format::R8G8B8A8_UNORM)
     502             :             {
     503           0 :                 ENSURE(textureData.m_Bpp == 32 && (textureData.m_Flags & TEX_ALPHA));
     504             :             }
     505             :             else
     506           0 :                 debug_warn("Unsupported format override.");
     507             :         }
     508             :         else
     509             :         {
     510           6 :             format = ChooseFormatAndTransformTextureDataIfNeeded(textureData, m_HasS3TC);
     511             :         }
     512             : 
     513           6 :         if (format == Renderer::Backend::Format::UNDEFINED)
     514             :         {
     515           0 :             LOGERROR("Texture failed to choose format; \"%s\"", texture->m_Properties.m_Path.string8());
     516           0 :             texture->ResetBackendTexture(
     517           0 :                 nullptr, m_ErrorTexture.GetTexture()->GetBackendTexture());
     518           0 :             texture->m_TextureData.reset();
     519           0 :             return;
     520             :         }
     521             : 
     522           6 :         const uint32_t width = texture->m_TextureData->m_Width;
     523           6 :         const uint32_t height = texture->m_TextureData->m_Height ;
     524           6 :         const uint32_t MIPLevelCount = texture->m_TextureData->GetMIPLevels().size();
     525           6 :         texture->m_BaseLevelOffset = 0;
     526             : 
     527             :         Renderer::Backend::Sampler::Desc defaultSamplerDesc =
     528             :             Renderer::Backend::Sampler::MakeDefaultSampler(
     529             :                 Renderer::Backend::Sampler::Filter::LINEAR,
     530           6 :                 Renderer::Backend::Sampler::AddressMode::REPEAT);
     531             : 
     532           6 :         defaultSamplerDesc.addressModeU = texture->m_Properties.m_AddressModeU;
     533           6 :         defaultSamplerDesc.addressModeV = texture->m_Properties.m_AddressModeV;
     534           6 :         if (texture->m_Properties.m_AnisotropicFilterEnabled && m_Device->GetCapabilities().anisotropicFiltering)
     535             :         {
     536           0 :             int maxAnisotropy = 1;
     537           0 :             CFG_GET_VAL("textures.maxanisotropy", maxAnisotropy);
     538           0 :             const int allowedValues[] = {2, 4, 8, 16};
     539           0 :             if (std::find(std::begin(allowedValues), std::end(allowedValues), maxAnisotropy) != std::end(allowedValues))
     540             :             {
     541           0 :                 defaultSamplerDesc.anisotropyEnabled = true;
     542           0 :                 defaultSamplerDesc.maxAnisotropy = maxAnisotropy;
     543             :             }
     544             :         }
     545             : 
     546           6 :         if (!texture->m_Properties.m_IgnoreQuality)
     547             :         {
     548           6 :             int quality = 2;
     549           6 :             CFG_GET_VAL("textures.quality", quality);
     550           6 :             if (quality == 1)
     551             :             {
     552           0 :                 if (MIPLevelCount > 1 && std::min(width, height) > 8)
     553           0 :                     texture->m_BaseLevelOffset += 1;
     554             :             }
     555           6 :             else if (quality == 0)
     556             :             {
     557           0 :                 if (MIPLevelCount > 2 && std::min(width, height) > 16)
     558           0 :                     texture->m_BaseLevelOffset += 2;
     559           0 :                 while (std::min(width >> texture->m_BaseLevelOffset, height >> texture->m_BaseLevelOffset) > 256 &&
     560           0 :                     MIPLevelCount > texture->m_BaseLevelOffset + 1)
     561             :                 {
     562           0 :                     texture->m_BaseLevelOffset += 1;
     563             :                 }
     564           0 :                 defaultSamplerDesc.mipFilter = Renderer::Backend::Sampler::Filter::NEAREST;
     565           0 :                 defaultSamplerDesc.anisotropyEnabled = false;
     566             :             }
     567             :         }
     568             : 
     569          36 :         texture->m_BackendTexture = m_Device->CreateTexture2D(
     570          12 :             texture->m_Properties.m_Path.string8().c_str(),
     571             :             Renderer::Backend::ITexture::Usage::TRANSFER_DST |
     572             :                 Renderer::Backend::ITexture::Usage::SAMPLED,
     573          12 :             format, (width >> texture->m_BaseLevelOffset), (height >> texture->m_BaseLevelOffset),
     574          12 :             defaultSamplerDesc, MIPLevelCount - texture->m_BaseLevelOffset);
     575             :     }
     576             : 
     577             :     /**
     578             :      * Set up some parameters for the loose cache filename code.
     579             :      */
     580          11 :     void PrepareCacheKey(const CTexturePtr& texture, MD5& hash, u32& version)
     581             :     {
     582             :         // Hash the settings, so we won't use an old loose cache file if the
     583             :         // settings have changed
     584          11 :         CTextureConverter::Settings settings = GetConverterSettings(texture);
     585          11 :         settings.Hash(hash);
     586             : 
     587             :         // Arbitrary version number - change this if we update the code and
     588             :         // need to invalidate old users' caches
     589          11 :         version = 1;
     590          11 :     }
     591             : 
     592             :     /**
     593             :      * Attempts to load a cached version of a texture.
     594             :      * If the texture is loaded (or there was an error), returns true.
     595             :      * Otherwise, returns false to indicate the caller should generate the cached version.
     596             :      */
     597           6 :     bool TryLoadingCached(const CTexturePtr& texture)
     598             :     {
     599           6 :         MD5 hash;
     600             :         u32 version;
     601           6 :         PrepareCacheKey(texture, hash, version);
     602             : 
     603          12 :         VfsPath loadPath;
     604           6 :         Status ret = m_CacheLoader.TryLoadingCached(texture->m_Properties.m_Path, hash, version, loadPath);
     605             : 
     606           6 :         if (ret == INFO::OK)
     607             :         {
     608             :             // Found a cached texture - load it
     609           1 :             LoadTexture(texture, loadPath);
     610           1 :             return true;
     611             :         }
     612           5 :         else if (ret == INFO::SKIPPED)
     613             :         {
     614             :             // No cached version was found - we'll need to create it
     615           5 :             return false;
     616             :         }
     617             :         else
     618             :         {
     619           0 :             ENSURE(ret < 0);
     620             : 
     621             :             // No source file or archive cache was found, so we can't load the
     622             :             // real texture at all - return the error texture instead
     623           0 :             LOGERROR("CCacheLoader failed to find archived or source file for: \"%s\"", texture->m_Properties.m_Path.string8());
     624           0 :             texture->ResetBackendTexture(
     625           0 :                 nullptr, m_ErrorTexture.GetTexture()->GetBackendTexture());
     626           0 :             return true;
     627             :         }
     628             :     }
     629             : 
     630             :     /**
     631             :      * Initiates an asynchronous conversion process, from the texture's
     632             :      * source file to the corresponding loose cache file.
     633             :      */
     634           5 :     void ConvertTexture(const CTexturePtr& texture)
     635             :     {
     636          10 :         const VfsPath sourcePath = texture->m_Properties.m_Path;
     637             : 
     638          10 :         PROFILE2("convert texture");
     639           5 :         PROFILE2_ATTR("name: %ls", sourcePath.string().c_str());
     640             : 
     641           5 :         MD5 hash;
     642             :         u32 version;
     643           5 :         PrepareCacheKey(texture, hash, version);
     644          10 :         const VfsPath looseCachePath = m_CacheLoader.LooseCachePath(sourcePath, hash, version);
     645             : 
     646           5 :         CTextureConverter::Settings settings = GetConverterSettings(texture);
     647             : 
     648           5 :         m_TextureConverter.ConvertTexture(texture, sourcePath, looseCachePath, settings);
     649           5 :     }
     650             : 
     651           0 :     bool TextureExists(const VfsPath& path) const
     652             :     {
     653           0 :         return m_VFS->GetFileInfo(m_CacheLoader.ArchiveCachePath(path), 0) == INFO::OK ||
     654           0 :                m_VFS->GetFileInfo(path, 0) == INFO::OK;
     655             :     }
     656             : 
     657           0 :     bool GenerateCachedTexture(const VfsPath& sourcePath, VfsPath& archiveCachePath)
     658             :     {
     659           0 :         archiveCachePath = m_CacheLoader.ArchiveCachePath(sourcePath);
     660             : 
     661           0 :         CTextureProperties textureProps(sourcePath);
     662           0 :         CTexturePtr texture = CreateTexture(textureProps);
     663           0 :         CTextureConverter::Settings settings = GetConverterSettings(texture);
     664             : 
     665           0 :         if (!m_TextureConverter.ConvertTexture(texture, sourcePath, VfsPath("cache") / archiveCachePath, settings))
     666           0 :             return false;
     667             : 
     668             :         while (true)
     669             :         {
     670           0 :             CTexturePtr textureOut;
     671           0 :             VfsPath dest;
     672             :             bool ok;
     673           0 :             if (m_TextureConverter.Poll(textureOut, dest, ok))
     674           0 :                 return ok;
     675             : 
     676           0 :             std::this_thread::sleep_for(std::chrono::microseconds(1));
     677           0 :         }
     678             :     }
     679             : 
     680          11 :     bool MakeProgress()
     681             :     {
     682             :         // Process any completed conversion tasks
     683             :         {
     684          17 :             CTexturePtr texture;
     685          17 :             VfsPath dest;
     686             :             bool ok;
     687          11 :             if (m_TextureConverter.Poll(texture, dest, ok))
     688             :             {
     689           5 :                 if (ok)
     690             :                 {
     691           5 :                     LoadTexture(texture, dest);
     692             :                 }
     693             :                 else
     694             :                 {
     695           0 :                     LOGERROR("Texture failed to convert: \"%s\"", texture->m_Properties.m_Path.string8());
     696           0 :                     texture->ResetBackendTexture(
     697           0 :                         nullptr, m_ErrorTexture.GetTexture()->GetBackendTexture());
     698             :                 }
     699           5 :                 texture->m_State = CTexture::LOADED;
     700           5 :                 return true;
     701             :             }
     702             :         }
     703             : 
     704             :         // We'll only push new conversion requests if it's not already busy
     705           6 :         bool converterBusy = m_TextureConverter.IsBusy();
     706             : 
     707           6 :         if (!converterBusy)
     708             :         {
     709             :             // Look for all high-priority textures needing conversion.
     710             :             // (Iterating over all textures isn't optimally efficient, but it
     711             :             // doesn't seem to be a problem yet and it's simpler than maintaining
     712             :             // multiple queues.)
     713          11 :             for (TextureCache::iterator it = m_TextureCache.begin(); it != m_TextureCache.end(); ++it)
     714             :             {
     715          11 :                 if ((*it)->m_State == CTexture::HIGH_NEEDS_CONVERTING)
     716             :                 {
     717             :                     // Start converting this texture
     718           5 :                     (*it)->m_State = CTexture::HIGH_IS_CONVERTING;
     719           5 :                     ConvertTexture(*it);
     720           5 :                     return true;
     721             :                 }
     722             :             }
     723             :         }
     724             : 
     725             :         // Try loading prefetched textures from their cache
     726           2 :         for (TextureCache::iterator it = m_TextureCache.begin(); it != m_TextureCache.end(); ++it)
     727             :         {
     728           1 :             if ((*it)->m_State == CTexture::PREFETCH_NEEDS_LOADING)
     729             :             {
     730           0 :                 if (TryLoadingCached(*it))
     731             :                 {
     732           0 :                     (*it)->m_State = CTexture::LOADED;
     733             :                 }
     734             :                 else
     735             :                 {
     736           0 :                     (*it)->m_State = CTexture::PREFETCH_NEEDS_CONVERTING;
     737             :                 }
     738           0 :                 return true;
     739             :             }
     740             :         }
     741             : 
     742             :         // If we've got nothing better to do, then start converting prefetched textures.
     743           1 :         if (!converterBusy)
     744             :         {
     745           0 :             for (TextureCache::iterator it = m_TextureCache.begin(); it != m_TextureCache.end(); ++it)
     746             :             {
     747           0 :                 if ((*it)->m_State == CTexture::PREFETCH_NEEDS_CONVERTING)
     748             :                 {
     749           0 :                     (*it)->m_State = CTexture::PREFETCH_IS_CONVERTING;
     750           0 :                     ConvertTexture(*it);
     751           0 :                     return true;
     752             :                 }
     753             :             }
     754             :         }
     755             : 
     756           1 :         return false;
     757             :     }
     758             : 
     759           0 :     bool MakeUploadProgress(
     760             :         Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     761             :     {
     762           0 :         if (!m_PredefinedTexturesUploaded)
     763             :         {
     764           0 :             m_DefaultTexture.Upload(deviceCommandContext);
     765           0 :             m_ErrorTexture.Upload(deviceCommandContext);
     766           0 :             m_WhiteTexture.Upload(deviceCommandContext);
     767           0 :             m_TransparentTexture.Upload(deviceCommandContext);
     768           0 :             m_AlphaGradientTexture.Upload(deviceCommandContext);
     769           0 :             m_BlackTextureCube.Upload(deviceCommandContext);
     770           0 :             m_PredefinedTexturesUploaded = true;
     771           0 :             return true;
     772             :         }
     773           0 :         return false;
     774             :     }
     775             : 
     776             :     /**
     777             :      * Compute the conversion settings that apply to a given texture, by combining
     778             :      * the textures.xml files from its directory and all parent directories
     779             :      * (up to the VFS root).
     780             :      */
     781          16 :     CTextureConverter::Settings GetConverterSettings(const CTexturePtr& texture)
     782             :     {
     783          32 :         fs::wpath srcPath = texture->m_Properties.m_Path.string();
     784             : 
     785          32 :         std::vector<CTextureConverter::SettingsFile*> files;
     786          32 :         VfsPath p;
     787          80 :         for (fs::wpath::iterator it = srcPath.begin(); it != srcPath.end(); ++it)
     788             :         {
     789         128 :             VfsPath settingsPath = p / "textures.xml";
     790          64 :             m_HotloadFiles[settingsPath].insert(texture);
     791          64 :             CTextureConverter::SettingsFile* f = GetSettingsFile(settingsPath);
     792          64 :             if (f)
     793           0 :                 files.push_back(f);
     794          64 :             p = p / GetWstringFromWpath(*it);
     795             :         }
     796          32 :         return m_TextureConverter.ComputeSettings(GetWstringFromWpath(srcPath.leaf()), files);
     797             :     }
     798             : 
     799             :     /**
     800             :      * Return the (cached) settings file with the given filename,
     801             :      * or NULL if it doesn't exist.
     802             :      */
     803          64 :     CTextureConverter::SettingsFile* GetSettingsFile(const VfsPath& path)
     804             :     {
     805          64 :         SettingsFilesMap::iterator it = m_SettingsFiles.find(path);
     806          64 :         if (it != m_SettingsFiles.end())
     807          52 :             return it->second.get();
     808             : 
     809          12 :         if (m_VFS->GetFileInfo(path, NULL) >= 0)
     810             :         {
     811           0 :             std::shared_ptr<CTextureConverter::SettingsFile> settings(m_TextureConverter.LoadSettings(path));
     812           0 :             m_SettingsFiles.insert(std::make_pair(path, settings));
     813           0 :             return settings.get();
     814             :         }
     815             :         else
     816             :         {
     817          12 :             m_SettingsFiles.insert(std::make_pair(path, std::shared_ptr<CTextureConverter::SettingsFile>()));
     818          12 :             return NULL;
     819             :         }
     820             :     }
     821             : 
     822           0 :     static Status ReloadChangedFileCB(void* param, const VfsPath& path)
     823             :     {
     824           0 :         return static_cast<CTextureManagerImpl*>(param)->ReloadChangedFile(path);
     825             :     }
     826             : 
     827           0 :     Status ReloadChangedFile(const VfsPath& path)
     828             :     {
     829             :         // Uncache settings file, if this is one
     830           0 :         m_SettingsFiles.erase(path);
     831             : 
     832             :         // Find all textures using this file
     833           0 :         HotloadFilesMap::iterator files = m_HotloadFiles.find(path);
     834           0 :         if (files != m_HotloadFiles.end())
     835             :         {
     836             :             // Flag all textures using this file as needing reloading
     837           0 :             for (std::set<std::weak_ptr<CTexture>>::iterator it = files->second.begin(); it != files->second.end(); ++it)
     838             :             {
     839           0 :                 if (std::shared_ptr<CTexture> texture = it->lock())
     840             :                 {
     841           0 :                     texture->m_State = CTexture::UNLOADED;
     842           0 :                     texture->ResetBackendTexture(
     843           0 :                         nullptr, m_DefaultTexture.GetTexture()->GetBackendTexture());
     844           0 :                     texture->m_TextureData.reset();
     845             :                 }
     846             :             }
     847             :         }
     848             : 
     849           0 :         return INFO::OK;
     850             :     }
     851             : 
     852           0 :     void ReloadAllTextures()
     853             :     {
     854           0 :         for (const CTexturePtr& texture : m_TextureCache)
     855             :         {
     856           0 :             texture->m_State = CTexture::UNLOADED;
     857           0 :             texture->ResetBackendTexture(
     858           0 :                 nullptr, m_DefaultTexture.GetTexture()->GetBackendTexture());
     859           0 :             texture->m_TextureData.reset();
     860             :         }
     861           0 :     }
     862             : 
     863           0 :     size_t GetBytesUploaded() const
     864             :     {
     865           0 :         size_t size = 0;
     866           0 :         for (TextureCache::const_iterator it = m_TextureCache.begin(); it != m_TextureCache.end(); ++it)
     867           0 :             size += (*it)->GetUploadedSize();
     868           0 :         return size;
     869             :     }
     870             : 
     871           0 :     void OnQualityChanged()
     872             :     {
     873           0 :         ReloadAllTextures();
     874           0 :     }
     875             : 
     876             : private:
     877             :     PIVFS m_VFS;
     878             :     CCacheLoader m_CacheLoader;
     879             :     Renderer::Backend::IDevice* m_Device = nullptr;
     880             :     CTextureConverter m_TextureConverter;
     881             : 
     882             :     CSingleColorTexture m_DefaultTexture;
     883             :     CSingleColorTexture m_ErrorTexture;
     884             :     CSingleColorTexture m_WhiteTexture;
     885             :     CSingleColorTexture m_TransparentTexture;
     886             :     CGradientTexture m_AlphaGradientTexture;
     887             :     CSingleColorTextureCube m_BlackTextureCube;
     888             :     bool m_PredefinedTexturesUploaded = false;
     889             : 
     890             :     // Cache of all loaded textures
     891             :     using TextureCache =
     892             :         std::unordered_set<CTexturePtr, TPhash, TPequal_to>;
     893             :     TextureCache m_TextureCache;
     894             :     // TODO: we ought to expire unused textures from the cache eventually
     895             : 
     896             :     // Store the set of textures that need to be reloaded when the given file
     897             :     // (a source file or settings.xml) is modified
     898             :     using HotloadFilesMap =
     899             :         std::unordered_map<VfsPath, std::set<std::weak_ptr<CTexture>, std::owner_less<std::weak_ptr<CTexture>>>>;
     900             :     HotloadFilesMap m_HotloadFiles;
     901             : 
     902             :     // Cache for the conversion settings files
     903             :     using SettingsFilesMap =
     904             :         std::unordered_map<VfsPath, std::shared_ptr<CTextureConverter::SettingsFile>>;
     905             :     SettingsFilesMap m_SettingsFiles;
     906             : 
     907             :     bool m_HasS3TC = false;
     908             : };
     909             : 
     910          66 : CTexture::CTexture(
     911             :     std::unique_ptr<Renderer::Backend::ITexture> texture,
     912             :     Renderer::Backend::ITexture* fallback,
     913          66 :     const CTextureProperties& props, CTextureManagerImpl* textureManager) :
     914          66 :     m_BackendTexture(std::move(texture)), m_FallbackBackendTexture(fallback),
     915             :     m_BaseColor(0), m_State(UNLOADED), m_Properties(props),
     916         132 :     m_TextureManager(textureManager)
     917             : {
     918          66 : }
     919             : 
     920             : CTexture::~CTexture() = default;
     921             : 
     922           0 : void CTexture::UploadBackendTextureIfNeeded(
     923             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     924             : {
     925           0 :     if (IsUploaded())
     926           0 :         return;
     927             : 
     928           0 :     if (!IsLoaded())
     929           0 :         TryLoad();
     930             : 
     931           0 :     if (!IsLoaded())
     932           0 :         return;
     933           0 :     else if (!m_TextureData || !m_BackendTexture)
     934             :     {
     935           0 :         ResetBackendTexture(nullptr, m_TextureManager->GetErrorTexture()->GetBackendTexture());
     936           0 :         m_State = UPLOADED;
     937           0 :         return;
     938             :     }
     939             : 
     940           0 :     m_UploadedSize = 0;
     941           0 :     for (uint32_t textureDataLevel = m_BaseLevelOffset, level = 0; textureDataLevel < m_TextureData->GetMIPLevels().size(); ++textureDataLevel)
     942             :     {
     943           0 :         const Tex::MIPLevel& levelData = m_TextureData->GetMIPLevels()[textureDataLevel];
     944           0 :         deviceCommandContext->UploadTexture(m_BackendTexture.get(), m_BackendTexture->GetFormat(),
     945           0 :             levelData.data, levelData.dataSize, level++);
     946           0 :         m_UploadedSize += levelData.dataSize;
     947             :     }
     948           0 :     m_TextureData.reset();
     949             : 
     950           0 :     m_State = UPLOADED;
     951             : }
     952             : 
     953          12 : Renderer::Backend::ITexture* CTexture::GetBackendTexture()
     954             : {
     955          12 :     return m_BackendTexture && IsUploaded() ? m_BackendTexture.get() : m_FallbackBackendTexture;
     956             : }
     957             : 
     958           0 : const Renderer::Backend::ITexture* CTexture::GetBackendTexture() const
     959             : {
     960           0 :     return m_BackendTexture && IsUploaded() ? m_BackendTexture.get() : m_FallbackBackendTexture;
     961             : }
     962             : 
     963           6 : bool CTexture::TryLoad()
     964             : {
     965             :     // If we haven't started loading, then try loading, and if that fails then request conversion.
     966             :     // If we have already tried prefetch loading, and it failed, bump the conversion request to HIGH priority.
     967           6 :     if (m_State == UNLOADED || m_State == PREFETCH_NEEDS_LOADING || m_State == PREFETCH_NEEDS_CONVERTING)
     968             :     {
     969          12 :         if (std::shared_ptr<CTexture> self = m_Self.lock())
     970             :         {
     971           6 :             if (m_State != PREFETCH_NEEDS_CONVERTING && m_TextureManager->TryLoadingCached(self))
     972           1 :                 m_State = LOADED;
     973             :             else
     974           5 :                 m_State = HIGH_NEEDS_CONVERTING;
     975             :         }
     976             :     }
     977             : 
     978           6 :     return IsLoaded() || IsUploaded();
     979             : }
     980             : 
     981           0 : void CTexture::Prefetch()
     982             : {
     983           0 :     if (m_State == UNLOADED)
     984             :     {
     985           0 :         if (std::shared_ptr<CTexture> self = m_Self.lock())
     986             :         {
     987           0 :             m_State = PREFETCH_NEEDS_LOADING;
     988             :         }
     989             :     }
     990           0 : }
     991             : 
     992           0 : void CTexture::ResetBackendTexture(
     993             :     std::unique_ptr<Renderer::Backend::ITexture> backendTexture,
     994             :     Renderer::Backend::ITexture* fallbackBackendTexture)
     995             : {
     996           0 :     m_BackendTexture = std::move(backendTexture);
     997           0 :     m_FallbackBackendTexture = fallbackBackendTexture;
     998           0 : }
     999             : 
    1000           0 : size_t CTexture::GetWidth() const
    1001             : {
    1002           0 :     return GetBackendTexture()->GetWidth();
    1003             : }
    1004             : 
    1005           0 : size_t CTexture::GetHeight() const
    1006             : {
    1007           0 :     return GetBackendTexture()->GetHeight();
    1008             : }
    1009             : 
    1010           0 : bool CTexture::HasAlpha() const
    1011             : {
    1012           0 :     const Renderer::Backend::Format format = GetBackendTexture()->GetFormat();
    1013             :     return
    1014           0 :         format == Renderer::Backend::Format::A8_UNORM ||
    1015           0 :         format == Renderer::Backend::Format::R8G8B8A8_UNORM ||
    1016           0 :         format == Renderer::Backend::Format::BC1_RGBA_UNORM ||
    1017           0 :         format == Renderer::Backend::Format::BC2_UNORM ||
    1018           0 :         format == Renderer::Backend::Format::BC3_UNORM;
    1019             : }
    1020             : 
    1021           0 : u32 CTexture::GetBaseColor() const
    1022             : {
    1023           0 :     return m_BaseColor;
    1024             : }
    1025             : 
    1026           0 : size_t CTexture::GetUploadedSize() const
    1027             : {
    1028           0 :     return m_UploadedSize;
    1029             : }
    1030             : 
    1031             : // CTextureManager: forward all calls to impl:
    1032             : 
    1033           9 : CTextureManager::CTextureManager(PIVFS vfs, bool highQuality, Renderer::Backend::IDevice* device) :
    1034           9 :     m(new CTextureManagerImpl(vfs, highQuality, device))
    1035             : {
    1036           9 : }
    1037             : 
    1038          18 : CTextureManager::~CTextureManager()
    1039             : {
    1040           9 :     delete m;
    1041           9 : }
    1042             : 
    1043          12 : CTexturePtr CTextureManager::CreateTexture(const CTextureProperties& props)
    1044             : {
    1045          12 :     return m->CreateTexture(props);
    1046             : }
    1047             : 
    1048           0 : CTexturePtr CTextureManager::WrapBackendTexture(
    1049             :     std::unique_ptr<Renderer::Backend::ITexture> backendTexture)
    1050             : {
    1051           0 :     return m->WrapBackendTexture(std::move(backendTexture));
    1052             : }
    1053             : 
    1054           0 : bool CTextureManager::TextureExists(const VfsPath& path) const
    1055             : {
    1056           0 :     return m->TextureExists(path);
    1057             : }
    1058             : 
    1059           0 : const CTexturePtr& CTextureManager::GetErrorTexture()
    1060             : {
    1061           0 :     return m->GetErrorTexture();
    1062             : }
    1063             : 
    1064           0 : const CTexturePtr& CTextureManager::GetWhiteTexture()
    1065             : {
    1066           0 :     return m->GetWhiteTexture();
    1067             : }
    1068             : 
    1069           0 : const CTexturePtr& CTextureManager::GetTransparentTexture()
    1070             : {
    1071           0 :     return m->GetTransparentTexture();
    1072             : }
    1073             : 
    1074           0 : const CTexturePtr& CTextureManager::GetAlphaGradientTexture()
    1075             : {
    1076           0 :     return m->GetAlphaGradientTexture();
    1077             : }
    1078             : 
    1079           0 : const CTexturePtr& CTextureManager::GetBlackTextureCube()
    1080             : {
    1081           0 :     return m->GetBlackTextureCube();
    1082             : }
    1083             : 
    1084          11 : bool CTextureManager::MakeProgress()
    1085             : {
    1086          11 :     return m->MakeProgress();
    1087             : }
    1088             : 
    1089           0 : bool CTextureManager::MakeUploadProgress(
    1090             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
    1091             : {
    1092           0 :     return m->MakeUploadProgress(deviceCommandContext);
    1093             : }
    1094             : 
    1095           0 : bool CTextureManager::GenerateCachedTexture(const VfsPath& path, VfsPath& outputPath)
    1096             : {
    1097           0 :     return m->GenerateCachedTexture(path, outputPath);
    1098             : }
    1099             : 
    1100           0 : size_t CTextureManager::GetBytesUploaded() const
    1101             : {
    1102           0 :     return m->GetBytesUploaded();
    1103             : }
    1104             : 
    1105           0 : void CTextureManager::OnQualityChanged()
    1106             : {
    1107           0 :     m->OnQualityChanged();
    1108           3 : }

Generated by: LCOV version 1.13