1 /* Copyright (C) 2023 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #ifndef INCLUDED_TEXTUREMANAGER
19 #define INCLUDED_TEXTUREMANAGER
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"
30 class CTextureProperties
;
31 class CTextureManagerImpl
;
34 * Texture manager with asynchronous loading and automatic DDS conversion/compression.
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.
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.
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.
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.
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
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).
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.
74 NONCOPYABLE(CTextureManager
);
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.
82 CTextureManager(PIVFS vfs
, bool highQuality
, Renderer::Backend::IDevice
* device
);
87 * Create a texture with the given properties.
88 * The texture data will not be loaded immediately.
90 CTexturePtr
CreateTexture(const CTextureProperties
& props
);
93 * Wraps a backend texture.
95 CTexturePtr
WrapBackendTexture(
96 std::unique_ptr
<Renderer::Backend::ITexture
> backendTexture
);
99 * Returns a magenta texture. Use this for highlighting errors
100 * (e.g. missing terrain textures).
102 const CTexturePtr
& GetErrorTexture();
105 * Returns a single color RGBA texture with CColor(1.0f, 1.0f, 1.0f, 1.0f).
107 const CTexturePtr
& GetWhiteTexture();
110 * Returns a single color RGBA texture with CColor(0.0f, 0.0f, 0.0f, 0.0f).
112 const CTexturePtr
& GetTransparentTexture();
115 * Returns a white RGBA texture with alpha gradient.
117 const CTexturePtr
& GetAlphaGradientTexture();
120 * Returns a single color RGBA texture cube with CColor(0.0f, 0.0f, 0.0f, 1.0f).
122 const CTexturePtr
& GetBlackTextureCube();
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.
133 * Work on asynchronous texture uploading operations, if any.
134 * Returns true if it did any work. Mostly the same as MakeProgress.
136 bool MakeUploadProgress(Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
);
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
144 bool GenerateCachedTexture(const VfsPath
& path
, VfsPath
& outputPath
);
147 * @return a cached version of the path
149 VfsPath
GetCachedPath(const VfsPath
& path
) const;
152 * Returns true if the given texture exists.
153 * This tests both for the original and converted filename.
155 bool TextureExists(const VfsPath
& path
) const;
158 * Returns total number of bytes uploaded for all current texture.
160 size_t GetBytesUploaded() const;
163 * Should be called on any quality or anisotropic change.
165 void OnQualityChanged();
168 CTextureManagerImpl
* m
;
172 * Represents the filename and GL parameters of a texture,
173 * for passing to CTextureManager::CreateTexture.
175 class CTextureProperties
177 friend class CTextureManagerImpl
;
178 friend struct TextureCacheCmp
;
179 friend struct TPequal_to
;
180 friend struct TPhash
;
184 * Use the given texture name, and default GL parameters.
186 explicit CTextureProperties(const VfsPath
& path
)
192 const VfsPath
& path
, const Renderer::Backend::Format formatOverride
)
193 : m_Path(path
), m_FormatOverride(formatOverride
)
198 * Set sampler address mode.
200 void SetAddressMode(const Renderer::Backend::Sampler::AddressMode addressMode
)
202 m_AddressModeU
= m_AddressModeV
= addressMode
;
206 * Set sampler address mode separately for different coordinates.
209 const Renderer::Backend::Sampler::AddressMode addressModeU
,
210 const Renderer::Backend::Sampler::AddressMode addressModeV
)
212 m_AddressModeU
= addressModeU
;
213 m_AddressModeV
= addressModeV
;
217 * The value of max anisotropy is set by options. Though it might make sense
218 * to add an override.
220 void SetAnisotropicFilter(const bool enabled
) { m_AnisotropicFilterEnabled
= enabled
; }
222 // TODO: rather than this static definition of texture properties
223 // (especially anisotropy), maybe we want something that can be more
224 // easily tweaked in an Options menu? e.g. the caller just specifies
225 // "terrain texture mode" and we combine it with the user's options.
226 // That'd let us dynamically change texture properties easily.
235 // void SetQuality(EQualityMode mode, float anisotropy, float lodbias, int reducemipmaps, ...);
237 // or something a bit like that.
239 void SetIgnoreQuality(bool ignore
) { m_IgnoreQuality
= ignore
; }
242 // Must update TPhash, TPequal_to when changing these fields
245 Renderer::Backend::Sampler::AddressMode m_AddressModeU
=
246 Renderer::Backend::Sampler::AddressMode::REPEAT
;
247 Renderer::Backend::Sampler::AddressMode m_AddressModeV
=
248 Renderer::Backend::Sampler::AddressMode::REPEAT
;
249 bool m_AnisotropicFilterEnabled
= false;
250 Renderer::Backend::Format m_FormatOverride
=
251 Renderer::Backend::Format::UNDEFINED
;
252 bool m_IgnoreQuality
= false;
256 * Represents a texture object.
257 * The texture data may or may not have been loaded yet.
258 * Before it has been loaded, all operations will act on a default
259 * 1x1-pixel grey texture instead.
263 NONCOPYABLE(CTexture
);
268 * Returns the width (in pixels) of the current texture.
270 size_t GetWidth() const;
273 * Returns the height (in pixels) of the current texture.
275 size_t GetHeight() const;
278 * Returns whether the current texture has an alpha channel.
280 bool HasAlpha() const;
283 * Returns the ARGB value of the lowest mipmap level (i.e. the
284 * average of the whole texture).
285 * Returns 0 if the texture has no mipmaps.
287 u32
GetBaseColor() const;
290 * Returns total number of bytes uploaded for this texture.
292 size_t GetUploadedSize() const;
295 * Uploads a texture data to a backend texture if successfully loaded.
296 * If the texture data hasn't been loaded yet, this may wait a short while to
297 * load it. If loading takes too long then it will return sooner and the data will
298 * be loaded in a background thread, so this does not guarantee the texture really
301 void UploadBackendTextureIfNeeded(
302 Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
);
305 * Returns a backend texture if successfully uploaded, else fallback.
307 Renderer::Backend::ITexture
* GetBackendTexture();
308 const Renderer::Backend::ITexture
* GetBackendTexture() const;
311 * Attempt to load the texture data quickly, as with
312 * GetUploadedBackendTextureIfNeeded(). Returns whether the texture data is
313 * currently loaded (but not uploaded).
318 * Returns whether the texture data is currently loaded.
320 bool IsLoaded() const { return m_State
== LOADED
; }
323 * Returns whether the texture data is currently uploaded.
325 bool IsUploaded() const { return m_State
== UPLOADED
; }
328 * Activate the prefetching optimisation for this texture.
329 * Use this if it is likely the texture will be needed in the near future.
330 * It will be loaded in the background so that it is likely to be ready when
331 * it is used by Bind().
336 friend class CTextureManagerImpl
;
337 friend class CPredefinedTexture
;
338 friend struct TextureCacheCmp
;
339 friend struct TPequal_to
;
340 friend struct TPhash
;
342 // Only the texture manager can create these
344 std::unique_ptr
<Renderer::Backend::ITexture
> texture
,
345 Renderer::Backend::ITexture
* fallback
,
346 const CTextureProperties
& props
, CTextureManagerImpl
* textureManager
);
348 void ResetBackendTexture(
349 std::unique_ptr
<Renderer::Backend::ITexture
> backendTexture
,
350 Renderer::Backend::ITexture
* fallbackBackendTexture
);
352 const CTextureProperties m_Properties
;
354 std::unique_ptr
<Renderer::Backend::ITexture
> m_BackendTexture
;
355 // It's possible to m_FallbackBackendTexture references m_BackendTexture.
356 Renderer::Backend::ITexture
* m_FallbackBackendTexture
= nullptr;
358 std::unique_ptr
<Tex
> m_TextureData
;
359 size_t m_UploadedSize
= 0;
360 uint32_t m_BaseLevelOffset
= 0;
364 UNLOADED
, // loading has not started
365 PREFETCH_NEEDS_LOADING
, // was prefetched; currently waiting to try loading from cache
366 PREFETCH_NEEDS_CONVERTING
, // was prefetched; currently waiting to be sent to the texture converter
367 PREFETCH_IS_CONVERTING
, // was prefetched; currently being processed by the texture converter
368 HIGH_NEEDS_CONVERTING
, // high-priority; currently waiting to be sent to the texture converter
369 HIGH_IS_CONVERTING
, // high-priority; currently being processed by the texture converter
370 LOADED
, // loading texture data has completed (successfully or not)
371 UPLOADED
// uploading to backend has completed (successfully or not)
374 CTextureManagerImpl
* m_TextureManager
;
376 // Self-reference to let us recover the CTexturePtr for this object.
377 // (weak pointer to avoid cycles)
378 std::weak_ptr
<CTexture
> m_Self
;
381 #endif // INCLUDED_TEXTUREMANAGER