LCOV - code coverage report
Current view: top level - source/graphics - TextureManager.h (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 11 19 57.9 %
Date: 2023-01-19 00:18:29 Functions: 8 11 72.7 %

          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             : #ifndef INCLUDED_TEXTUREMANAGER
      19             : #define INCLUDED_TEXTUREMANAGER
      20             : 
      21             : #include "graphics/Texture.h"
      22             : #include "lib/file/vfs/vfs.h"
      23             : #include "lib/tex/tex.h"
      24             : #include "renderer/backend/IDevice.h"
      25             : #include "renderer/backend/IDeviceCommandContext.h"
      26             : #include "renderer/backend/ITexture.h"
      27             : 
      28             : #include <memory>
      29             : 
      30             : class CTextureProperties;
      31             : class CTextureManagerImpl;
      32             : 
      33             : /**
      34             :  * Texture manager with asynchronous loading and automatic DDS conversion/compression.
      35             :  *
      36             :  * Input textures can be any format. They will be converted to DDS using settings defined
      37             :  * in files named "texture.xml", in the same directory as the texture and in its parent
      38             :  * directories. See CTextureConverter for the XML syntax. The DDS file will be cached
      39             :  * for faster loading in the future.
      40             :  *
      41             :  * Typically the graphics code will initialise many textures at the start of the game,
      42             :  * mostly for off-screen objects, by calling CreateTexture().
      43             :  * Loading texture data may be very slow (especially if it needs to be converted
      44             :  * to DDS), and we don't want the game to become unresponsive.
      45             :  * CreateTexture therefore returns an object immediately, without loading the
      46             :  * texture. If the object is never used then the data will never be loaded.
      47             :  *
      48             :  * Typically, the renderer will call CTexture::Bind() when it wants to use the
      49             :  * texture. This will trigger the loading of the texture data. If it can be loaded
      50             :  * quickly (i.e. there is already a cached DDS version), then it will be loaded before
      51             :  * the function returns, and the texture can be rendered as normal.
      52             :  *
      53             :  * If loading will take a long time, then Bind() binds a default placeholder texture
      54             :  * and starts loading the texture in the background. It will use the correct texture
      55             :  * when the renderer next calls Bind() after the load has finished.
      56             :  *
      57             :  * It is also possible to prefetch textures which are not being rendered yet, but
      58             :  * are expected to be rendered soon (e.g. for off-screen terrain tiles).
      59             :  * These will be loaded in the background, when there are no higher-priority textures
      60             :  * to load.
      61             :  *
      62             :  * The same texture file can be safely loaded multiple times with different backend parameters
      63             :  * (but this should be avoided whenever possible, as it wastes VRAM).
      64             :  *
      65             :  * For release packages, DDS files can be precached by appending ".dds" to their name,
      66             :  * which will be used instead of doing runtime conversion. This means most players should
      67             :  * never experience the slow asynchronous conversion behaviour.
      68             :  * These cache files will typically be packed into an archive for faster loading;
      69             :  * if no archive cache is available then the source file will be converted and stored
      70             :  * as a loose cache file on the user's disk.
      71             :  */
      72             : class CTextureManager
      73             : {
      74             :     NONCOPYABLE(CTextureManager);
      75             : 
      76             : public:
      77             :     /**
      78             :      * Construct texture manager. vfs must be the VFS instance used for all textures
      79             :      * loaded from this object.
      80             :      * highQuality is slower and intended for batch-conversion modes.
      81             :      */
      82             :     CTextureManager(PIVFS vfs, bool highQuality, Renderer::Backend::IDevice* device);
      83             : 
      84             :     ~CTextureManager();
      85             : 
      86             :     /**
      87             :      * Create a texture with the given properties.
      88             :      * The texture data will not be loaded immediately.
      89             :      */
      90             :     CTexturePtr CreateTexture(const CTextureProperties& props);
      91             : 
      92             :     /**
      93             :      * Wraps a backend texture.
      94             :      */
      95             :     CTexturePtr WrapBackendTexture(
      96             :         std::unique_ptr<Renderer::Backend::ITexture> backendTexture);
      97             : 
      98             :     /**
      99             :      * Returns a magenta texture. Use this for highlighting errors
     100             :      * (e.g. missing terrain textures).
     101             :      */
     102             :     const CTexturePtr& GetErrorTexture();
     103             : 
     104             :     /**
     105             :      * Returns a single color RGBA texture with CColor(1.0f, 1.0f, 1.0f, 1.0f).
     106             :      */
     107             :     const CTexturePtr& GetWhiteTexture();
     108             : 
     109             :     /**
     110             :      * Returns a single color RGBA texture with CColor(0.0f, 0.0f, 0.0f, 0.0f).
     111             :      */
     112             :     const CTexturePtr& GetTransparentTexture();
     113             : 
     114             :     /**
     115             :      * Returns a white RGBA texture with alpha gradient.
     116             :      */
     117             :     const CTexturePtr& GetAlphaGradientTexture();
     118             : 
     119             :     /**
     120             :      * Returns a single color RGBA texture cube with CColor(0.0f, 0.0f, 0.0f, 1.0f).
     121             :      */
     122             :     const CTexturePtr& GetBlackTextureCube();
     123             : 
     124             :     /**
     125             :      * Work on asynchronous texture loading operations, if any.
     126             :      * Returns true if it did any work.
     127             :      * The caller should typically loop this per frame until it returns
     128             :      * false or exceeds the allocated time for this frame.
     129             :      */
     130             :     bool MakeProgress();
     131             : 
     132             :     /**
     133             :      * Work on asynchronous texture uploading operations, if any.
     134             :      * Returns true if it did any work. Mostly the same as MakeProgress.
     135             :      */
     136             :     bool MakeUploadProgress(Renderer::Backend::IDeviceCommandContext* deviceCommandContext);
     137             : 
     138             :     /**
     139             :      * Synchronously converts and compresses and saves the texture,
     140             :      * and returns the output path (minus a "cache/" prefix). This
     141             :      * is intended for pre-caching textures in release archives.
     142             :      * @return true on success
     143             :      */
     144             :     bool GenerateCachedTexture(const VfsPath& path, VfsPath& outputPath);
     145             : 
     146             :     /**
     147             :      * Returns true if the given texture exists.
     148             :      * This tests both for the original and converted filename.
     149             :      */
     150             :     bool TextureExists(const VfsPath& path) const;
     151             : 
     152             :     /**
     153             :      * Returns total number of bytes uploaded for all current texture.
     154             :      */
     155             :     size_t GetBytesUploaded() const;
     156             : 
     157             :     /**
     158             :      * Should be called on any quality or anisotropic change.
     159             :      */
     160             :     void OnQualityChanged();
     161             : 
     162             : private:
     163             :     CTextureManagerImpl* m;
     164             : };
     165             : 
     166             : /**
     167             :  * Represents the filename and GL parameters of a texture,
     168             :  * for passing to CTextureManager::CreateTexture.
     169             :  */
     170         198 : class CTextureProperties
     171             : {
     172             :     friend class CTextureManagerImpl;
     173             :     friend struct TextureCacheCmp;
     174             :     friend struct TPequal_to;
     175             :     friend struct TPhash;
     176             : 
     177             : public:
     178             :     /**
     179             :      * Use the given texture name, and default GL parameters.
     180             :      */
     181          61 :     explicit CTextureProperties(const VfsPath& path)
     182          61 :         : m_Path(path)
     183             :     {
     184          61 :     }
     185             : 
     186           5 :     CTextureProperties(
     187             :         const VfsPath& path, const Renderer::Backend::Format formatOverride)
     188           5 :         : m_Path(path), m_FormatOverride(formatOverride)
     189             :     {
     190           5 :     }
     191             : 
     192             :     /**
     193             :      * Set sampler address mode.
     194             :      */
     195           0 :     void SetAddressMode(const Renderer::Backend::Sampler::AddressMode addressMode)
     196             :     {
     197           0 :         m_AddressModeU = m_AddressModeV = addressMode;
     198           0 :     }
     199             : 
     200             :     /**
     201             :      * Set sampler address mode separately for different coordinates.
     202             :      */
     203           0 :     void SetAddressMode(
     204             :         const Renderer::Backend::Sampler::AddressMode addressModeU,
     205             :         const Renderer::Backend::Sampler::AddressMode addressModeV)
     206             :     {
     207           0 :         m_AddressModeU = addressModeU;
     208           0 :         m_AddressModeV = addressModeV;
     209           0 :     }
     210             : 
     211             :     /**
     212             :      * The value of max anisotropy is set by options. Though it might make sense
     213             :      * to add an override.
     214             :      */
     215           0 :     void SetAnisotropicFilter(const bool enabled) { m_AnisotropicFilterEnabled = enabled; }
     216             : 
     217             :     // TODO: rather than this static definition of texture properties
     218             :     // (especially anisotropy), maybe we want something that can be more
     219             :     // easily tweaked in an Options menu? e.g. the caller just specifies
     220             :     // "terrain texture mode" and we combine it with the user's options.
     221             :     // That'd let us dynamically change texture properties easily.
     222             :     //
     223             :     // enum EQualityMode
     224             :     // {
     225             :     //   NONE,
     226             :     //   TERRAIN,
     227             :     //   MODEL,
     228             :     //   GUI
     229             :     // }
     230             :     // void SetQuality(EQualityMode mode, float anisotropy, float lodbias, int reducemipmaps, ...);
     231             :     //
     232             :     // or something a bit like that.
     233             : 
     234           5 :     void SetIgnoreQuality(bool ignore) { m_IgnoreQuality = ignore; }
     235             : 
     236             : private:
     237             :     // Must update TPhash, TPequal_to when changing these fields
     238             :     VfsPath m_Path;
     239             : 
     240             :     Renderer::Backend::Sampler::AddressMode m_AddressModeU =
     241             :         Renderer::Backend::Sampler::AddressMode::REPEAT;
     242             :     Renderer::Backend::Sampler::AddressMode m_AddressModeV =
     243             :         Renderer::Backend::Sampler::AddressMode::REPEAT;
     244             :     bool m_AnisotropicFilterEnabled = false;
     245             :     Renderer::Backend::Format m_FormatOverride =
     246             :         Renderer::Backend::Format::UNDEFINED;
     247             :     bool m_IgnoreQuality = false;
     248             : };
     249             : 
     250             : /**
     251             :  * Represents a texture object.
     252             :  * The texture data may or may not have been loaded yet.
     253             :  * Before it has been loaded, all operations will act on a default
     254             :  * 1x1-pixel grey texture instead.
     255             :  */
     256          66 : class CTexture
     257             : {
     258             :     NONCOPYABLE(CTexture);
     259             : public:
     260             :     ~CTexture();
     261             : 
     262             :     /**
     263             :      * Returns the width (in pixels) of the current texture.
     264             :      */
     265             :     size_t GetWidth() const;
     266             : 
     267             :     /**
     268             :      * Returns the height (in pixels) of the current texture.
     269             :      */
     270             :     size_t GetHeight() const;
     271             : 
     272             :     /**
     273             :      * Returns whether the current texture has an alpha channel.
     274             :      */
     275             :     bool HasAlpha() const;
     276             : 
     277             :     /**
     278             :      * Returns the ARGB value of the lowest mipmap level (i.e. the
     279             :      * average of the whole texture).
     280             :      * Returns 0 if the texture has no mipmaps.
     281             :      */
     282             :     u32 GetBaseColor() const;
     283             : 
     284             :     /**
     285             :      * Returns total number of bytes uploaded for this texture.
     286             :      */
     287             :     size_t GetUploadedSize() const;
     288             : 
     289             :     /**
     290             :      * Uploads a texture data to a backend texture if successfully loaded.
     291             :      * If the texture data hasn't been loaded yet, this may wait a short while to
     292             :      * load it. If loading takes too long then it will return sooner and the data will
     293             :      * be loaded in a background thread, so this does not guarantee the texture really
     294             :      * will be uploaded.
     295             :      */
     296             :     void UploadBackendTextureIfNeeded(
     297             :         Renderer::Backend::IDeviceCommandContext* deviceCommandContext);
     298             : 
     299             :     /**
     300             :      * Returns a backend texture if successfully uploaded, else fallback.
     301             :      */
     302             :     Renderer::Backend::ITexture* GetBackendTexture();
     303             :     const Renderer::Backend::ITexture* GetBackendTexture() const;
     304             : 
     305             :     /**
     306             :      * Attempt to load the texture data quickly, as with
     307             :      * GetUploadedBackendTextureIfNeeded(). Returns whether the texture data is
     308             :      * currently loaded (but not uploaded).
     309             :      */
     310             :     bool TryLoad();
     311             : 
     312             :     /**
     313             :      * Returns whether the texture data is currently loaded.
     314             :      */
     315          15 :     bool IsLoaded() const { return m_State == LOADED; }
     316             : 
     317             :     /**
     318             :      * Returns whether the texture data is currently uploaded.
     319             :      */
     320          17 :     bool IsUploaded() const { return m_State == UPLOADED; }
     321             : 
     322             :     /**
     323             :      * Activate the prefetching optimisation for this texture.
     324             :      * Use this if it is likely the texture will be needed in the near future.
     325             :      * It will be loaded in the background so that it is likely to be ready when
     326             :      * it is used by Bind().
     327             :      */
     328             :     void Prefetch();
     329             : 
     330             : private:
     331             :     friend class CTextureManagerImpl;
     332             :     friend class CPredefinedTexture;
     333             :     friend struct TextureCacheCmp;
     334             :     friend struct TPequal_to;
     335             :     friend struct TPhash;
     336             : 
     337             :     // Only the texture manager can create these
     338             :     explicit CTexture(
     339             :         std::unique_ptr<Renderer::Backend::ITexture> texture,
     340             :         Renderer::Backend::ITexture* fallback,
     341             :         const CTextureProperties& props, CTextureManagerImpl* textureManager);
     342             : 
     343             :     void ResetBackendTexture(
     344             :         std::unique_ptr<Renderer::Backend::ITexture> backendTexture,
     345             :         Renderer::Backend::ITexture* fallbackBackendTexture);
     346             : 
     347             :     const CTextureProperties m_Properties;
     348             : 
     349             :     std::unique_ptr<Renderer::Backend::ITexture> m_BackendTexture;
     350             :     // It's possible to m_FallbackBackendTexture references m_BackendTexture.
     351             :     Renderer::Backend::ITexture* m_FallbackBackendTexture = nullptr;
     352             :     u32 m_BaseColor;
     353             :     std::unique_ptr<Tex> m_TextureData;
     354             :     size_t m_UploadedSize = 0;
     355             :     uint32_t m_BaseLevelOffset = 0;
     356             : 
     357             :     enum
     358             :     {
     359             :         UNLOADED, // loading has not started
     360             :         PREFETCH_NEEDS_LOADING, // was prefetched; currently waiting to try loading from cache
     361             :         PREFETCH_NEEDS_CONVERTING, // was prefetched; currently waiting to be sent to the texture converter
     362             :         PREFETCH_IS_CONVERTING, // was prefetched; currently being processed by the texture converter
     363             :         HIGH_NEEDS_CONVERTING, // high-priority; currently waiting to be sent to the texture converter
     364             :         HIGH_IS_CONVERTING, // high-priority; currently being processed by the texture converter
     365             :         LOADED, // loading texture data has completed (successfully or not)
     366             :         UPLOADED // uploading to backend has completed (successfully or not)
     367             :     } m_State;
     368             : 
     369             :     CTextureManagerImpl* m_TextureManager;
     370             : 
     371             :     // Self-reference to let us recover the CTexturePtr for this object.
     372             :     // (weak pointer to avoid cycles)
     373             :     std::weak_ptr<CTexture> m_Self;
     374             : };
     375             : 
     376             : #endif // INCLUDED_TEXTUREMANAGER

Generated by: LCOV version 1.13