automatic mipmap generation Topic is solved

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


jwwalker
Goblin
Posts: 248
Joined: Thu Aug 12, 2021 10:06 pm
Location: San Diego, CA, USA
x 18

automatic mipmap generation

Post by jwwalker »

I have some textures that I am loading "manually", but I would like to get mipmaps generated automatically if that is possible. When I tried calling TextureGpu::_autogenerateMipmaps right after creating the texture, I got an assertion failure

Code: Select all

-[MTLDebugBlitCommandEncoder generateMipmapsForTexture:]:1015: failed assertion `tex must not be nil.'
When I called TextureGpu::_autogenerateMipmaps after loading from a StagingTexture, I got a different assertion failure:

Code: Select all

-[MTLDebugBlitCommandEncoder generateMipmapsForTexture:]:1026: failed assertion `[tex mipmapLevelCount](1) must be > 1.'
Then I tried calling TextureGpu::setNumMipmaps, and got an assertion failure complaining that the residency status should be OnStorage but is actually Resident.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5436
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1343

Re: automatic mipmap generation

Post by dark_sylinc »

Hi!

Post a bit of code to see what you're doing, and what is the texture for (e.g. is data loaded from disk once? should the mipmaps be generated very often like every frame or just onc is data loaded from memory?)

Cheers
jwwalker
Goblin
Posts: 248
Joined: Thu Aug 12, 2021 10:06 pm
Location: San Diego, CA, USA
x 18

Re: automatic mipmap generation

Post by jwwalker »

Here's my code to create a texture from an image in memory:

Code: Select all

Ogre::TextureGpu* _Nullable CreateTexture(
    const char* _Nonnull inName,
    const std::vector<unsigned char>& inPixelData,
    size_t inWidth,
    size_t inHeight,
    size_t inBytesPerRow )
{
    Ogre::TextureGpu* texture = nullptr;
    Ogre::RenderSystem *renderSystem = Ogre::Root::getSingleton().getRenderSystem();
    Ogre::TextureGpuManager *textureManager = renderSystem->getTextureGpuManager();

    texture = textureManager->createTexture( Ogre::String(inName),
        Ogre::GpuPageOutStrategy::SaveToSystemRam,
        Ogre::TextureFlags::ManualTexture,
        Ogre::TextureTypes::Type2DArray,
        Ogre::BLANKSTRING,
        Ogre::TextureFilter::TypeGenerateDefaultMipmaps );
    if (texture != nullptr)
    {
        texture->setPixelFormat( Ogre::PFG_RGBA8_UNORM_SRGB );
        texture->setTextureType( Ogre::TextureTypes::Type2DArray );
        texture->setResolution( (Ogre::uint32) inWidth,
            (Ogre::uint32) inHeight );
        
        if (texture->getResidencyStatus() == Ogre::GpuResidency::OnStorage)
        {
            texture->scheduleTransitionTo( Ogre::GpuResidency::Resident, nullptr );
        }
        
        Ogre::StagingTexture* stagingTexture = textureManager->getStagingTexture(
            (Ogre::uint32) inWidth, (Ogre::uint32) inHeight, 1u, 1u,
            texture->getPixelFormat() );
        stagingTexture->startMapRegion();
        Ogre::TextureBox texBox = stagingTexture->mapRegion(
            (Ogre::uint32) inWidth,
            (Ogre::uint32) inHeight, 1u, 1u, texture->getPixelFormat() );
        texBox.copyFrom( const_cast<unsigned char*>(inPixelData.data()),
            (Ogre::uint32) inWidth,
            (Ogre::uint32) inHeight, (Ogre::uint32) inBytesPerRow );
        stagingTexture->stopMapRegion();
        stagingTexture->upload( texBox, texture, 0, nullptr, nullptr, true );
        textureManager->removeStagingTexture( stagingTexture );
    }
    
    return texture;
}
(Hmm, now that I look at that again, maybe I should try setting the number of mipmaps before the scheduleTransitionTo call.)

There could be many textures loaded this way, some coming from data within 3D object files, and some coming from image files (e.g. JPEG) on disk. New textures can be loaded at unpredictable times, not just when the program starts but certainly not every frame. I would imagine that mipmaps for a texture should be generated soon after that texture is loaded.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5436
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1343

Re: automatic mipmap generation

Post by dark_sylinc »

jwwalker wrote: Sun Jan 16, 2022 4:41 am (Hmm, now that I look at that again, maybe I should try setting the number of mipmaps before the scheduleTransitionTo call.)
Yes, that's exactly the reason setNumMipmaps complained. It needs to be done while the texture is (still) OnStorage
jwwalker wrote: Sun Jan 16, 2022 4:41 am There could be many textures loaded this way, some coming from data within 3D object files, and some coming from image files (e.g. JPEG) on disk. New textures can be loaded at unpredictable times, not just when the program starts but certainly not every frame. I would imagine that mipmaps for a texture should be generated soon after that texture is loaded.
OK. The reason I asked frequency is because to call _autogenerateMipmaps you need TextureFlags::RenderToTexture | TextureFlags::AllowAutomipmaps flags (see _autogenerateMipmaps docs).

This has a performance cost, but it's ok if you plan on calling _autogenerateMipmaps often (e.g. every frame).

For regular textures (i.e. uploaded once, read all the time afterwards) the recommended path is to create a temporary 2nd texture, call _autogenerateMipmaps there; and then copy the data to the original texture and destroy the temporary 2nd texture (this is what the filter does).

Now you have 4 possible solutions:

1. Do it yourself, _autogenerateMipmaps on the same texture. Best for textures that will be changing often. What you're doing now, but the texture needs TextureFlags::RenderToTexture | TextureFlags::AllowAutomipmaps. You're already very close.

2. Do it yourself, _autogenerateMipmaps on a temporary texture. See GenerateHwMipmaps::_executeSerial for example code

3. Let Ogre do it via a ResourceLoadingListener registered in ResourceGroupManager and overriding ResourceLoadingListener::grouplessResourceExists grouplessResourceLoading and grouplessResourceOpened. That way Ogre can load it asynchronously but instead of a file, the data is sourced from the listener. This allows you for async streaming.
You'll have to create the texture NOT as manual texture; but rather pretending the texture is a regular one coming from a file (i.e. AutomaticBatching, Type2D), and using filenames to identify them in the listener.

4. Let Ogre do it, pretend you're loading a regular texture from file; but when you call scheduleTransitionTo, you provide your own Image instead of nullptr. eg. see AreaApproxLightsGameState::createAreaMask

Take note that if D3D11 device is lost (and Vulkan too, but Vulkan currently isn't handling it) automatic recovery won't be possible except for Option 3.
You can still listen for device lost events and manually recover though.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5436
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1343

Re: automatic mipmap generation

Post by dark_sylinc »

I forgot to mention: right now you're requesting a mipmap filter, but because you're asking for a ManualTexture, it's being ignored.

Filters currently will only work with AutomaticBatching textures
jwwalker
Goblin
Posts: 248
Joined: Thu Aug 12, 2021 10:06 pm
Location: San Diego, CA, USA
x 18

Re: automatic mipmap generation

Post by jwwalker »

For now I've chosen option 2, and it works, but thanks for all the options! :D