[2.3] Getting assertion Failure on HlmsManager::getBasicBlock

Problems building or running the engine, queries about how to use features etc.
Post Reply
psysu
Halfling
Posts: 72
Joined: Tue Jun 01, 2021 7:47 am
x 6

[2.3] Getting assertion Failure on HlmsManager::getBasicBlock

Post by psysu »

Ogre Version: 2.3
Operating System: Win10
Render System: DirectX 11

Hi, im getting an OGRE_ASSERT_LOW failure on HlmsManager::getBasicBlock when i create a Datablock( when retrieving macroblock for the datablock to be precise). can someone explain why i am getting this, Thanks.

Code: Select all

template <typename T, HlmsBasicBlock type, size_t maxLimit>
    T* HlmsManager::getBasicBlock( typename vector<T>::type &container, const T &baseParams )
    {
        assert( mRenderSystem && "A render system must be selected first!" );
        assert( baseParams.mBlockType == type &&
                "baseParams.mBlockType should always be BLOCK_MACRO or BLOCK_BLEND! "
                "You can ignore this assert,  but it usually indicates memory corruption"
                "(or you created the block without its default constructor)." );

    typename vector<T>::type::iterator itor = std::find( container.begin(), container.end(),
                                                         baseParams );

    if( itor == container.end() )
    {
        OGRE_ASSERT_LOW( container.size() <= maxLimit &&
                         "Exceeded the max number of blocks that can be created during "
                         "the lifetime of an application!!!");
        container.push_back( baseParams );
        container.back().mRefCount   = 0;
        container.back().mId         = std::numeric_limits<uint16>::max();
        container.back().mLifetimeId = static_cast<uint16>( container.size() - 1u );
        container.back().mBlockType  = type;
        itor = container.end() - 1u;
    }

    if( !itor->mRefCount )
    {
        OGRE_ASSERT_LOW( itor->mId == std::numeric_limits<uint16>::max() ); // assertion failure
        const size_t idx = getFreeBasicBlock( type, &*itor );
        itor->mId = static_cast<uint16>( idx );
    }

    return &(*itor);
}

im only getting this assertion failure when i create lot of datablocks ( like more than 10,000 subMeshes. each one has its own unique datablock ). Heres how i create a Datablock for each SubMesh.

Code: Select all

	Ogre::HlmsManager* pHlmsManager = Ogre::Root::getSingleton().getHlmsManager();
	Ogre::HlmsPbsDatablock* pDataBlock = static_cast<Ogre::HlmsPbsDatablock*>( pHlmsManager->getDatablockNoDefault(sMaterialName) );
	if( !pDataBlock )
	{
		/*
		... 
			retrieving fTransperncy 
		...
		*/
		Ogre::HlmsBlendblock blend_block = Ogre::HlmsBlendblock{};
		Ogre::HlmsMacroblock macro_block = Ogre::HlmsMacroblock{};
		if(fTransparency>0.00001)
		{ 
			blend_block.setBlendType( Ogre::SceneBlendType::SBT_TRANSPARENT_ALPHA );
			macro_block.mDepthWrite = false;
		}	
		macro_block.mCullMode = Ogre::CullingMode::CULL_NONE;

	Ogre::HlmsPbs* pHlmsPbs = static_cast<Ogre::HlmsPbs*>( pHlmsManager->getHlms( Ogre::HlmsTypes::HLMS_PBS ) );
	pDataBlock = static_cast<Ogre::HlmsPbsDatablock*>( pHlmsPbs->createDatablock(sMaterialName, sMaterialName, macro_block, blend_block, {} ) ); // Getting Assertion Failure during this operation
	
	/**
	...
		Setting Other Material Properties 
	...
	**/
}
return pDataBlock;
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5298
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1279
Contact:

Re: [2.3] Getting assertion Failure on HlmsManager::getBasicBlock

Post by dark_sylinc »

This looks like an OgreNext bug.

Are you able to repro that bug inside of the samples? It would be much faster if we have a consistent repro.

psysu
Halfling
Posts: 72
Joined: Tue Jun 01, 2021 7:47 am
x 6

