[2.1] Reloading textures

Discussion area about developing with Ogre-Next (2.1, 2.2 and beyond)


xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 169

[2.1] Reloading textures

Post by xrgo »

Hello! I am trying to reload an object's texture:

Code: Select all

        Ogre::HlmsPbsDatablock *datablock = static_cast<Ogre::HlmsPbsDatablock*>(
                                                        subitem->getDatablock() );
        datablock->getTexture(Ogre::PBSM_DIFFUSE)->reload();
but it just disappear, do I need to refresh something else?
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5433
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1341

Re: [2.1] Reloading textures

Post by dark_sylinc »

I'm afraid it doesn't work that way. The TexturePtr is an old interface that is manually filled by the HlmsTextureManager. When you call reload it won't go through HlmsTextureManager (and even if it did, it would have no way to determine which texture in particular to reload).

There is no way to reload at the moment. The steps taken in HlmsTextureManager::createOrRetrieveTexture would have to be repeated but using an mTextureArrays[mapType] that already exists (the one that controls the texture).

What may work is to call HlmsTextureManager::destroyTexture and immediately call HlmsTextureManager::createOrRetrieveTexture (beware it might return a different pointer).
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 169

Re: [2.1] Reloading textures

Post by xrgo »

Thank you, that's good enough for me. This is my code for reloading a texture:

Code: Select all

        Ogre::HlmsManager *hlmsManager = Ogre::Root::getSingletonPtr()->getHlmsManager();
        Ogre::HlmsTextureManager *hlmsTextureManager = hlmsManager->getTextureManager();

        hlmsTextureManager->destroyTexture("texture.dds");

        Ogre::HlmsTextureManager::TextureLocation texLocation = hlmsTextureManager->createOrRetrieveTexture( "texture.dds", Ogre::HlmsTextureManager::TEXTURE_TYPE_DIFFUSE);

        Ogre::HlmsPbsDatablock *datablock = static_cast<Ogre::HlmsPbsDatablock*>( subitem->getDatablock() );
        datablock->setTexture( Ogre::PBSM_DIFFUSE, texLocation.xIdx, texLocation.texture );
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 169

Re: [2.1] Reloading textures

Post by xrgo »

Now I have another problem. To reload the texture I need to know its file name, but if I do:

Code: Select all

        Ogre::TextureManager::ResourceMapIterator textureResourceMapIterator = Ogre::TextureManager::getSingleton().getResourceIterator();
        while (textureResourceMapIterator.hasMoreElements())
        {
            Ogre::ResourcePtr texturePtr = textureResourceMapIterator.getNext();
            std::cout<<texturePtr->getName()<<std::endl;
        }
I get names like

Code: Select all

[Hash 0xeb2bfd33]
[Hash 0xd84c7c3a]
[Hash 0x296098eb]
[Hash 0x594d701c]
[Hash 0xf16e7c5b]
[Hash 0x9b4ce5aa]
[Hash 0xa7121903]
HlmsTextureManager/0
HlmsTextureManager/1
HlmsTextureManager/2
HlmsTextureManager/3
HlmsTextureManager/4
HlmsTextureManager/5
HlmsTextureManager/6
HlmsTextureManager/7
HlmsTextureManager/8
HlmsTextureManager/9
HlmsTextureManager/10
HlmsTextureManager/11
HlmsTextureManager/12
HlmsTextureManager/13
rtt/140084109256816/[Hash 0x83fe644c]
I think the ones that has "HlmsTextureManager" are the ones that my item uses, but how do I translate this names to the file name, or where else can I get the file name??

If I try to get it from the datablock
datablock->getTexture(Ogre::PBSM_DIFFUSE)->getName()
its the same

Thanks in advance!

EDIT:
I am doing a semi-automated texture reload so I need to do one of this:

1. check for every texture in HlmsTextureManager if that file has changed. For that I need to know the file name
2. watch my textures directory, and when a texture file changed update the datablocks that are using this texture file. For this I have the texture file name, but I need to search which datablock is using it
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 169

Re: [2.1] Reloading textures

Post by xrgo »

any particular reason why in OgreHlmsTextureManager.cpp line ~506

this:

