2023-02-01 22:56:35 +01:00
|
|
|
/* Copyright (C) 2023 Wildfire Games.
|
2023-07-27 22:54:46 +02:00
|
|
|
* This file is part of 0 A.D.
|
2010-09-10 23:02:10 +02:00
|
|
|
*
|
2023-07-27 22:54:46 +02:00
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
2010-09-10 23:02:10 +02:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
2023-07-27 22:54:46 +02:00
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
2010-09-10 23:02:10 +02:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2023-07-27 22:54:46 +02:00
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
2010-09-10 23:02:10 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef INCLUDED_TEXTUREMANAGER
|
|
|
|
#define INCLUDED_TEXTUREMANAGER
|
|
|
|
|
2019-11-25 15:30:25 +01:00
|
|
|
#include "graphics/Texture.h"
|
2010-09-10 23:02:10 +02:00
|
|
|
#include "lib/file/vfs/vfs.h"
|
2022-02-13 20:30:28 +01:00
|
|
|
#include "lib/tex/tex.h"
|
2022-05-09 00:02:46 +02:00
|
|
|
#include "renderer/backend/IDevice.h"
|
|
|
|
#include "renderer/backend/IDeviceCommandContext.h"
|
|
|
|
#include "renderer/backend/ITexture.h"
|
2010-09-10 23:02:10 +02:00
|
|
|
|
2019-11-25 15:30:25 +01:00
|
|
|
#include <memory>
|
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
class CTextureProperties;
|
|
|
|
class CTextureManagerImpl;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Texture manager with asynchronous loading and automatic DDS conversion/compression.
|
|
|
|
*
|
|
|
|
* Input textures can be any format. They will be converted to DDS using settings defined
|
|
|
|
* in files named "texture.xml", in the same directory as the texture and in its parent
|
|
|
|
* directories. See CTextureConverter for the XML syntax. The DDS file will be cached
|
|
|
|
* for faster loading in the future.
|
|
|
|
*
|
|
|
|
* Typically the graphics code will initialise many textures at the start of the game,
|
|
|
|
* mostly for off-screen objects, by calling CreateTexture().
|
|
|
|
* Loading texture data may be very slow (especially if it needs to be converted
|
|
|
|
* to DDS), and we don't want the game to become unresponsive.
|
|
|
|
* CreateTexture therefore returns an object immediately, without loading the
|
|
|
|
* texture. If the object is never used then the data will never be loaded.
|
|
|
|
*
|
|
|
|
* Typically, the renderer will call CTexture::Bind() when it wants to use the
|
|
|
|
* texture. This will trigger the loading of the texture data. If it can be loaded
|
|
|
|
* quickly (i.e. there is already a cached DDS version), then it will be loaded before
|
|
|
|
* the function returns, and the texture can be rendered as normal.
|
|
|
|
*
|
|
|
|
* If loading will take a long time, then Bind() binds a default placeholder texture
|
|
|
|
* and starts loading the texture in the background. It will use the correct texture
|
|
|
|
* when the renderer next calls Bind() after the load has finished.
|
|
|
|
*
|
|
|
|
* It is also possible to prefetch textures which are not being rendered yet, but
|
|
|
|
* are expected to be rendered soon (e.g. for off-screen terrain tiles).
|
|
|
|
* These will be loaded in the background, when there are no higher-priority textures
|
|
|
|
* to load.
|
|
|
|
*
|
2022-05-09 00:02:46 +02:00
|
|
|
* The same texture file can be safely loaded multiple times with different backend parameters
|
2010-09-10 23:02:10 +02:00
|
|
|
* (but this should be avoided whenever possible, as it wastes VRAM).
|
|
|
|
*
|
|
|
|
* For release packages, DDS files can be precached by appending ".dds" to their name,
|
|
|
|
* which will be used instead of doing runtime conversion. This means most players should
|
|
|
|
* never experience the slow asynchronous conversion behaviour.
|
|
|
|
* These cache files will typically be packed into an archive for faster loading;
|
|
|
|
* if no archive cache is available then the source file will be converted and stored
|
|
|
|
* as a loose cache file on the user's disk.
|
|
|
|
*/
|
|
|
|
class CTextureManager
|
|
|
|
{
|
2013-05-22 23:40:56 +02:00
|
|
|
NONCOPYABLE(CTextureManager);
|
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* Construct texture manager. vfs must be the VFS instance used for all textures
|
|
|
|
* loaded from this object.
|
2010-09-20 18:34:09 +02:00
|
|
|
* highQuality is slower and intended for batch-conversion modes.
|
2010-09-10 23:02:10 +02:00
|
|
|
*/
|
2022-05-09 00:02:46 +02:00
|
|
|
CTextureManager(PIVFS vfs, bool highQuality, Renderer::Backend::IDevice* device);
|
2010-09-10 23:02:10 +02:00
|
|
|
|
|
|
|
~CTextureManager();
|
|
|
|
|
|
|
|
/**
|
2022-02-25 07:59:57 +01:00
|
|
|
* Create a texture with the given properties.
|
2010-09-10 23:02:10 +02:00
|
|
|
* The texture data will not be loaded immediately.
|
|
|
|
*/
|
|
|
|
CTexturePtr CreateTexture(const CTextureProperties& props);
|
|
|
|
|
2022-02-25 07:59:57 +01:00
|
|
|
/**
|
|
|
|
* Wraps a backend texture.
|
|
|
|
*/
|
|
|
|
CTexturePtr WrapBackendTexture(
|
2022-05-09 00:02:46 +02:00
|
|
|
std::unique_ptr<Renderer::Backend::ITexture> backendTexture);
|
2022-02-25 07:59:57 +01:00
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
/**
|
|
|
|
* Returns a magenta texture. Use this for highlighting errors
|
|
|
|
* (e.g. missing terrain textures).
|
|
|
|
*/
|
2022-02-13 20:30:28 +01:00
|
|
|
const CTexturePtr& GetErrorTexture();
|
2010-09-10 23:02:10 +02:00
|
|
|
|
2021-05-29 14:31:14 +02:00
|
|
|
/**
|
|
|
|
* Returns a single color RGBA texture with CColor(1.0f, 1.0f, 1.0f, 1.0f).
|
|
|
|
*/
|
2022-02-13 20:30:28 +01:00
|
|
|
const CTexturePtr& GetWhiteTexture();
|
2021-05-29 14:31:14 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a single color RGBA texture with CColor(0.0f, 0.0f, 0.0f, 0.0f).
|
|
|
|
*/
|
2022-02-13 20:30:28 +01:00
|
|
|
const CTexturePtr& GetTransparentTexture();
|
2021-05-29 14:31:14 +02:00
|
|
|
|
2022-02-25 07:59:57 +01:00
|
|
|
/**
|
|
|
|
* Returns a white RGBA texture with alpha gradient.
|
|
|
|
*/
|
|
|
|
const CTexturePtr& GetAlphaGradientTexture();
|
|
|
|
|
2022-04-17 12:10:52 +02:00
|
|
|
/**
|
|
|
|
* Returns a single color RGBA texture cube with CColor(0.0f, 0.0f, 0.0f, 1.0f).
|
|
|
|
*/
|
|
|
|
const CTexturePtr& GetBlackTextureCube();
|
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
/**
|
|
|
|
* Work on asynchronous texture loading operations, if any.
|
|
|
|
* Returns true if it did any work.
|
|
|
|
* The caller should typically loop this per frame until it returns
|
|
|
|
* false or exceeds the allocated time for this frame.
|
|
|
|
*/
|
|
|
|
bool MakeProgress();
|
|
|
|
|
2022-02-13 20:30:28 +01:00
|
|
|
/**
|
|
|
|
* Work on asynchronous texture uploading operations, if any.
|
|
|
|
* Returns true if it did any work. Mostly the same as MakeProgress.
|
|
|
|
*/
|
2022-05-09 00:02:46 +02:00
|
|
|
bool MakeUploadProgress(Renderer::Backend::IDeviceCommandContext* deviceCommandContext);
|
2022-02-13 20:30:28 +01:00
|
|
|
|
2010-09-18 20:21:00 +02:00
|
|
|
/**
|
|
|
|
* Synchronously converts and compresses and saves the texture,
|
|
|
|
* and returns the output path (minus a "cache/" prefix). This
|
|
|
|
* is intended for pre-caching textures in release archives.
|
|
|
|
* @return true on success
|
|
|
|
*/
|
|
|
|
bool GenerateCachedTexture(const VfsPath& path, VfsPath& outputPath);
|
|
|
|
|
2023-02-01 22:56:35 +01:00
|
|
|
/**
|
|
|
|
* @return a cached version of the path
|
|
|
|
*/
|
|
|
|
VfsPath GetCachedPath(const VfsPath& path) const;
|
|
|
|
|
2018-08-05 23:50:00 +02:00
|
|
|
/**
|
|
|
|
* Returns true if the given texture exists.
|
|
|
|
* This tests both for the original and converted filename.
|
|
|
|
*/
|
|
|
|
bool TextureExists(const VfsPath& path) const;
|
|
|
|
|
2013-11-09 02:03:23 +01:00
|
|
|
/**
|
|
|
|
* Returns total number of bytes uploaded for all current texture.
|
|
|
|
*/
|
|
|
|
size_t GetBytesUploaded() const;
|
|
|
|
|
2022-02-13 20:30:28 +01:00
|
|
|
/**
|
|
|
|
* Should be called on any quality or anisotropic change.
|
|
|
|
*/
|
|
|
|
void OnQualityChanged();
|
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
private:
|
|
|
|
CTextureManagerImpl* m;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents the filename and GL parameters of a texture,
|
|
|
|
* for passing to CTextureManager::CreateTexture.
|
|
|
|
*/
|
|
|
|
class CTextureProperties
|
|
|
|
{
|
|
|
|
friend class CTextureManagerImpl;
|
|
|
|
friend struct TextureCacheCmp;
|
2011-02-19 22:24:39 +01:00
|
|
|
friend struct TPequal_to;
|
|
|
|
friend struct TPhash;
|
2010-09-10 23:02:10 +02:00
|
|
|
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* Use the given texture name, and default GL parameters.
|
|
|
|
*/
|
2022-02-13 20:30:28 +01:00
|
|
|
explicit CTextureProperties(const VfsPath& path)
|
|
|
|
: m_Path(path)
|
2010-09-10 23:02:10 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-02-13 20:30:28 +01:00
|
|
|
CTextureProperties(
|
|
|
|
const VfsPath& path, const Renderer::Backend::Format formatOverride)
|
|
|
|
: m_Path(path), m_FormatOverride(formatOverride)
|
|
|
|
{
|
|
|
|
}
|
2011-07-30 02:56:45 +02:00
|
|
|
|
|
|
|
/**
|
2022-02-13 20:30:28 +01:00
|
|
|
* Set sampler address mode.
|
2011-07-30 02:56:45 +02:00
|
|
|
*/
|
2022-02-13 20:30:28 +01:00
|
|
|
void SetAddressMode(const Renderer::Backend::Sampler::AddressMode addressMode)
|
|
|
|
{
|
|
|
|
m_AddressModeU = m_AddressModeV = addressMode;
|
|
|
|
}
|
2010-09-10 23:02:10 +02:00
|
|
|
|
|
|
|
/**
|
2022-02-13 20:30:28 +01:00
|
|
|
* Set sampler address mode separately for different coordinates.
|
2010-09-10 23:02:10 +02:00
|
|
|
*/
|
2022-02-13 20:30:28 +01:00
|
|
|
void SetAddressMode(
|
|
|
|
const Renderer::Backend::Sampler::AddressMode addressModeU,
|
|
|
|
const Renderer::Backend::Sampler::AddressMode addressModeV)
|
|
|
|
{
|
|
|
|
m_AddressModeU = addressModeU;
|
|
|
|
m_AddressModeV = addressModeV;
|
|
|
|
}
|
2010-09-10 23:02:10 +02:00
|
|
|
|
2013-10-18 17:36:31 +02:00
|
|
|
/**
|
2022-02-13 20:30:28 +01:00
|
|
|
* The value of max anisotropy is set by options. Though it might make sense
|
|
|
|
* to add an override.
|
2013-10-18 17:36:31 +02:00
|
|
|
*/
|
2022-02-13 20:30:28 +01:00
|
|
|
void SetAnisotropicFilter(const bool enabled) { m_AnisotropicFilterEnabled = enabled; }
|
2013-10-18 17:36:31 +02:00
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
// TODO: rather than this static definition of texture properties
|
|
|
|
// (especially anisotropy), maybe we want something that can be more
|
|
|
|
// easily tweaked in an Options menu? e.g. the caller just specifies
|
|
|
|
// "terrain texture mode" and we combine it with the user's options.
|
|
|
|
// That'd let us dynamically change texture properties easily.
|
|
|
|
//
|
|
|
|
// enum EQualityMode
|
|
|
|
// {
|
|
|
|
// NONE,
|
|
|
|
// TERRAIN,
|
|
|
|
// MODEL,
|
|
|
|
// GUI
|
|
|
|
// }
|
|
|
|
// void SetQuality(EQualityMode mode, float anisotropy, float lodbias, int reducemipmaps, ...);
|
|
|
|
//
|
|
|
|
// or something a bit like that.
|
|
|
|
|
2022-02-13 20:30:28 +01:00
|
|
|
void SetIgnoreQuality(bool ignore) { m_IgnoreQuality = ignore; }
|
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
private:
|
2013-10-18 17:36:31 +02:00
|
|
|
// Must update TPhash, TPequal_to when changing these fields
|
2010-09-10 23:02:10 +02:00
|
|
|
VfsPath m_Path;
|
2022-02-13 20:30:28 +01:00
|
|
|
|
|
|
|
Renderer::Backend::Sampler::AddressMode m_AddressModeU =
|
|
|
|
Renderer::Backend::Sampler::AddressMode::REPEAT;
|
|
|
|
Renderer::Backend::Sampler::AddressMode m_AddressModeV =
|
|
|
|
Renderer::Backend::Sampler::AddressMode::REPEAT;
|
|
|
|
bool m_AnisotropicFilterEnabled = false;
|
|
|
|
Renderer::Backend::Format m_FormatOverride =
|
|
|
|
Renderer::Backend::Format::UNDEFINED;
|
|
|
|
bool m_IgnoreQuality = false;
|
2010-09-10 23:02:10 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a texture object.
|
|
|
|
* The texture data may or may not have been loaded yet.
|
|
|
|
* Before it has been loaded, all operations will act on a default
|
|
|
|
* 1x1-pixel grey texture instead.
|
|
|
|
*/
|
|
|
|
class CTexture
|
|
|
|
{
|
|
|
|
NONCOPYABLE(CTexture);
|
|
|
|
public:
|
|
|
|
~CTexture();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the width (in pixels) of the current texture.
|
|
|
|
*/
|
|
|
|
size_t GetWidth() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the height (in pixels) of the current texture.
|
|
|
|
*/
|
|
|
|
size_t GetHeight() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether the current texture has an alpha channel.
|
|
|
|
*/
|
|
|
|
bool HasAlpha() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the ARGB value of the lowest mipmap level (i.e. the
|
|
|
|
* average of the whole texture).
|
|
|
|
* Returns 0 if the texture has no mipmaps.
|
|
|
|
*/
|
2015-03-16 00:59:48 +01:00
|
|
|
u32 GetBaseColor() const;
|
2010-09-10 23:02:10 +02:00
|
|
|
|
2013-11-09 02:03:23 +01:00
|
|
|
/**
|
|
|
|
* Returns total number of bytes uploaded for this texture.
|
|
|
|
*/
|
|
|
|
size_t GetUploadedSize() const;
|
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
/**
|
2022-02-13 20:30:28 +01:00
|
|
|
* Uploads a texture data to a backend texture if successfully loaded.
|
2010-09-10 23:02:10 +02:00
|
|
|
* If the texture data hasn't been loaded yet, this may wait a short while to
|
|
|
|
* load it. If loading takes too long then it will return sooner and the data will
|
|
|
|
* be loaded in a background thread, so this does not guarantee the texture really
|
2022-02-13 20:30:28 +01:00
|
|
|
* will be uploaded.
|
2010-09-10 23:02:10 +02:00
|
|
|
*/
|
2022-02-13 20:30:28 +01:00
|
|
|
void UploadBackendTextureIfNeeded(
|
2022-05-09 00:02:46 +02:00
|
|
|
Renderer::Backend::IDeviceCommandContext* deviceCommandContext);
|
2010-09-10 23:02:10 +02:00
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
/**
|
2022-02-13 20:30:28 +01:00
|
|
|
* Returns a backend texture if successfully uploaded, else fallback.
|
2011-03-26 21:17:21 +01:00
|
|
|
*/
|
2022-05-09 00:02:46 +02:00
|
|
|
Renderer::Backend::ITexture* GetBackendTexture();
|
|
|
|
const Renderer::Backend::ITexture* GetBackendTexture() const;
|
2011-03-26 21:17:21 +01:00
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
/**
|
2022-02-13 20:30:28 +01:00
|
|
|
* Attempt to load the texture data quickly, as with
|
|
|
|
* GetUploadedBackendTextureIfNeeded(). Returns whether the texture data is
|
|
|
|
* currently loaded (but not uploaded).
|
2010-09-10 23:02:10 +02:00
|
|
|
*/
|
|
|
|
bool TryLoad();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether the texture data is currently loaded.
|
|
|
|
*/
|
2022-02-13 20:30:28 +01:00
|
|
|
bool IsLoaded() const { return m_State == LOADED; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether the texture data is currently uploaded.
|
|
|
|
*/
|
|
|
|
bool IsUploaded() const { return m_State == UPLOADED; }
|
2010-09-10 23:02:10 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Activate the prefetching optimisation for this texture.
|
|
|
|
* Use this if it is likely the texture will be needed in the near future.
|
|
|
|
* It will be loaded in the background so that it is likely to be ready when
|
|
|
|
* it is used by Bind().
|
|
|
|
*/
|
|
|
|
void Prefetch();
|
|
|
|
|
|
|
|
private:
|
2022-02-13 20:30:28 +01:00
|
|
|
friend class CTextureManagerImpl;
|
2022-02-25 07:59:57 +01:00
|
|
|
friend class CPredefinedTexture;
|
2022-02-13 20:30:28 +01:00
|
|
|
friend struct TextureCacheCmp;
|
|
|
|
friend struct TPequal_to;
|
|
|
|
friend struct TPhash;
|
|
|
|
|
|
|
|
// Only the texture manager can create these
|
|
|
|
explicit CTexture(
|
2022-05-09 00:02:46 +02:00
|
|
|
std::unique_ptr<Renderer::Backend::ITexture> texture,
|
|
|
|
Renderer::Backend::ITexture* fallback,
|
2022-02-13 20:30:28 +01:00
|
|
|
const CTextureProperties& props, CTextureManagerImpl* textureManager);
|
|
|
|
|
|
|
|
void ResetBackendTexture(
|
2022-05-09 00:02:46 +02:00
|
|
|
std::unique_ptr<Renderer::Backend::ITexture> backendTexture,
|
|
|
|
Renderer::Backend::ITexture* fallbackBackendTexture);
|
2010-09-10 23:02:10 +02:00
|
|
|
|
|
|
|
const CTextureProperties m_Properties;
|
|
|
|
|
2022-05-09 00:02:46 +02:00
|
|
|
std::unique_ptr<Renderer::Backend::ITexture> m_BackendTexture;
|
2022-02-13 20:30:28 +01:00
|
|
|
// It's possible to m_FallbackBackendTexture references m_BackendTexture.
|
2022-05-09 00:02:46 +02:00
|
|
|
Renderer::Backend::ITexture* m_FallbackBackendTexture = nullptr;
|
2015-03-16 00:59:48 +01:00
|
|
|
u32 m_BaseColor;
|
2022-02-13 20:30:28 +01:00
|
|
|
std::unique_ptr<Tex> m_TextureData;
|
|
|
|
size_t m_UploadedSize = 0;
|
|
|
|
uint32_t m_BaseLevelOffset = 0;
|
2010-09-10 23:02:10 +02:00
|
|
|
|
2022-02-13 20:30:28 +01:00
|
|
|
enum
|
|
|
|
{
|
2010-09-10 23:02:10 +02:00
|
|
|
UNLOADED, // loading has not started
|
|
|
|
PREFETCH_NEEDS_LOADING, // was prefetched; currently waiting to try loading from cache
|
|
|
|
PREFETCH_NEEDS_CONVERTING, // was prefetched; currently waiting to be sent to the texture converter
|
|
|
|
PREFETCH_IS_CONVERTING, // was prefetched; currently being processed by the texture converter
|
|
|
|
HIGH_NEEDS_CONVERTING, // high-priority; currently waiting to be sent to the texture converter
|
|
|
|
HIGH_IS_CONVERTING, // high-priority; currently being processed by the texture converter
|
2022-02-13 20:30:28 +01:00
|
|
|
LOADED, // loading texture data has completed (successfully or not)
|
|
|
|
UPLOADED // uploading to backend has completed (successfully or not)
|
2010-09-10 23:02:10 +02:00
|
|
|
} m_State;
|
|
|
|
|
|
|
|
CTextureManagerImpl* m_TextureManager;
|
|
|
|
|
|
|
|
// Self-reference to let us recover the CTexturePtr for this object.
|
|
|
|
// (weak pointer to avoid cycles)
|
2015-01-25 04:10:58 +01:00
|
|
|
std::weak_ptr<CTexture> m_Self;
|
2010-09-10 23:02:10 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif // INCLUDED_TEXTUREMANAGER
|