Re: [2.3] Getting assertion Failure on HlmsManager::getBasicBlock

Post by psysu »

Alright I'm getting this assertion failure in my application when I set cullmode to CULL_NONE in macroblock. When I used the default culling mode it works.

I tried reproducing this behaviour in the Ogre-Next PbsMaterials sample, by creating 30,000 datablocks for each "Sphere1000.mesh" Item. I don't get this assertion failure in the Ogre-Next sample though.

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5298
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1279
Contact:

Re: [2.3] Getting assertion Failure on HlmsManager::getBasicBlock

Post by dark_sylinc »

I can't reproduce it either.

Therefore I can only tell you this:

I've seen this kind of problems when there is some sort of ABI mismatch (e.g. Debug vs Release, NDEBUG was defined when Ogre was built, but your app does not define or viceversa; one of the DLLs being loaded is actually older/newer, the *.h header file belongs to an older/newer version, you are using the wrong OgreBuildSettings.h).

We added 'generateAbiCookie' in OgreNext 3.0 for this reason, to try to detect and minimize ABI mismatch errors.

With an ABI mismatch, the HlmsMacroblock or HlmsBlendblock you pass to Ogre may end up having garbage values because there is an extra or a missing variable/padding (often sizeof(HlmsMacroblock) is different between your app and the DLLs). This could explain what you're seeing.

Otherwise the only thing I can tell you is that you step into mHlmsManager->getMacroblock / mHlmsManager->getBlendblock (I don't know which one is causing the exception, because you didn't mention) which ends up inside HlmsManager::getBasicBlock.

The algorithm is very simple:

HlmsManager::getBasicBlock receives baseParams (which can be either HlmsMacroblock or HlmsBlendblock) and performs a linear search to see if another block with the same parameters had already been created:

Code: Select all

typename vector<T>::type::iterator itor =
            std::find( container.begin(), container.end(), baseParams );

std::find relies on operators HlmsMacroblock::!= and HlmsBlendblock::!= to perform the comparison.

Thus obviously, if you feed the same HlmsMacroblock/HlmsBlendblock yet the if( itor == container.end() ) returns true; dig into the operator to understand why the code thinks the two allegedly-equal blocks are not equal.

Most likely one of the variables is containing garbage. Watchout for booleans containing a value other than 0 or 1. When something gets corrupted, they will contain arbitrary numbers and they will appear in the debugger's variable watch.

Cheers
Matias

psysu
Halfling
Posts: 72
Joined: Tue Jun 01, 2021 7:47 am
x 6

Re: [2.3] Getting assertion Failure on HlmsManager::getBasicBlock

Post by psysu »

Thanks matias for the detailed answer. i'll look into this in our application.

a bit unrelated question to this, me and my team are using Ogre 1.X engine for CAD and CAE applications, which deals with lot of meshes ( sometimes more than 10,000, 30,000 ) and each of them have its own material properties. We are currently prototyping our application with Ogre-Next engine, what are the limits of the Ogre-Next engine, like how many unique meshes and datablocks can be created.

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5298
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1279
Contact:

Re: [2.3] Getting assertion Failure on HlmsManager::getBasicBlock

Post by dark_sylinc »

psysu wrote: Thu Oct 06, 2022 5:39 am

me and my team are using Ogre 1.X engine for CAD and CAE applications, which deals with lot of meshes ( sometimes more than 10,000, 30,000 ) and each of them have its own material properties. We are currently prototyping our application with Ogre-Next engine, what are the limits of the Ogre-Next engine, like how many unique meshes and datablocks can be created.

There shouldn't be any (other than the limits of what GPU RAM lets you*). You may stress our memory manager, so let us know if you run into problems.

