hlms texture units numbering

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


User avatar
bishopnator
Gnome
Posts: 328
Joined: Thu Apr 26, 2007 11:43 am
Location: Slovakia / Switzerland
x 16

hlms texture units numbering

Post by bishopnator »

Hi, I am trying to integrate a single texture to my custom hlms and trying to understand the code in HlmsPbs. I see that in HlmsPbs::calculateHashForPreCreate some texture are set to fixed texture units - the indices are defined by PBSM_xxx values and set in HlmsPbs::calculateHashForPreCreate through setTextureProperty, but there are some which are assigned dynamically - like ssrTexture or gBuf_depthTexture. I don't see how it can possible work with this dynamic assignment. In HlmsPbs::notifyPropertiesMergedPreGenerationStep() the texUnit member is initialized to some starting offset and then incremented according to some properties.

  • let's have there renderable A with properties HlmsBaseProp::UsePrePass, HlmsBaseProp::UseSsr and HlmsBaseProp::SsRefractionsAvailable
  • another renderable B with properties HlmsBaseProp::UsePrePass and HlmsBaseProp::SsRefractionsAvailable (so no HlmsBaseProp::UseSsr)

According to implementation the renderable A will assign certain value to HlmsBaseProp::SsRefractionsAvailable, but renderable B will assign different value to the same property (which is minus 1 compared to renderable A because a lack of ssrTexture won't increment the texUnit variable). However in HlmsPbs::fillBuffersFor the textures are bound only if HLMS type is changed - so if there are multiple renderables with different properties, this dynamic assignment is, I think, somehow broken. I would expect the code which assign indices dynamically once, in the binding code cache the binds and then by new renderable compare whether the indices are the same and if not, update the texture binding.

Also I would expect that the conditions are identical. The current implementation set the ssrTexture based on presence of properties HlmsBaseProp::UsePrePass and HlmsBaseProp::UseSsr, but in HlmsPbs::fillBuffersFor the bode binds the texture (and increment texUnit local variable) if input parameter casterPass is false and mSsrTexture is not nullptr. It is necessary to search through code to verify that those properties are actually set based on same conditions - why is it implemented like that? (which it seems it is, but it is no so simple to follow it)

I suppose that there is no bug and the code somehow works - maybe some properties are set before rendering in the render target and are valid through the whole rendering pipeline in that window (or pass) and hence the binding of the textures is constant for all upcoming renderables for that particular HLMS.

Am I missing something here?

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

Re: hlms texture units numbering

Post by dark_sylinc »

Your understanding is quite good.

The part you're missing is this:

  1. You correctly guessed that if Renderable A and Renderable B have mismatching properties like UsePrePass or SsRefractionsAvailable, it will glitch.

  2. However this is (or should be) impossible. SsRefractionsAvailable is a per-pass property set by preparePassHashBase. Both A and B will have this property set or unset at the same time, but not mismatched. Therefore as long as we are inside the same RenderQueue::render iteration, this property will stay the same. If a later iteration of RenderQueue::render toggles the setting, both A and B will still have the same value (either both are on, or both are off. But not mixed).

Remember that the final hash is derived from combining the hlms pass hash and the hlms renderable hash:

Code: Select all

const HlmsCache *hlmsCache =
    hlms->getMaterial( &c_dummyCache, passCache, queuedRenderable, casterPass, nullptr );

passCache contains SsRefractionsAvailable property. queuedRenderable.renderable->mHlmsHash (and mHlmsCasterHash) contains the renderable hash.

A clearer example is planar reflections. The code is the following:

Code: Select all

const int32 hasPlanarReflections = getProperty( tid, PbsProperty::HasPlanarReflections );
if( hasPlanarReflections )
{
	const bool usesPlanarReflections =
	    getProperty( tid, PbsProperty::UsePlanarReflections ) != 0;
	if( usesPlanarReflections )
	    setTextureReg( tid, PixelShader, "planarReflectionTex", texUnit );
	++texUnit;
}

HasPlanarReflections is set per pass in HlmsPbs::preparePassHash. UsePlanarReflections is set per renderable in HlmsPbs::calculateHashForPreCreate.

Notice that if the Renderable does not use planar reflections, the texUnit is still incremented (otherwise it would glitch, like you predicted).

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

Re: hlms texture units numbering

Post by dark_sylinc »

Ah! I forgot to address the following: you correctly guessed that at runtime HlmsPbs::fillBuffersFor only checks for mSsrTexture:

Code: Select all

if( mSsrTexture )
{
  *commandBuffer->addCommand<CbTexture>() =
      CbTexture( (uint16)texUnit++, mSsrTexture, 0 );
}

Therefore if mSsrTexture being un/set is not in sync with SsRefractionsAvailable, then indeed it would glitch.

Yes, that is correct. The only thing keeping this together is the code itself not containing bugs:

Code: Select all

// In Hlms::preparePassHashBase
if( sceneManager->getCurrentRefractionsTexture() != 0 )
{
    setProperty( kNoTid, HlmsBaseProp::VPos, 1 );
    setProperty( kNoTid, HlmsBaseProp::ScreenPosInt, 1 );
    setProperty( kNoTid, HlmsBaseProp::SsRefractionsAvailable, 1 );
}

// In HlmsPbs::preparePassHash
mRefractionsTexture = sceneManager->getCurrentRefractionsTexture();

// In HlmsPbs::fillBuffersFor
if( mRefractionsTexture )
{
	*commandBuffer->addCommand<CbTexture>() =
		CbTexture( (uint16)texUnit++, mRefractionsTexture, mDecalsSamplerblock );
}

If these 3 snippets were to fail out of sync in their if logic conditions, then it could glitch.

The reason fillBuffersFor went for if( mRefractionsTexture ) is that getProperty is a little bit too expensive for such a hot location. Perhaps a comment clarifying:

Code: Select all

if( mRefractionsTexture ) // Implies HlmsBaseProp::SsRefractionsAvailable
{
	*commandBuffer->addCommand<CbTexture>() =
		CbTexture( (uint16)texUnit++, mRefractionsTexture, mDecalsSamplerblock );
}

// Or an assert:
OGRE_ASSERT_HIGH( (mRefractionsTexture != 0) == getProperty( HlmsBaseProp::SsRefractionsAvailable ) != 0 );
if( mRefractionsTexture ) // Implies HlmsBaseProp::SsRefractionsAvailable
{
	*commandBuffer->addCommand<CbTexture>() =
		CbTexture( (uint16)texUnit++, mRefractionsTexture, mDecalsSamplerblock );
}

Would make this easier to follow?

User avatar
bishopnator
Gnome
Posts: 328
Joined: Thu Apr 26, 2007 11:43 am
Location: Slovakia / Switzerland
x 16

Re: hlms texture units numbering

Post by bishopnator »

Thanks for very detailed explanation. At least I see that I am on correct path. In my HLMS I will keep as much as possible the texture units fixed - I think I won't overflow through the limit of maximum used textures and hence I will avoid this dynamic assignment according to the currently used properties.

The comments as you proposed would definitely help - in HLMS code I usually get somehow to the property name and trying to search for its occurrences so with such comment I could eventually find it and the code would be more in sync as it will clarify the usage of different conditions (but with the comment it would explain that they are actually the same).

I hope that the modern HLSL/GLSL doesn't require that the shaders always use binding of the textures beginning at 0, but it is possible to bind a texture to e.g. slot 5 without binding anything to slots 0-4. At least the older versions (dx9) there wasn't such limit.

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

Re: hlms texture units numbering

Post by dark_sylinc »

I forgot to mention a few things:

You can use mReservedTexSlots and mReservedTexBufferSlots to reserve the first slots for you.

This can give you breathing room to avoid complicating your customizations. HlmsTerra sets mReservedTexSlots to 3 so it can bind the heightmap, shadow map and normal map of the terrain.

I hope that the modern HLSL/GLSL doesn't require that the shaders always use binding of the textures beginning at 0, but it is possible to bind a texture to e.g. slot 5 without binding anything to slots 0-4. At least the older versions (dx9) there wasn't such limit.

They don't, but you may find with API or HW limitations on how high you can go (specially in OpenGL because it fuses textures with samplers, and most HW can't do more than 31 sampler units; on D3D11 you get 127 texture slots; though if you bind slot 4 and then slot 127, you may incur driver overhead as the driver has to scan ranges 5-126 even if they're empty).