OGRE-Next  3.0.0
Object-Oriented Graphics Rendering Engine
Ogre::TextureGpuManager Class Referenceabstract

This class manages all textures (i.e. More...

#include <OgreTextureGpuManager.h>

+ Inheritance diagram for Ogre::TextureGpuManager:

Classes

struct  BudgetEntry
 Specifies the minimum squared resolution & number of slices to keep around all the for time StagingTextures. More...
 
struct  MetadataCacheEntry
 
struct  ResourceEntry
 

Public Types

typedef vector< BudgetEntry >::type BudgetEntryVec
 
typedef map< IdString, MetadataCacheEntry >::type MetadataCacheMap
 
typedef map< IdString, ResourceEntry >::type ResourceEntryMap
 
- Public Types inherited from Ogre::TextureGpuListener
enum  Reason {
  Unknown , FromStorageToSysRam , FromSysRamToStorage , GainedResidency ,
  LostResidency , PoolTextureSlotChanged , ResidentToSysRamSync , MetadataCacheOutOfDate ,
  ExceptionThrown , FsaaSettingAlteredByApi , ReadyForRendering , Deleted
}
 

Public Member Functions

 TextureGpuManager (VaoManager *vaoManager, RenderSystem *renderSystem)
 
 ~TextureGpuManager () override
 
void _queueDownloadToRam (TextureGpu *texture, bool resyncOnly)
 
void _releaseSlotFromTexture (TextureGpu *texture)
 Must be called from main thread. More...
 
void _removeMetadataCacheEntry (TextureGpu *texture)
 
void _reserveSlotForTexture (TextureGpu *texture)
 Must be called from main thread. More...
 
void _scheduleTransitionTo (TextureGpu *texture, GpuResidency::GpuResidency targetResidency, Image2 *image, bool autoDeleteImage, bool reuploadOnly, bool bSkipMultiload)
 
void _scheduleUpdate (TextureGpu *texture, uint32 filters, Image2 *image, bool autoDeleteImage, bool skipMetadataCache=false, uint32 sliceOrDepth=std::numeric_limits< uint32 >::max())
 
void _setIgnoreScheduledTasks (bool ignoreSchedTasks)
 When true we will ignore all tasks in mScheduledTasks and execute transitions immediately Caller is responsible for ensuring this is safe to do. More...
 
bool _update (bool syncWithWorkerThread)
 Returns true if there is no more streaming work to be done yet (if false, calls to _update could be needed once again) See waitForStreamingCompletion. More...
 
void _updateMetadataCache (TextureGpu *texture)
 
void _updateStreaming ()
 
unsigned long _updateStreamingWorkerThread (ThreadHandle *threadHandle)
 
unsigned long _updateTextureMultiLoadWorkerThread (ThreadHandle *threadHandle)
 Implements multiload. More...
 
void _waitFor (TextureGpu *texture, bool metadataOnly)
 Do not use directly. See TextureGpu::waitForMetadata & TextureGpu::waitForDataReady. More...
 
void _waitForPendingGpuToCpuSyncs (TextureGpu *texture)
 Do not use directly. See TextureGpu::waitForPendingSyncs. More...
 
virtual bool checkSupport (PixelFormatGpu format, TextureTypes::TextureTypes textureType, uint32 textureFlags) const
 Checks if the given format with the texture flags combination is supported. More...
 
AsyncTextureTicketcreateAsyncTextureTicket (uint32 width, uint32 height, uint32 depthOrSlices, TextureTypes::TextureTypes textureType, PixelFormatGpu pixelFormatFamily)
 Creates an AsyncTextureTicket that can be used to download data GPU -> CPU from a TextureGpu. More...
 
TextureGpucreateOrRetrieveTexture (const String &name, const String &aliasName, GpuPageOutStrategy::GpuPageOutStrategy pageOutStrategy, CommonTextureTypes::CommonTextureTypes type, const String &resourceGroup=BLANKSTRING, uint32 poolId=0)
 Helper function to call createOrRetrieveTexture with common parameters used for 2D diffuse textures loaded from file. More...
 
TextureGpucreateOrRetrieveTexture (const String &name, const String &aliasName, GpuPageOutStrategy::GpuPageOutStrategy pageOutStrategy, uint32 textureFlags, TextureTypes::TextureTypes initialType, const String &resourceGroup=BLANKSTRING, uint32 filters=0, uint32 poolId=0)
 
TextureGpucreateOrRetrieveTexture (const String &name, GpuPageOutStrategy::GpuPageOutStrategy pageOutStrategy, CommonTextureTypes::CommonTextureTypes type, const String &resourceGroup=BLANKSTRING, uint32 poolId=0)
 
TextureGpucreateOrRetrieveTexture (const String &name, GpuPageOutStrategy::GpuPageOutStrategy pageOutStrategy, uint32 textureFlags, TextureTypes::TextureTypes initialType, const String &resourceGroup=BLANKSTRING, uint32 filters=0, uint32 poolId=0)
 
TextureGpucreateTexture (const String &name, const String &aliasName, GpuPageOutStrategy::GpuPageOutStrategy pageOutStrategy, uint32 textureFlags, TextureTypes::TextureTypes initialType, const String &resourceGroup=BLANKSTRING, uint32 filters=0, uint32 poolId=0)
 
TextureGpucreateTexture (const String &name, GpuPageOutStrategy::GpuPageOutStrategy pageOutStrategy, uint32 textureFlags, TextureTypes::TextureTypes initialType, const String &resourceGroup=BLANKSTRING, uint32 filters=0, uint32 poolId=0)
 
void destroyAllAsyncTextureTicket ()
 
void destroyAsyncTextureTicket (AsyncTextureTicket *ticket)
 
void destroyTexture (TextureGpu *texture)
 Destroys a texture. More...
 
void dumpMemoryUsage (Log *log, Ogre::uint32 mask=ResidencyMask::All) const
 
void dumpStats () const
 
void exportTextureMetadataCache (String &outJson)
 
const StringfindAliasNameStr (IdString idName) const
 This function CAN be called from any thread. More...
 
const StringfindResourceGroupStr (IdString idName) const
 This function CAN be called from any thread. More...
 
const StringfindResourceNameStr (IdString idName) const
 This function CAN be called from any thread. More...
 
TextureGpufindTextureNoThrow (IdString name) const
 
const BudgetEntryVecgetBudget () const
 
DefaultMipmapGen::DefaultMipmapGen getDefaultMipmapGeneration () const
 
DefaultMipmapGen::DefaultMipmapGen getDefaultMipmapGenerationCubemaps () const
 
const ResourceEntryMapgetEntries () const
 
uint64 getLoadRequestsCounter () const
 Calling waitForStreamingCompletion before Root::renderOneFrame should guarantee the render is perfect. More...
 
void getMemoryStats (size_t &outTextureBytesCpu, size_t &outTextureBytesGpu, size_t &outUsedStagingTextureBytes, size_t &outAvailableStagingTextureBytes)
 
bool getProfileLoadingTime () const
 
RenderSystemgetRenderSystem () const
 
StagingTexturegetStagingTexture (uint32 width, uint32 height, uint32 depth, uint32 slices, PixelFormatGpu pixelFormat, size_t minConsumptionRatioThreshold=25u)
 Creates a StagingTexture which is required to upload data CPU -> GPU into a TextureGpu. More...
 
VaoManagergetVaoManager () const
 
bool hasPoolId (uint32 poolId, uint32 width, uint32 height, uint8 numMipmaps, PixelFormatGpu pixelFormat) const
 
bool hasTextureResource (const String &aliasName, const String &resourceGroup) const
 Returns true if a texture with the given aliasName exists, or if a ResourceGroupListener provides such texture, or if such texture exists (i.e. More...
 
void importTextureMetadataCache (const String &filename, const char *jsonString, bool bCreateReservedPools)
 
bool isDoneStreaming () const
 Returns true if we're done loading all textures based on the return value of the last call to TextureGpuManager::_update and whether new tasks have been scheduled since then. More...
 
void notifyTextureChanged (TextureGpu *texture, TextureGpuListener::Reason reason, void *extraData) override
 
void removeStagingTexture (StagingTexture *stagingTexture)
 
TextureGpureservePoolId (uint32 poolId, uint32 width, uint32 height, uint32 numSlices, uint8 numMipmaps, PixelFormatGpu pixelFormat)
 Reserves and preallocates a pool with the given parameters Returns the master texture that owns the pool. More...
 
void saveTexture (TextureGpu *texture, const String &folderPath, set< String >::type &savedTextures, bool saveOitd, bool saveOriginal, HlmsTextureExportListener *listener)
 
void setDefaultMipmapGeneration (DefaultMipmapGen::DefaultMipmapGen defaultMipmapGen, DefaultMipmapGen::DefaultMipmapGen defaultMipmapGenCubemaps)
 Whether to use HW or SW mipmap generation when specifying TextureFilter::TypeGenerateDefaultMipmaps for loading files from textures. More...
 
void setMultiLoadPool (uint32 numThreads)
 OgreNext always performs background streaming to load textures in a worker thread. More...
 
void setProfileLoadingTime (bool bProfile)
 When enabled, we will profile the time it takes a texture to go from Resident to Ready and Log it. More...
 
void setStagingTextureMaxBudgetBytes (size_t stagingTextureMaxBudgetBytes)
 At a high level, texture loading works like this: More...
 
void setTextureGpuManagerListener (TextureGpuManagerListener *listener)
 Sets a new listener. More...
 
void setTrylockMutexFailureLimit (uint32 tryLockFailureLimit)
 The main thread tries to acquire a lock from the background thread, do something very quick, and release it. More...
 
void setWorkerThreadMaxPerStagingTextureRequestBytes (size_t maxPerStagingTextureRequestBytes)
 The worker thread tracks how many data it is loading so the Main thread can request additional StagingTextures if necessary. More...
 
void setWorkerThreadMaxPreloadBytes (size_t maxPreloadBytes)
 The worker thread first loads the texture from disk to RAM (aka "preload", and then copies from RAM to StagingTexture. More...
 
void setWorkerThreadMinimumBudget (const BudgetEntryVec &budget, uint32 maxSplitResolution=0)
 Background streaming works by having a bunch of preallocated StagingTextures so we're ready to start uploading as soon as we see a request to load a texture from file. More...
 
void shutdown ()
 
void waitForStreamingCompletion ()
 Blocks main thread until all pending textures are fully loaded. More...
 
- Public Member Functions inherited from Ogre::TextureGpuListener
virtual ~TextureGpuListener ()
 
virtual bool shouldStayLoaded (TextureGpu *texture)
 Return true if this TextureGpu should likely stay loaded or else graphical changes could occur. More...
 

Public Attributes

bool mIgnoreSRgbPreference
 While true, calls to createTexture & createOrRetrieveTexture will ignore and unset the TextureFlags::PrefersLoadingFromFileAsSRGB flag. More...
 

Detailed Description

This class manages all textures (i.e.

TextureGpu) since Ogre 2.2

Explanation of the streaming model:

TextureGpuManager uses a worker thread to load textures in the background. There are several restrictions the implementation needs to account for:

  • D3D11 does not support persistent mapping. This means we must call unmap on a StagingTexture before we can copy it to the final texture.
  • Calling map/unmap from multiple threads is nearly impossible in OpenGL. This means map/unmap calls must happen in the main thread.
  • ResourceGroupManager is not thread-friendly (building with thread support fills ResourceGroupManager with huge fat mutexes)
  • Most APIs allow using a simple buffer to store all sorts of staging data (regardless of format and resolution), but D3D11 is very inflexible about this, requiring StagingTextures to have a 2D resolution (rather than being a 1D buffer with just bytes), and must match the same format.

Because of these restrictions, TextureGpuManager::scheduleLoadRequest will grab the Archive to load from file from ResourceGroupManager and then create a request for the worker thread.

The worker thread (_updateStreamingWorkerThread) runs an infinite loop waiting for new requests.

The worker thread will process incoming LoadRequest: it will open the file and retrieve the important information first aka the metadata (such as resolution, pixel format, number of mipmaps, etc). It is most likely the worker thread will load the whole image from disk, and not just the metadata (at least the first slice + first mip).

The image is at the moment in RAM, but two things need to happen:

  1. The image must be copied to a StagingTexture so that it can be later be copied to the final texture.
  2. The texture must become Resident so that our API commands can actually work (like copying from StagingTexture to the final texture object)

The worker thread will now push an entry to mStreamingData.queuedImages for all the slices & mips that are pending to copy from RAM to a StagingTexture. That includes slice 0 mip 0.

It will also emit a command the main thread will eventually see to make the texture Resident (via ObjCmdBuffer::TransitionToResident)

The worker thread will call TextureGpuManager::getStreaming to grab an available StagingTexture that has been pre-mapped in the main thread that can hold the data we want to upload.

If no such Texture is available, we cannot upload the texture yet and this failure is recorded; and we do not remove the entry from mStreamingData.queuedImages Until mStreamingData.queuedImages[i].empty() returns true, the worker thread, with each new iteration, will try again to grab a StagingTexture to finish the jobs.

From the main thread, with each TextureGpuManager::_update; fullfillBudget will check the recorded failures in getStreaming from the worker thread and create & map more StagingTextures to satisfy the worker thread.

The main thread will also execute the transition to resident commands sent from the worker thread.

This means that in worst case scenario, uploading a texture may require multiple ping pongs between the worker & main thread, which is why TextureGpuManager::waitForStreamingCompletion keeps calling _update in a loop until the texture is fully loaded.

To put things into perspective: if getStreaming always failed to grab a StagingTexture (i.e. due to a bug), then waitForStreamingCompletion would be stuck in an infinite loop.

Note that this behavior applies to both multithreaded and singlethreaded versions (i.e. when OGRE_FORCE_TEXTURE_STREAMING_ON_MAIN_THREAD is defined).

When everything's done, queuedImage.empty() returns true, a ObjCmdBuffer::NotifyDataIsReady command to the main thread is issued, and the entry is removed from mStreamingData.queuedImages

Of course, the main thread tries to predict how much StagingTexture will be needed by tracking past usage and by trying to fullfill mBudget (see TextureGpuManager::setWorkerThreadMinimumBudget), so under best case scenario the ping pong between worker & main thread is kept to a minimum.

TextureGpuManager::mMaxPreloadBytes puts an upper limit to this prediction to prevent memory from sky-rocketing (or reaching out of memory conditions) during spikes. There's only so much GART/GTT memory available in a system.

The variable mEntriesToProcessPerIteration controls how many entries in LoadRequests are processed per iteration in the worker thread. High values cause the worker thread to appear "stuck" loading lots of images without flushing our internal command buffer, which means the main thread won't see what's happening and thus unable to fullfull budget failure requests, predict accurately, and unable to perform transition to resident requests. I.e. the main thread will sit idle, until it suddenly sees a lot of work coming from the worker thread. Very low values can cause more threading contention due to excessive flushing.

The metadata cache helps with performance by being able to know in the main thread before creating the LoadRequest what texture pool to reserve. But performance will be degraded if the metadata cache lied, as we must then perform multiple ping pongs between the threads to correct the error.

See also
TextureGpu
Remarks
Even if you use waitForStreamingCompletion to have all of your frames rendering "perfectly" (i.e. prefer stalling rendering instead of showing a replacement texture until the real one is loaded), multithreaded streaming can still reduce your loading times because Ogre will immediately start loading the texture from disk while the main thread can keep executing your code (like moving on to the next Item or Datablock you're instantiating)

Member Typedef Documentation

◆ BudgetEntryVec

◆ MetadataCacheMap

◆ ResourceEntryMap

Constructor & Destructor Documentation

◆ TextureGpuManager()

Ogre::TextureGpuManager::TextureGpuManager ( VaoManager vaoManager,
RenderSystem renderSystem 
)

◆ ~TextureGpuManager()

Ogre::TextureGpuManager::~TextureGpuManager ( )
override

Member Function Documentation

◆ _queueDownloadToRam()

void Ogre::TextureGpuManager::_queueDownloadToRam ( TextureGpu texture,
bool  resyncOnly 
)

◆ _releaseSlotFromTexture()

void Ogre::TextureGpuManager::_releaseSlotFromTexture ( TextureGpu texture)

Must be called from main thread.

◆ _removeMetadataCacheEntry()

void Ogre::TextureGpuManager::_removeMetadataCacheEntry ( TextureGpu texture)

◆ _reserveSlotForTexture()

void Ogre::TextureGpuManager::_reserveSlotForTexture ( TextureGpu texture)

Must be called from main thread.

◆ _scheduleTransitionTo()

void Ogre::TextureGpuManager::_scheduleTransitionTo ( TextureGpu texture,
GpuResidency::GpuResidency  targetResidency,
Image2 image,
bool  autoDeleteImage,
bool  reuploadOnly,
bool  bSkipMultiload 
)

◆ _scheduleUpdate()

void Ogre::TextureGpuManager::_scheduleUpdate ( TextureGpu texture,
uint32  filters,
Image2 image,
bool  autoDeleteImage,
bool  skipMetadataCache = false,
uint32  sliceOrDepth = std::numeric_limits< uint32 >::max() 
)

◆ _setIgnoreScheduledTasks()

void Ogre::TextureGpuManager::_setIgnoreScheduledTasks ( bool  ignoreSchedTasks)

When true we will ignore all tasks in mScheduledTasks and execute transitions immediately Caller is responsible for ensuring this is safe to do.

The main reason for this function is that when the metadata cache is proven to be out of date and comes back to the main thread, we need to perform a Resident -> OnStorage -> Resident transition that bypasses pending operations, and pretend the texture has been in Resident all along.

◆ _update()

bool Ogre::TextureGpuManager::_update ( bool  syncWithWorkerThread)

Returns true if there is no more streaming work to be done yet (if false, calls to _update could be needed once again) See waitForStreamingCompletion.

Parameters
syncWithWorkerThreadWhen true, we will wait for the worker thread to release the main mutex instead of just continuing and trying again next time we get called. This is important for waitForStreamingCompletion & _waitFor because otherwise main thread may not see worker thread has finished because it's also grabbing the main mutex; and waitForStreamingCompletion will go to sleep thinking worker thread has yet to finish, and worker thread won't wake up the main thread because it has already notified it.

◆ _updateMetadataCache()

void Ogre::TextureGpuManager::_updateMetadataCache ( TextureGpu texture)

◆ _updateStreaming()

void Ogre::TextureGpuManager::_updateStreaming ( )

◆ _updateStreamingWorkerThread()

unsigned long Ogre::TextureGpuManager::_updateStreamingWorkerThread ( ThreadHandle threadHandle)

◆ _updateTextureMultiLoadWorkerThread()

unsigned long Ogre::TextureGpuManager::_updateTextureMultiLoadWorkerThread ( ThreadHandle threadHandle)

Implements multiload.

It is a simple job pool that picks the next request and loads an Image2.

Then passes it to the worker thread as if the main thread had requested to load a texture from an Image2 pointer, instead of loading it from file or listener.

i.e. it pretends the user loaded: tex->scheduleTransitionTo( GpuResidency::Resident, &image, autoDeleteImage = true );

If there are any errors we abort and pass the raw LoadRequest to the streaming thread; and the streaming thread, trying to open this texture, should encounter the same error again and handle it properly.

Parameters
threadHandle
Returns
Thread's return value.

◆ _waitFor()

void Ogre::TextureGpuManager::_waitFor ( TextureGpu texture,
bool  metadataOnly 
)

Do not use directly. See TextureGpu::waitForMetadata & TextureGpu::waitForDataReady.

◆ _waitForPendingGpuToCpuSyncs()

void Ogre::TextureGpuManager::_waitForPendingGpuToCpuSyncs ( TextureGpu texture)

Do not use directly. See TextureGpu::waitForPendingSyncs.

◆ checkSupport()

virtual bool Ogre::TextureGpuManager::checkSupport ( PixelFormatGpu  format,
TextureTypes::TextureTypes  textureType,
uint32  textureFlags 
) const
virtual

Checks if the given format with the texture flags combination is supported.

Parameters
format
textureFlagsSee TextureFlags::TextureFlags Supported flags are: NotTexture RenderToTexture Uav AllowAutomipmaps

When NotTexture is set, we don't check whether it's possible to sample from this texture. Note that some buggy Android drivers may report that it's not possible to sample from that texture when it actually is.

Returns
True if supported. False otherwise

Reimplemented in Ogre::VulkanTextureGpuManager, and Ogre::MetalTextureGpuManager.

◆ createAsyncTextureTicket()

AsyncTextureTicket* Ogre::TextureGpuManager::createAsyncTextureTicket ( uint32  width,
uint32  height,
uint32  depthOrSlices,
TextureTypes::TextureTypes  textureType,
PixelFormatGpu  pixelFormatFamily 
)

Creates an AsyncTextureTicket that can be used to download data GPU -> CPU from a TextureGpu.

To upload data CPU -> GPU see getStagingTexture

Parameters
width
height
depthOrSlices
pixelFormatFamilyIf the value is not a family value, it will automatically be converted to one.
Returns

◆ createOrRetrieveTexture() [1/4]

TextureGpu* Ogre::TextureGpuManager::createOrRetrieveTexture ( const String name,
const String aliasName,
GpuPageOutStrategy::GpuPageOutStrategy  pageOutStrategy,
CommonTextureTypes::CommonTextureTypes  type,
const String resourceGroup = BLANKSTRING,
uint32  poolId = 0 
)

Helper function to call createOrRetrieveTexture with common parameters used for 2D diffuse textures loaded from file.

◆ createOrRetrieveTexture() [2/4]

TextureGpu* Ogre::TextureGpuManager::createOrRetrieveTexture ( const String name,
const String aliasName,
GpuPageOutStrategy::GpuPageOutStrategy  pageOutStrategy,
uint32  textureFlags,
TextureTypes::TextureTypes  initialType,
const String resourceGroup = BLANKSTRING,
uint32  filters = 0,
uint32  poolId = 0 
)

◆ createOrRetrieveTexture() [3/4]

TextureGpu* Ogre::TextureGpuManager::createOrRetrieveTexture ( const String name,
GpuPageOutStrategy::GpuPageOutStrategy  pageOutStrategy,
CommonTextureTypes::CommonTextureTypes  type,
const String resourceGroup = BLANKSTRING,
uint32  poolId = 0 
)

◆ createOrRetrieveTexture() [4/4]

TextureGpu* Ogre::TextureGpuManager::createOrRetrieveTexture ( const String name,
GpuPageOutStrategy::GpuPageOutStrategy  pageOutStrategy,
uint32  textureFlags,
TextureTypes::TextureTypes  initialType,
const String resourceGroup = BLANKSTRING,
uint32  filters = 0,
uint32  poolId = 0 
)

◆ createTexture() [1/2]

TextureGpu* Ogre::TextureGpuManager::createTexture ( const String name,
const String aliasName,
GpuPageOutStrategy::GpuPageOutStrategy  pageOutStrategy,
uint32  textureFlags,
TextureTypes::TextureTypes  initialType,
const String resourceGroup = BLANKSTRING,
uint32  filters = 0,
uint32  poolId = 0 
)
Parameters
nameName of the resource. For example TreeWood.png
aliasNameUsually aliasName = name. An alias name allows you to load the same texture (e.g. TreeWood.png) with different settings. For example: Alias 0 - "Tree Wood With Mipmaps" Alias 1 - "Tree Wood Without Mipmaps" Alias 2 - "Tree Wood Without TextureFlags::AutomaticBatching" This lets you have 3 copies of the same file in memory.
pageOutStrategy
textureFlagsSee TextureFlags::TextureFlags
initialTypeStrictly not required (i.e. can be left TextureTypes::Unknown) however it can be needed if set to a material before it is fully loaded; and the shader expects a particular type (e.g. it expects a cubemap). While it's not yet loaded, a dummy texture will that matches the type will be used; and it's important that the right dummy texture is selected. So if you know in advance a particular type is needed, this parameter tells Ogre what dummy to use.
resourceGroupOptional, but required if you want to load files from disk (or anything provided by the ResourceGroupManager)
poolIdOptional. See TextureGpu::setTexturePoolId This parameter informs which pool ID you wish the texture to be assigned for. Note however, if you're using createOrRetrieveTexture and the texture has already been created (i.e. it's being retrieved) then the pool ID parameter will be ignored, as the texture was already created with a pool ID.
Returns

◆ createTexture() [2/2]

TextureGpu* Ogre::TextureGpuManager::createTexture ( const String name,
GpuPageOutStrategy::GpuPageOutStrategy  pageOutStrategy,
uint32  textureFlags,
TextureTypes::TextureTypes  initialType,
const String resourceGroup = BLANKSTRING,
uint32  filters = 0,
uint32  poolId = 0 
)

◆ destroyAllAsyncTextureTicket()

void Ogre::TextureGpuManager::destroyAllAsyncTextureTicket ( )

◆ destroyAsyncTextureTicket()

void Ogre::TextureGpuManager::destroyAsyncTextureTicket ( AsyncTextureTicket ticket)

◆ destroyTexture()

void Ogre::TextureGpuManager::destroyTexture ( TextureGpu texture)

Destroys a texture.

Classes who wish to hold a weak reference should listen for TextureGpuListener::Deleted events and clear their pointers when the texture gets destroyed.

Classes who wish to hold a stronger reference (note: it says 'stronger', not 'strong') should return true in TextureGpuListener::shouldStayLoaded, but it is not guaranteed to be honoured.

Users should iterate through listeners and see if any listener's shouldStayLoaded returns true. If you still want to destroy the texture, the class should still be able to handle TextureGpuListener::Deleted gracefully.

See MemoryGameState::unloadUnusedTextures in Tutorial_MemoryGameState.cpp

Ogre doesn't call destroyTexture unless it's on shutdown or a specific Ogre-controlled texture (e.g. something related to PBS, Irradiance Fields, etc)

Users are the ones in control of which textures get unloaded. It is suggested users group textures by criteria so that they can be loaded and unloaded in bulk (i.e. by relation to a level, or area in an open world game, by scene, etc)

Parameters
texture

◆ dumpMemoryUsage()

void Ogre::TextureGpuManager::dumpMemoryUsage ( Log log,
Ogre::uint32  mask = ResidencyMask::All 
) const

◆ dumpStats()

void Ogre::TextureGpuManager::dumpStats ( ) const

◆ exportTextureMetadataCache()

void Ogre::TextureGpuManager::exportTextureMetadataCache ( String outJson)

◆ findAliasNameStr()

const String* Ogre::TextureGpuManager::findAliasNameStr ( IdString  idName) const

This function CAN be called from any thread.

◆ findResourceGroupStr()

const String* Ogre::TextureGpuManager::findResourceGroupStr ( IdString  idName) const

This function CAN be called from any thread.

◆ findResourceNameStr()

const String* Ogre::TextureGpuManager::findResourceNameStr ( IdString  idName) const

This function CAN be called from any thread.

◆ findTextureNoThrow()

TextureGpu* Ogre::TextureGpuManager::findTextureNoThrow ( IdString  name) const

◆ getBudget()

const BudgetEntryVec& Ogre::TextureGpuManager::getBudget ( ) const

◆ getDefaultMipmapGeneration()

DefaultMipmapGen::DefaultMipmapGen Ogre::TextureGpuManager::getDefaultMipmapGeneration ( ) const

◆ getDefaultMipmapGenerationCubemaps()

DefaultMipmapGen::DefaultMipmapGen Ogre::TextureGpuManager::getDefaultMipmapGenerationCubemaps ( ) const

◆ getEntries()

const ResourceEntryMap& Ogre::TextureGpuManager::getEntries ( ) const
inline

◆ getLoadRequestsCounter()

uint64 Ogre::TextureGpuManager::getLoadRequestsCounter ( ) const
inline

Calling waitForStreamingCompletion before Root::renderOneFrame should guarantee the render is perfect.

Except... a new texture may be loaded while inside renderOneFrame. If that happens the render may not be perfect. You can solve that by rendering the frame again if you need all frames to be 'perfect':

textureMgr->waitForStreamingCompletion();
const oldValue = textureMgr->getLoadRequestsCounter();
root->renderOneFrame();
if( oldValue != textureMgr->getLoadRequestsCounter() )
{
textureMgr->waitForStreamingCompletion();
root->renderOneFrame();
}

◆ getMemoryStats()

void Ogre::TextureGpuManager::getMemoryStats ( size_t &  outTextureBytesCpu,
size_t &  outTextureBytesGpu,
size_t &  outUsedStagingTextureBytes,
size_t &  outAvailableStagingTextureBytes 
)

◆ getProfileLoadingTime()

bool Ogre::TextureGpuManager::getProfileLoadingTime ( ) const
inline

◆ getRenderSystem()

RenderSystem* Ogre::TextureGpuManager::getRenderSystem ( ) const

◆ getStagingTexture()

StagingTexture* Ogre::TextureGpuManager::getStagingTexture ( uint32  width,
uint32  height,
uint32  depth,
uint32  slices,
PixelFormatGpu  pixelFormat,
size_t  minConsumptionRatioThreshold = 25u 
)

Creates a StagingTexture which is required to upload data CPU -> GPU into a TextureGpu.

To download data GPU -> CPU see readRequest

Remarks
We try to find the smallest available texture (won't stall) that can fit the request.
Parameters
minConsumptionRatioThresholdValue in range [0; 100]. The smallest available texture we find may still be too big (e.g. you need to upload 64x64 texture RGBA8 and we return a 8192x8192x4 staging texture which is overkill). For these cases, here you can specify how much "is too big". For example by specifying a consumptionRatio of 50; it means that the data you asked for must occupy at least 50% of the space; otherwise we'll create a new StagingTexture.

A value of 100 means the StagingTexture must fit exactly (fully used). A value of 0 means any StagingTexture will do, no matter how large.

StagingTextures that haven't been using in a while will be destroyed. However if for some reason we end up returning a huge texture every frame for small workloads, we'll be keeping that waste potentially forever.

Returns
StagingTexture that meets the criteria. When you're done, remove it by calling removeStagingTexture.

◆ getVaoManager()

VaoManager* Ogre::TextureGpuManager::getVaoManager ( ) const

◆ hasPoolId()

bool Ogre::TextureGpuManager::hasPoolId ( uint32  poolId,
uint32  width,
uint32  height,
uint8  numMipmaps,
PixelFormatGpu  pixelFormat 
) const

◆ hasTextureResource()

bool Ogre::TextureGpuManager::hasTextureResource ( const String aliasName,
const String resourceGroup 
) const

Returns true if a texture with the given aliasName exists, or if a ResourceGroupListener provides such texture, or if such texture exists (i.e.

as a file) in the ResourceGroupManager.

This can return true regardless of whether the texture has been loaded or created.

Not to be confused with findTextureNoThrow which only looks for already created textures.

Parameters
aliasName
resourceGroup
Returns
True if there is such texture (loaded or not) False if there is no such texture

◆ importTextureMetadataCache()

void Ogre::TextureGpuManager::importTextureMetadataCache ( const String filename,
const char *  jsonString,
bool  bCreateReservedPools 
)

◆ isDoneStreaming()

bool Ogre::TextureGpuManager::isDoneStreaming ( ) const

Returns true if we're done loading all textures based on the return value of the last call to TextureGpuManager::_update and whether new tasks have been scheduled since then.

Remarks
Do NOT call this in a loop e.g.
// Do not do this
while( textureGpuManager->isDoneStreaming() )
Sleep( 1 );

Because it will spin forever! The return value of this function changes whenever TextureGpuManager::_update is called (directly or indirectly).

The main purpose for this function is to poll whether we're done streaming so that e.g. users can show/hide a loading screen or loading icon.

If you need to wait until all textures are done, use waitForStreamingCompletion

◆ notifyTextureChanged()

void Ogre::TextureGpuManager::notifyTextureChanged ( TextureGpu texture,
TextureGpuListener::Reason  reason,
void *  extraData 
)
overridevirtual

◆ removeStagingTexture()

void Ogre::TextureGpuManager::removeStagingTexture ( StagingTexture stagingTexture)

◆ reservePoolId()

TextureGpu* Ogre::TextureGpuManager::reservePoolId ( uint32  poolId,
uint32  width,
uint32  height,
uint32  numSlices,
uint8  numMipmaps,
PixelFormatGpu  pixelFormat 
)

Reserves and preallocates a pool with the given parameters Returns the master texture that owns the pool.

Destroy this pool with TextureGpuManager::destroyTexture

◆ saveTexture()

void Ogre::TextureGpuManager::saveTexture ( TextureGpu texture,
const String folderPath,
set< String >::type &  savedTextures,
bool  saveOitd,
bool  saveOriginal,
HlmsTextureExportListener listener 
)

◆ setDefaultMipmapGeneration()

void Ogre::TextureGpuManager::setDefaultMipmapGeneration ( DefaultMipmapGen::DefaultMipmapGen  defaultMipmapGen,
DefaultMipmapGen::DefaultMipmapGen  defaultMipmapGenCubemaps 
)

Whether to use HW or SW mipmap generation when specifying TextureFilter::TypeGenerateDefaultMipmaps for loading files from textures.

This setting has no effect for filters explicitly asking for HW mipmap generation.

Parameters
defaultMipmapGenWhether to enable HW mipmap generation for textures. Default is true.
defaultMipmapGenCubemapsWhether to enable HW mipmap generation for cubemap textures. Default is false.

◆ setMultiLoadPool()

void Ogre::TextureGpuManager::setMultiLoadPool ( uint32  numThreads)

OgreNext always performs background streaming to load textures in a worker thread.

However there is only ONE background thread performing all work serially while the main thread can do other stuff (like rendering, even if textures aren't ready).

There are times where you need to load many textures at once and having a threadpool of textures increases throughput.

The threadpool will load N textures at once into RAM, and then send them to the background thread to upload them to the GPU.

Once the background thread is ready, the main thread is signalled about the situation.

Enabling the MultiLoad pool can give performance benefits in the following scenarios:

  • You are IO limited: the threadpool gets stalled but in the meantime the background thread still loads what's ready in System RAM into GPU RAM
  • You are ALU/CPU limited: Loading PNG/JPG files needs decoding. Loading multiple at once is better use of CPU resources.

Enabling Multiload pool may increase memory consumption because you may end up with many images (more than numThreads) loaded in RAM.

You can change this value at any time, but it is an expensive call as we need to synchronize with the threadpool and flush all work.

Testing shows on an AMD Ryzen 5900X (12C/24T) loading of many PNG & JPG files of varying sizes (up to 2048x2048 RGBA8_UNORM) was cut from 1 second to 0.7 seconds in a Debug build.

However a Lenovo TB-X6C6X (4C/4T) Android tablet, the same workload cut from 5 seconds to 1 second, in a Release build.

Remarks
Because of the multithreaded nature, enabling this feature means textures may be loaded out of order.

Without this feature, normally textures would be loaded in order, unless there was an issue (e.g. file couldn't be found, raising an exception; the metadata cache was out of date, etc).

This can have an impact if you rely on the order (e.g. if you are using reservePoolId()). If you need to preserve ordering, you can use TextureGpu::scheduleTransition and set bSkipMultiload = true.

Testing indicates the ideal value is somewhere between 4-8 threads. More threads and you get diminishing returns.

Parameters
numThreadsHow many number of threads to use for loading multiple textures. 0 to disable this feature (Default).

◆ setProfileLoadingTime()

void Ogre::TextureGpuManager::setProfileLoadingTime ( bool  bProfile)

When enabled, we will profile the time it takes a texture to go from Resident to Ready and Log it.

Parameters
bProfileTrue to enable. False to disable profiling. Default value depends on Debug mode (default to true on Debug, false on Release)

◆ setStagingTextureMaxBudgetBytes()

void Ogre::TextureGpuManager::setStagingTextureMaxBudgetBytes ( size_t  stagingTextureMaxBudgetBytes)

At a high level, texture loading works like this:

  1. Grab a free StagingTexture from "available" pool in main thread
  2. Load image from file in secondary thread and fill the StagingTexture
  3. Copy from StagingTexture to final TextureGpu in main thread
  4. Release the StagingTexture so it goes back to "available" pool.
  5. Repeat from step 1 for next batch of images to load.
    All is well except for one little detail in steps 1 and 4: The StagingTexture released at step 4 can't become immediately available as the GPU could still be performing the copy from step 3, so we must wait a few frames until it's safe to map it again.
    That means at step 1, there may be StagingTextures in the "available" pool, yet however none of them are actually ready to grab; so we create a new one instead.
    In other words, if the CPU produces textures faster than the GPU can consume them, we may keep creating more and more StagingTextures until we run out of memory.
    That's where this function comes in. This function limits how much we let the "available" pool grow. If the threshold is exceeded, instead of creating a new StagingTexture at step 1; we'll begin to stall and wait for the GPU to catch up; so we can reuse these StagingTextures again. If no StagingTexture is capable of performing the upload (e.g. they're of incompatible format) we'll start deleting StagingTextures to make room for the one we need. The details are explained in checkStagingTextureLimits.
    This limit is tightly respected by Ogre but not a hard one. For example if you set the limit on 256MB and we require a StagingTexture of 326MB to load a very, very big texture, then Ogre has no other choice but to delete all textures in mAvailableStagingTextures and create one of 326MB that can perform the operation; but Ogre won't error out because 326 > 256MB. (though in such scenario the process may run out of memory and crash)
    Remarks
    This limit only counts for textures that are in zero-referenced in mAvailableStagingTextures. For example if you've set the limit in 256MB and you've created 1GB worth of StagingTextures (i.e. via getStagingTexture) and never released them via removeStagingTexture; those textures don't count. We only check the limit against the released textures in mAvailableStagingTextures.
    Parameters
    stagingTextureMaxBudgetBytesLimit in bytes, on how much memory we let in mAvailableStagingTextures before we start stalling the GPU and/or aggressively destroying them.

◆ setTextureGpuManagerListener()

void Ogre::TextureGpuManager::setTextureGpuManagerListener ( TextureGpuManagerListener listener)

Sets a new listener.

The old one will be destroyed with OGRE_DELETE See TextureGpuManagerListener. Pointer cannot be null.

◆ setTrylockMutexFailureLimit()

void Ogre::TextureGpuManager::setTrylockMutexFailureLimit ( uint32  tryLockFailureLimit)

The main thread tries to acquire a lock from the background thread, do something very quick, and release it.

If the lock failed to acquire, we try again next time _update is called. However if this happens too often in a row, we should stall and wait indefinitely for the background thread.

This function allows you to specify how many failures we have to let pass before we stall.

Remarks
This is a failsafe mechanism for edge case behaviors that should never happen. It is rare for the tryLock() to fail more than twice in a row.

However if loading a several big files (e.g. large cubemaps) or loading from a slow medium (e.g. from the internet directly) many tryLock() failures could be common.

If failure to acquire the lock is common and expected, small limit values could cause a lot of stutter, because e.g. a value of 3 could cause fps lag spikes every 3 frames.

A sensible value such as 1200 means that a stall would only happen after 20 seconds of repeated failure if running at constant 60 fps.

Parameters
tryLockFailureLimitHow many failures we have to wait for a stall, expressed in calls TextureGpuManager::_update. Usually there's one call to _update per frame, but there can be more. Use 0 to always stall Use std::numeric_limits<uint32>::max() for no failure limits (i.e. never stall)

◆ setWorkerThreadMaxPerStagingTextureRequestBytes()

void Ogre::TextureGpuManager::setWorkerThreadMaxPerStagingTextureRequestBytes ( size_t  maxPerStagingTextureRequestBytes)

The worker thread tracks how many data it is loading so the Main thread can request additional StagingTextures if necessary.

One big StagingTexture reduces the amount of time we map memory so we can upload.

However one big StagingTexture also means that if we've used 1 byte out of 200MB available, we have to wait until that byte has finished transferring (that usually means the StagingTexture becomes available 3 frames later); which can result in three big StagingTextures (one for each frame) which can be overkill.

This function allows you to specify when we decide to break these requests in smaller pieces, which by default is set at 64MB

Parameters
maxPerStagingTextureRequestBytes

◆ setWorkerThreadMaxPreloadBytes()

void Ogre::TextureGpuManager::setWorkerThreadMaxPreloadBytes ( size_t  maxPreloadBytes)

The worker thread first loads the texture from disk to RAM (aka "preload", and then copies from RAM to StagingTexture.

Later the main thread will copy from StagingTexture to actual texture.

This value controls how many bytes are preloaded (i.e. from disk to RAM) by the worker thread until the next _update call from the main thread is issued.
Higher values allows worker thread to keep loading textures while your main thread loads the rest of the scene. Lower values prevent Out of Memory conditions.
Remarks
Due to how the code works, this value will also affect how much StagingTexture we ask to the main thread (because preloading becomes a bottleneck).
Testing shows that very high values (i.e. >256MB) have the potential of uncovering driver bugs (even in 64-bit builds) and thus are not recommended.
The value is an approximation and not a hard limit. e.g. if loading a 128MB cubemap and the limit is 1 byte; then we'll preload 128MBs. But we won't be loading anything else. Also due to how the code works, there is some broad granularity issues that can cause us to consume a bit more.
Parameters
maxPreloadBytes

◆ setWorkerThreadMinimumBudget()

void Ogre::TextureGpuManager::setWorkerThreadMinimumBudget ( const BudgetEntryVec budget,
uint32  maxSplitResolution = 0 
)

Background streaming works by having a bunch of preallocated StagingTextures so we're ready to start uploading as soon as we see a request to load a texture from file.

If there is no minimum budget or it is too small for the texture you're trying to load, background threads can't start as soon as possible and has to wait until the next call to _update (or to waitForStreamingCompletion). This controls how much memory we reserve.
Remarks
Be careful on reserving too much memory, or else Out of Memory situations could arise. The amount of memory you can reserved is limited by the GTT (Graphics Translation Table) and the limit may be much lower than the total System RAM. For example my 16GB RAM system with a 2GB GPU, the GTT limit on Linux is of 3GB (use radeontop to find this information). See https://en.wikipedia.org/wiki/Graphics_address_remapping_table
Parameters
budgetArray of parameters for the staging textures we'll reserve. The budget can be empty.
maxSplitResolutionTextures bigger than this resolution in any axis will be taken as "exceptions" or "spikes" that won't last long. e.g. if maxSplitResolution = 2048 then a 2048x16, 67x2048, 2048x2048, and a 4096x4096 texture will all be considered abnormal.

This can significantly affect how much memory we consume while streaming. A value of 0 means to keep current value.

If an entry in the budget contains minNumSlices > 1 and minResolution >= maxSplitResolution then a lot of memory waste could end up being caused; thus we will warn to the Ogre.log if you set such setting.

The default value in 32-bit systems and mobile is 2048 The default value in 64-bit Desktop systems is 4096

This setting is closely related to setWorkerThreadMaxPerStagingTextureRequestBytes, because a texture whose resolution is >= maxSplitResolution will force us to use multiple StagingTextures, thus relieving the pressure on memory and memory fragmentation.

◆ shutdown()

void Ogre::TextureGpuManager::shutdown ( )

◆ waitForStreamingCompletion()

void Ogre::TextureGpuManager::waitForStreamingCompletion ( )

Blocks main thread until all pending textures are fully loaded.

Member Data Documentation

◆ mIgnoreSRgbPreference

bool Ogre::TextureGpuManager::mIgnoreSRgbPreference

While true, calls to createTexture & createOrRetrieveTexture will ignore and unset the TextureFlags::PrefersLoadingFromFileAsSRGB flag.

This is useful if user is not doing PBR, or working in its own colour space manually handled.

Default value is false

Remarks
PUBLIC VARIABLE. This variable can be altered directly. Changes are reflected immediately.

Changes will be reflected on new textures. Existing textures no longer possess the information to know whether they were created w/ PrefersLoadingFromFileAsSRGB

This value is not read nor write from the worker thread, thus it is thread-safe.

Textures may still be loaded as SRGB if they explicitly request SRGB e.g. texture->setPixelFormat( PFG_RGBA8_UNORM_SRGB ) was called or the texture is loaded from an OITD or DDS format specifically asking for PFG_RGBA8_UNORM_SRGB.

What this flag controls is that if we're loading a regular texture asking for PFG_RGBA8_UNORM like PNG (and other linear formats) then should we honour PrefersLoadingFromFileAsSRGB flag or not.


The documentation for this class was generated from the following file: