1 /* Copyright (C) 2018 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 "Texture.h" 22 23 #include <memory> 24 25 #include "lib/ogl.h" 26 #include "lib/file/vfs/vfs.h" 27 #include "lib/res/handle.h" 28 29 class CTextureProperties; 30 class CTextureManagerImpl; 31 32 /** 33 * Texture manager with asynchronous loading and automatic DDS conversion/compression. 34 * 35 * Input textures can be any format. They will be converted to DDS using settings defined 36 * in files named "texture.xml", in the same directory as the texture and in its parent 37 * directories. See CTextureConverter for the XML syntax. The DDS file will be cached 38 * for faster loading in the future. 39 * 40 * Typically the graphics code will initialise many textures at the start of the game, 41 * mostly for off-screen objects, by calling CreateTexture(). 42 * Loading texture data may be very slow (especially if it needs to be converted 43 * to DDS), and we don't want the game to become unresponsive. 44 * CreateTexture therefore returns an object immediately, without loading the 45 * texture. If the object is never used then the data will never be loaded. 46 * 47 * Typically, the renderer will call CTexture::Bind() when it wants to use the 48 * texture. This will trigger the loading of the texture data. If it can be loaded 49 * quickly (i.e. there is already a cached DDS version), then it will be loaded before 50 * the function returns, and the texture can be rendered as normal. 51 * 52 * If loading will take a long time, then Bind() binds a default placeholder texture 53 * and starts loading the texture in the background. It will use the correct texture 54 * when the renderer next calls Bind() after the load has finished. 55 * 56 * It is also possible to prefetch textures which are not being rendered yet, but 57 * are expected to be rendered soon (e.g. for off-screen terrain tiles). 58 * These will be loaded in the background, when there are no higher-priority textures 59 * to load. 60 * 61 * The same texture file can be safely loaded multiple times with different GL parameters 62 * (but this should be avoided whenever possible, as it wastes VRAM). 63 * 64 * For release packages, DDS files can be precached by appending ".dds" to their name, 65 * which will be used instead of doing runtime conversion. This means most players should 66 * never experience the slow asynchronous conversion behaviour. 67 * These cache files will typically be packed into an archive for faster loading; 68 * if no archive cache is available then the source file will be converted and stored 69 * as a loose cache file on the user's disk. 70 */ 71 class CTextureManager 72 { 73 NONCOPYABLE(CTextureManager); 74 75 public: 76 /** 77 * Construct texture manager. vfs must be the VFS instance used for all textures 78 * loaded from this object. 79 * highQuality is slower and intended for batch-conversion modes. 80 * disableGL is intended for tests, and will disable all GL uploads. 81 */ 82 CTextureManager(PIVFS vfs, bool highQuality, bool disableGL); 83 84 ~CTextureManager(); 85 86 /** 87 * Create a texture with the given GL properties. 88 * The texture data will not be loaded immediately. 89 */ 90 CTexturePtr CreateTexture(const CTextureProperties& props); 91 92 /** 93 * Returns a magenta texture. Use this for highlighting errors 94 * (e.g. missing terrain textures). 95 */ 96 CTexturePtr GetErrorTexture(); 97 98 /** 99 * Work on asynchronous texture loading operations, if any. 100 * Returns true if it did any work. 101 * The caller should typically loop this per frame until it returns 102 * false or exceeds the allocated time for this frame. 103 */ 104 bool MakeProgress(); 105 106 /** 107 * Synchronously converts and compresses and saves the texture, 108 * and returns the output path (minus a "cache/" prefix). This 109 * is intended for pre-caching textures in release archives. 110 * @return true on success 111 */ 112 bool GenerateCachedTexture(const VfsPath& path, VfsPath& outputPath); 113 114 /** 115 * Returns true if the given texture exists. 116 * This tests both for the original and converted filename. 117 */ 118 bool TextureExists(const VfsPath& path) const; 119 120 /** 121 * Returns total number of bytes uploaded for all current texture. 122 */ 123 size_t GetBytesUploaded() const; 124 125 private: 126 CTextureManagerImpl* m; 127 }; 128 129 /** 130 * Represents the filename and GL parameters of a texture, 131 * for passing to CTextureManager::CreateTexture. 132 */ 133 class CTextureProperties 134 { 135 friend class CTextureManagerImpl; 136 friend struct TextureCacheCmp; 137 friend struct TPequal_to; 138 friend struct TPhash; 139 140 public: 141 /** 142 * Use the given texture name, and default GL parameters. 143 */ CTextureProperties(const VfsPath & path)144 explicit CTextureProperties(const VfsPath& path) : 145 m_Path(path), m_Filter(GL_LINEAR_MIPMAP_LINEAR), 146 m_WrapS(GL_REPEAT), m_WrapT(GL_REPEAT), m_Aniso(1.0f), m_Format(0) 147 { 148 } 149 150 /** 151 * Set min/mag filter mode (typically GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST, etc). 152 */ SetFilter(GLint filter)153 void SetFilter(GLint filter) { m_Filter = filter; } 154 155 /** 156 * Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc). 157 */ SetWrap(GLint wrap)158 void SetWrap(GLint wrap) { m_WrapS = wrap; m_WrapT = wrap; } 159 160 /** 161 * Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc), 162 * separately for S and T. 163 */ SetWrap(GLint wrap_s,GLint wrap_t)164 void SetWrap(GLint wrap_s, GLint wrap_t) { m_WrapS = wrap_s; m_WrapT = wrap_t; } 165 166 /** 167 * Set maximum anisotropy value. Must be >= 1.0. Should be a power of 2. 168 */ SetMaxAnisotropy(float aniso)169 void SetMaxAnisotropy(float aniso) { m_Aniso = aniso; } 170 171 /** 172 * Set GL texture upload format, to override the default. 173 * Typically GL_ALPHA or GL_LUMINANCE for 8-bit textures. 174 */ SetFormatOverride(GLenum format)175 void SetFormatOverride(GLenum format) { m_Format = format; } 176 177 // TODO: rather than this static definition of texture properties 178 // (especially anisotropy), maybe we want something that can be more 179 // easily tweaked in an Options menu? e.g. the caller just specifies 180 // "terrain texture mode" and we combine it with the user's options. 181 // That'd let us dynamically change texture properties easily. 182 // 183 // enum EQualityMode 184 // { 185 // NONE, 186 // TERRAIN, 187 // MODEL, 188 // GUI 189 // } 190 // void SetQuality(EQualityMode mode, float anisotropy, float lodbias, int reducemipmaps, ...); 191 // 192 // or something a bit like that. 193 194 private: 195 // Must update TPhash, TPequal_to when changing these fields 196 VfsPath m_Path; 197 GLint m_Filter; 198 GLint m_WrapS; 199 GLint m_WrapT; 200 float m_Aniso; 201 GLenum m_Format; 202 }; 203 204 /** 205 * Represents a texture object. 206 * The texture data may or may not have been loaded yet. 207 * Before it has been loaded, all operations will act on a default 208 * 1x1-pixel grey texture instead. 209 */ 210 class CTexture 211 { 212 friend class CTextureManagerImpl; 213 friend struct TextureCacheCmp; 214 friend struct TPequal_to; 215 friend struct TPhash; 216 217 // Only the texture manager can create these 218 explicit CTexture(Handle handle, const CTextureProperties& props, CTextureManagerImpl* textureManager); 219 220 NONCOPYABLE(CTexture); 221 222 public: 223 224 ~CTexture(); 225 226 /** 227 * Returns the width (in pixels) of the current texture. 228 */ 229 size_t GetWidth() const; 230 231 /** 232 * Returns the height (in pixels) of the current texture. 233 */ 234 size_t GetHeight() const; 235 236 /** 237 * Returns whether the current texture has an alpha channel. 238 */ 239 bool HasAlpha() const; 240 241 /** 242 * Returns the ARGB value of the lowest mipmap level (i.e. the 243 * average of the whole texture). 244 * Returns 0 if the texture has no mipmaps. 245 */ 246 u32 GetBaseColor() const; 247 248 /** 249 * Returns total number of bytes uploaded for this texture. 250 */ 251 size_t GetUploadedSize() const; 252 253 /** 254 * Bind the texture to the given GL texture unit. 255 * If the texture data hasn't been loaded yet, this may wait a short while to 256 * load it. If loading takes too long then it will return sooner and the data will 257 * be loaded in a background thread, so this does not guarantee the texture really 258 * will be loaded. 259 */ 260 void Bind(size_t unit = 0); 261 262 /** 263 * Returns a ogl_tex handle, for later binding. See comments from Bind(). 264 */ 265 Handle GetHandle(); 266 267 /** 268 * Attempt to load the texture data quickly, as with Bind(). 269 * Returns whether the texture data is currently loaded. 270 */ 271 bool TryLoad(); 272 273 /** 274 * Returns whether the texture data is currently loaded. 275 */ 276 bool IsLoaded(); 277 278 /** 279 * Activate the prefetching optimisation for this texture. 280 * Use this if it is likely the texture will be needed in the near future. 281 * It will be loaded in the background so that it is likely to be ready when 282 * it is used by Bind(). 283 */ 284 void Prefetch(); 285 286 private: 287 /** 288 * Replace the Handle stored by this object. 289 * If takeOwnership is true, it will not increment the Handle's reference count. 290 */ 291 void SetHandle(Handle handle, bool takeOwnership = false); 292 293 const CTextureProperties m_Properties; 294 295 Handle m_Handle; 296 u32 m_BaseColor; 297 298 enum { 299 UNLOADED, // loading has not started 300 PREFETCH_NEEDS_LOADING, // was prefetched; currently waiting to try loading from cache 301 PREFETCH_NEEDS_CONVERTING, // was prefetched; currently waiting to be sent to the texture converter 302 PREFETCH_IS_CONVERTING, // was prefetched; currently being processed by the texture converter 303 HIGH_NEEDS_CONVERTING, // high-priority; currently waiting to be sent to the texture converter 304 HIGH_IS_CONVERTING, // high-priority; currently being processed by the texture converter 305 LOADED // loading has completed (successfully or not) 306 } m_State; 307 308 CTextureManagerImpl* m_TextureManager; 309 310 // Self-reference to let us recover the CTexturePtr for this object. 311 // (weak pointer to avoid cycles) 312 std::weak_ptr<CTexture> m_Self; 313 }; 314 315 std::size_t hash_value(const CTexturePtr& v); 316 std::size_t hash_value(const CTextureProperties& v); 317 318 #endif // INCLUDED_TEXTUREMANAGER 319