Code: Select all

                textureArray.texture = TextureManager::getSingleton().createManual(
                                            "HlmsTextureManager/" +
                                            StringConverter::toString( mTextureId++ ),
                                            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
                                            texType, width, height, depth, numMipmaps - baseMipLevel,
                                            defaultPixelFormat == PF_UNKNOWN ? imageFormat :
                                                                               defaultPixelFormat,
                                            TU_DEFAULT & ~TU_AUTOMIPMAP, 0,
                                            mDefaultTextureParameters[mapType].hwGammaCorrection,
                                            0, BLANKSTRING, false );
cant be this:

Code: Select all

                textureArray.texture = TextureManager::getSingleton().createManual(
                                            texName,              //<<<<<<<<<< actual file name
                                            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
                                            texType, width, height, depth, numMipmaps - baseMipLevel,
                                            defaultPixelFormat == PF_UNKNOWN ? imageFormat :
                                                                               defaultPixelFormat,
                                            TU_DEFAULT & ~TU_AUTOMIPMAP, 0,
                                            mDefaultTextureParameters[mapType].hwGammaCorrection,
                                            0, BLANKSTRING, false );
??? I did it and it solves my problem, but not sure if it breaks something
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5433
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1341

Re: [2.1] Reloading textures

Post by dark_sylinc »

xrgo wrote:any particular reason why in OgreHlmsTextureManager.cpp line ~506
Yes. Re-read the manual section about the HlmsTextureManager, then re-read my post again:
The TexturePtr is an old interface that is manually filled by the HlmsTextureManager
The TexturePtr may contain "tens of textures in the same texture". A lot of things can break if you assign a name of an actual texture to these automatically generated textures.

The TexturePtr doesn't have enough information to retrieve the name. You need the data in TextureLocation (basically the pointer & the xIdx for texture arrays, and the pointer, xIdx, yIdx & divisor for texture atlases) to get all the required information; and compare it to the entires in HlmsTextureManager::mTextureArrays until you find a match (there is not a function to do this for you although this would be trivial to do).
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 169

Re: [2.1] Reloading textures

Post by xrgo »

dark_sylinc wrote:The TexturePtr may contain "tens of textures in the same texture"
Doh, I alwaays knew that, I dont know what I was thinking xD.

I finally solved my problem like this:

Since I am creating all my materials by code, I save the hlms texture name that my texture is located in a map:

Code: Select all

                    Ogre::HlmsTextureManager::TextureLocation texLocation = hlmsTextureManager->createOrRetrieveTexture( fileName, Ogre::HlmsTextureManager::TEXTURE_TYPE_MONOCHROME);
                    datablock->setTexture( Ogre::PBSM_DIFFUSE, texLocation.xIdx, texLocation.texture );

                    textureName[fileName] = texLocation.texture->getName();
so later I can refresh my Item's datablock:

Code: Select all

            Ogre::HlmsPbsDatablock *datablock = static_cast<Ogre::HlmsPbsDatablock*>( Item->getSubItem(0)->getDatablock() );
            if( !datablock->getTexture(Ogre::PBSM_DIFFUSE).isNull() &&
                    datablock->getTexture(Ogre::PBSM_DIFFUSE)->getName() == textureName.at(textureFileName) ){

                hlmsTextureManager->destroyTexture(textureFileName);
                Ogre::HlmsTextureManager::TextureLocation texLocation texLocation = hlmsTextureManager->createOrRetrieveTexture( textureFileName, Ogre::HlmsTextureManager::TEXTURE_TYPE_DIFFUSE);
                datablock->setTexture( Ogre::PBSM_DIFFUSE, texLocation.xIdx, texLocation.texture );
                textureName[textureFileName] = texLocation.texture->getName()); //update texture name 
            }
Thank you very much!!
darkheron
Kobold
Posts: 28
Joined: Fri Aug 29, 2014 9:05 pm

Re: [2.1] Reloading textures

Post by darkheron »

I am bumping this up because in the course of my own foray into reloading textures, I have come across what I believe to be a bug.

In HlmsTextureManager.cpp line 596 inside of the void HlmsTextureManager::destroyTexture( IdString aliasName ) method specifically this section of code