*Considering each PBS datablock is around 300 bytes (I'm rounding up, I'm remembering off the top of my head, it's around 256 bytes), 30k materials should be around 8.5MB, so that shouldn't be a problem. As for the submeshes, that depends on how many vertices you need.

You should look at Samples/2.0/Tutorials/Tutorial_Memory on how to control memory. Particularly initMiscParamsListener (i.e. VaoManager::VERTEX_IMMUTABLE & co.). Very small memory pools (a value of 0 is allowed, that just means each buffer gets its own pool) in the VaoManager will negatively affect performance, as the OS allocator becomes the memory manager; and OgreNext can't batch draw calls together either.
Very big pools may waste memory; specially if fragmentation begins to appear.

What's the appropriate value depends on your own usage. You'll have to monitor RAM & VRAM usage (use Process Monitor, Vendor Tools like GPUPerfAPI, getMemoryStats as demonstrated in MemoryGameState::generateDebugText) until you strike the right balance.

If you e.g. you create 30.000 (sub)meshes, but then you delete 15.000 of them to make room for creating 12.000 more (making it a total of 28.000), fragmentation could be a problem and thus calling sceneManager->shrinkToFitMemoryPools() & vaoManager->cleanupEmptyPools() would be highly recommended.

But if you delete almost all submeshes, and then start creating more; fragmentation won't be a problem at all.

One more thing: This was only an issue with NVIDIA drivers and 32-bit applications, NVIDIA driver will happily queue up our commands infinitely until it feels it has enough, and eventually run out of memory. This wasn't a problem with Ogre1 because ironically due to not being as optimized, NVIDIA driver was every now and then forced to flush those queued commands.

We haven't encountered this problem at all in 64-bit apps; but with so many submeshes I fear you might run into it. I wrote RenderSystem::_clearStateAndFlushCommandBuffer() to force the drivers to flush those commands and OgreNext does sometimes call it (in TextureGpuManager::checkStagingTextureLimits). If you get crashes where you suspect running out of memory is the cause, randomly calling _clearStateAndFlushCommandBuffer() before the crash might fix them.

But of course don't call it unnecessarily because it's an expensive call, it causes a full flush.

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5298
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1279
Contact:

Re: [2.3] Getting assertion Failure on HlmsManager::getBasicBlock

Post by dark_sylinc »

D'oh! About the limits you encountered in this post:

HlmsMacroblock/HlmsBlendblock/HlmsSamplerblock have two limits:

1) It's reference counted. BasicBlock::mRefCount is 16-bit; thus it may overflow. If you encounter this problem, changing it to a uint32 will fix it.

2) There is a limit in the number of IDs. BasicBlock::mId. Each HlmsMacroblock that is different will have a different ID.
There can only be OGRE_HLMS_NUM_MACROBLOCKS live different HlmsMacroblocks.
And throughout the lifetime of the application only OGRE_HLMS_MAX_LIFETIME_MACROBLOCKS different HlmsMacroblocks can be created (i.e. even if you destroy the HlmsMacroblocks, we still keep them around internally).

The OGRE_HLMS_NUM_MACROBLOCKS macro can be increased if this number is too low.
As for OGRE_HLMS_MAX_LIFETIME_MACROBLOCKS, I'm afraid on D3D11 that number (4096) is the hardlimit by the API. However the other APIs may not have such limitations.

(the same goes for the _BLENDBLOCKS and _SAMPLERBLOCKS macros)

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5298
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1279
Contact:

Re: [2.3] Getting assertion Failure on HlmsManager::getBasicBlock

Post by dark_sylinc »

I just realized the error you are getting (the assert) can be caused by integer overflow (it's also possible there's a leak due to a missing call to HlmsManager::destroyMacroblock, which decreases the reference count).

Change uint16 mRefCount; to uint32 in struct _OgreExport BasicBlock in OgreMain/include/OgreHlmsDatablock.h

psysu
Halfling
Posts: 72
Joined: Tue Jun 01, 2021 7:47 am
x 6

Re: [2.3] Getting assertion Failure on HlmsManager::getBasicBlock

Post by psysu »

Change uint16 mRefCount; to uint32 in struct _OgreExport BasicBlock in OgreMain/include/OgreHlmsDatablock.h

yeap this solved the issue. Weirdly enough this also solves the issue I get from the original code, I get an exception when I load huge number of submeshes and shutdown Ogre-Next engine context:

Code: Select all

OGRE EXCEPTION(1:InvalidStateException): This macroblock has already been destroyed! in HlmsManager::destroyMacroblock

For example, I got this exception when I load 30.632 submeshes and datablocks.

Post Reply