Code: Select all

  void HlmsTextureManager::destroyTexture( IdString aliasName )
    {
        TextureEntry searchName( aliasName );
        TextureEntryVec::iterator it = std::lower_bound( mEntries.begin(), mEntries.end(), searchName );

        if( it != mEntries.end() && it->name == searchName.name )
        {
            mEntries.erase( it );  <--------------------- THIS INVALIDATES THE ITERATOR it

            TextureArrayVec::iterator texArrayIt = mTextureArrays[it->mapType].begin() + it->arrayIdx; <--- BUT STILL WE USE IT
            texArrayIt->destroyEntry( it->entryIdx );  <----------- AND AGAIN

            if( texArrayIt->activeEntries == texArrayIt->maxTextures )
            {
                //The whole array has no actual content. Destroy the texture.
                ResourcePtr texResource = texArrayIt->texture;
                TextureManager::getSingleton().remove( texResource );
                texArrayIt = efficientVectorRemove( mTextureArrays[it->mapType], texArrayIt );  <-------- AND AGAIN

                if( texArrayIt != mTextureArrays[it->mapType].end() ) <-------------- AND AGAIN
                {
                    //The last element has now a new index. Update the references in mEntries
                    size_t newArrayIdx = it->arrayIdx;  <--------------------- YEP, AGAIN :)
                    StringVector::const_iterator itor = texArrayIt->entries.begin();
                    StringVector::const_iterator end  = texArrayIt->entries.end();

                    while( itor != end )
                    {
                        searchName.name = *itor;
                        it = std::lower_bound( mEntries.begin(), mEntries.end(), searchName );
                        assert( it != mEntries.end() && it->name == searchName.name );
                        it->arrayIdx = newArrayIdx;
                        ++itor;
                    }
                }
            }
        }
    }
Specifically, when destroying a texture, the operation would work correctly once, but then because there is trash inside of the value pointed to by the entity iterator, the cleanup fails, (as evidenced by data dump from pHlmsTextureManager->dumpMemoryUsage();) and a subsequent destroyTexture operation will cause an abortion. By simply caching the values we need into local variables like so :

Code: Select all

uint16 arrayIdx = it->arrayIdx;
uint16 entryIdx = it->entryIdx;
TextureMapType mapType = it->mapType;
mEntries.erase( it );
and using the locals in place of the derefrenced iterator, everthing seems to work excellent, and I can reload my textures at runtime ad nauseam with no error, and a perfect HlmsTextureManager memory dump.

I believe this is a bug, but I thought I should raise the issue here to be sure.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5433
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1341

Re: [2.1] Reloading textures

Post by dark_sylinc »

Fixed. Thanks. It was a serious bug (To be honest... I must've been high when I wrote that, I can't believe I wrote that buggy abomination).
Wasn't the only thing failing if you look at the commit!

Cheers and thanks,
Matias
darkheron
Kobold
Posts: 28
Joined: Fri Aug 29, 2014 9:05 pm

Re: [2.1] Reloading textures

Post by darkheron »

patched up, but now on a deletion the assert at line 619:

Code: Select all

assert( itEntry != mEntries.end() && itEntry->name == searchName.name );
Triggers. A quick run through the debugger seems to suggest that the StringVector iterator itor will eventually return a NULL string, since there are actually more entries in the vector then are actually used, so itEntry->name != seachName.name. Maybe you mean to iterate from 0..texArrayIt->activeEntries here?

Just a heads up. I can dig into it more later if you like, but for now it's off to work.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5433
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1341

Re: [2.1] Reloading textures

Post by dark_sylinc »

My subconscious kept telling me there was still a bug there! Thanks!

Hopefully, it's fixed for good this time.
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 169

Re: [2.1] Reloading textures

Post by xrgo »

another (maybe) related question:

How can I resize a texture? or load it scaled or something like that. What I need:
I have some really big textures (4k) and I want to be able to reduce the size, for instance to 512 to be able to run my app in graphics cards with little VRAM.
Is that possible? I know I can have "duplicated" files with lower resolution, but would be great if I could o it by code.
Thanks!
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5433
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1341

Re: [2.1] Reloading textures

Post by dark_sylinc »

The HlmsTextureManager will already try to downscale the texture when looking at RenderSystemCapabilities::getMaximumResolutionXD (where X is a number).

Though what you're saying is about downscaling due to VRAM limits, rather than texture resolution ones. But the code should be the same.
Note however this feature hasn't been thoroughly tested. I've ran into a few crashes on some textures that I didn't check worry too much at the time. You may want to try it for yourself by artificially changing the maximum resolution or slightly changing OGRE's code.
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 169

Re: [2.1] Reloading textures

Post by xrgo »

dark_sylinc wrote:The HlmsTextureManager will already try to downscale the texture when looking at RenderSystemCapabilities::getMaximumResolutionXD (where X is a number).
Thank you, I tried forcing a max res in the HlmsTextureManager and it worked, but now I want to do it inside my code, I tried setting up a custom RenderSystemCapabilities, but I am having some trouble.
If I do this:

Code: Select all

Ogre::RenderSystemCapabilities *caps = renderSys->createRenderSystemCapabilities();
before creating my windows it crashes at that line, but if I do it after I get an error here:

Code: Select all

renderSys->useCustomRenderSystemCapabilities(caps);
this error:

Code: Select all

Custom render capabilities must be set before the RenderSystem is initialised
any ideas?
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 169

Re: [2.1] Reloading textures

Post by xrgo »

any ideas why customRenderSystemCapabilities is not working?
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 169

Re: [2.1] Reloading textures

Post by xrgo »

Hello, I came up with a solution that I like a lot! I noticed that samplerBlocks had a "mMinLod" and if I set any number but 0, like 4, I do get lower resolution textures! but no memory consumption decrease =(
So I take that idea an modified the method "createOrRetrieveTexture" a little to accept a last parameter "minLod":

Code: Select all

TextureLocation createOrRetrieveTexture( const String &texName, TextureMapType mapType, float minLod=0 );
so then after it sees if it needs to resize the texture (around line 425) I do:

Code: Select all

............
                    width  = static_cast<uint>( floorf( maxResolution * aspectRatio ) );
                        height = maxResolution;
                    }

                    image.resize( width, height );
                }
            }

            //New resize!! when asked
            if( minLod!=0 ){
                    baseMipLevel = 0;
                    while( baseMipLevel < minLod
                           && (baseMipLevel <= image.getNumMipmaps()) )
                    {
                        width  >>= 1;
                        height >>= 1;
                        ++baseMipLevel;
                    }
            }

            //Find an array where we can put it. If there is none, we'll have have to create a new one
            TextureArrayVec::iterator dstArrayIt = findSuitableArray( mapType, width, height, depth,
                                                                      faces, imageFormat,
                                                                      numMipmaps - baseMipLevel );
.........
Using this method I do get lower resolution textures and lower the memory usage!!! and I can choose the kind of texture, or which texture I want to reduce quality =)
For example now in my app I have a quality selector: in "High" texture memory usage is like 1.5gb, and in "Low" is like 0.5gb.
The only problem is that it needs mipmaps, but all my textures already have it so not an issue for me

Is there any chances something like this gets implemented in to Ogre?

Thanks!!
Last edited by xrgo on Thu May 28, 2015 4:21 pm, edited 1 time in total.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5433
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1341

Re: [2.1] Reloading textures

Post by dark_sylinc »

xrgo wrote:For example now in my app I have a quality selector: in "High" texture memory usage is like 1.5gb, and in "Low" is like 0.5gb.
The only problem is that it needs mipmaps, but all my textures already have it so not an issue for me

Is there any chances something like this gets implemented in to Ogre?

Thanks!!
Call image.generateMipmaps; but beware it won't work with all formats (e.g. compressed formats). Returns false if mipmaps weren't generated. Also previous mipmaps will be discarded (e.g. if already embedded in the file)

The idea is quite clever btw. I was thinking of something similar, but I like yours better.
xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 169

Re: [2.1] Reloading textures

Post by xrgo »

dark_sylinc wrote:The idea is quite clever btw. I was thinking of something similar, but I like yours better.
Any chances to see this (or similar) integrated with Ogre?? :roll:

And another related topic:
lurking some information on another forums I found this: (http://www.gamedev.net/topic/669411-wha ... try5236291)
and I liked this Idea very much:
Stainless wrote:One trick I use a lot is to record the highest mipmap ever used on a texture. It's complicated to get the detection to work, but it can be done. Then I can generate a report and flag any large textures that are never used at full resolution.
Is something like this possible in Ogre 2.1? I dont need it now, but I think is very interesting and useful

Thanks!
User avatar
Zonder
Ogre Magi
Posts: 1172
Joined: Mon Aug 04, 2008 7:51 pm
Location: Manchester - England
x 76

Re: [2.1] Reloading textures

Post by Zonder »

xrgo wrote:
dark_sylinc wrote:
Stainless wrote:One trick I use a lot is to record the highest mipmap ever used on a texture. It's complicated to get the detection to work, but it can be done. Then I can generate a report and flag any large textures that are never used at full resolution.
Is something like this possible in Ogre 2.1? I dont need it now, but I think is very interesting and useful

Thanks!

+1
There are 10 types of people in the world: Those who understand binary, and those who don't...