[2.2]: HLMS datablock reference count type

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


Post Reply
crancran
Greenskin
Posts: 138
Joined: Wed May 05, 2010 3:36 pm
x 6

[2.2]: HLMS datablock reference count type

Post by crancran »

I recently was working on improving the render view/draw distance algorithm our engine uses and I noticed when I increased the draw distance to a specific point and began moving around the game world, as objects were removed from the scene and destroyed, I got this error:

Code: Select all

"The Samplerblock wasn't created with this manager!"
After some inspection of the problem, I sourced the problem to the data type used by mRefCount inside BasicBlock.

For small scenes where you have minimal objects using a limited number of materials and textures, the uint16 type (65k cap) is probably fine. But when anyone begins to explore a larger or more detailed scene where objects share the same sampler block, the 65k limit is very easily reached.

I changed the code locally to use a uint32 and thus far that has solved my issue.

From what I can tell, HlmsManager will now simply require slightly more than 16k more memory (if my math is right) to store the additional data based on the two 4096 entry vectors and the several other vectors which are negligible.

I'm curious if we shouldn't revisit that data-type in the main source to be a bit more flexible for larger/detailed scenes?
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: [2.2]: HLMS datablock reference count type

Post by dark_sylinc »

Something feels very wrong.

You should not be hitting those limits so easily. Sounds like handlers are being leaked, or you're doing insane things like having one material per instance.

Each PBS datablock supports up to 15 texture per material (including the environment map). If you used all of them, you would need 4369 unique materials.

It may be worth investigating why the reference count is getting so high, because "it's large scene" does not sound like a reasonable explanation :?
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: [2.2]: HLMS datablock reference count type

Post by dark_sylinc »

crancran wrote: Mon Jan 14, 2019 9:09 am

Code: Select all

"The Samplerblock wasn't created with this manager!"
After some inspection of the problem, I sourced the problem to the data type used by mRefCount inside BasicBlock.

For small scenes where you have minimal objects using a limited number of materials and textures, the uint16 type (65k cap) is probably fine.
If reference count is overflowing, you should be hitting the assert in HlmsManager::getSamplerblock:

Code: Select all

assert( retVal->mRefCount < 0xFFFF && "Reference count overflow!" );
Make sure you're compiling without NDEBUG so asserts do trigger.
crancran
Greenskin
Posts: 138
Joined: Wed May 05, 2010 3:36 pm
x 6

Re: [2.2]: HLMS datablock reference count type

Post by crancran »

dark_sylinc wrote: Mon Jan 14, 2019 4:50 pm insane things like having one material per instance.
I know for a fact I am doing this, so perhaps its my approach that could be improved.

Our terrain system uses the traditional grid approach where we know based on the current camera position and view distance, how many terrain grid tiles should be visible to the player. We continue to subdivide each grid tile into 16x16 equal size cells where a cell defines the following characteristics:

1. Diffuse textures used
2. Alpha blend mask used for texture splatting
3. Static shadow map used
4. Host of other various things for other terrain tasks

So yes; I do in fact define a unique material per instance (cell in this case).

Any suggestions on how I could be more efficient with texture splatting with my use case?
crancran
Greenskin
Posts: 138
Joined: Wed May 05, 2010 3:36 pm
x 6

Re: [2.2]: HLMS datablock reference count type

Post by crancran »

So I have been doing some thinking and I believe I have a proposal that might work; albeit I could use some feedback to know whether or not I'm on the right track. The main idea here rather than to draw/texture per cell, take a step up and do that process per terrain tile. This would drastically reduce the overhead in memory/resources substantially but I'm not sure performance wise if it makes sense.

We know the detail textures used per tile along with the metadata about them. The first step would be to allocate the appropriate number of texture pools based on that metadata and then load the detail textures into those pools. Since I want to pack textures per tile together, I'll alias each texture being added to that pool so that if that texture is used by another pool, we guarantee proper packing of the textures. In other words, if a terrain tile contains 31 256x256 textures and 2 512x512 textures, then that tile would allocate 2 pools. If another tile uses any of those same textures, it would allocate its own pools and store those textures there; allowing me to easily deallocate pools as a tile is unloaded without impacting other tiles.

The next step would be to avoid constructing lots of mini-textures for the alpha blend masks and static shadow maps. Since cells use 64x64 textures to represent this information, those can be combined into a single 1024x1024 RGBA mega-texture where r=layer1, g=layer2, b=layer3, a=shadow. The shader can therefore deduce the weights across the max of 4 layers easily.

The final step is to determine which textures to sample and how sampling should happen. This is where I think my implementation might be sketchy, but my idea is to use another texture - a 16x16 RGB texture where each texel represents a single cell in the tile. I can provide the base x/y/z position for the tile as properties and by combining that with the vertex position in the vertex shader, I can compute which cell the vertex is within and therefore tell the pixel shader which texel to sample. In the pixel shader, the 16x16 texture essentially tells me which 2D array and its index to sample. Perhaps I can do this with defining this blend mapping on the material and supply it via some other buffer type through hlms rather than a texture?

What do you think dark_sylinc?
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: [2.2]: HLMS datablock reference count type

Post by dark_sylinc »

It would seem you're using your own Hlms implementation?

Because if so, then you can retrieve the samplerblock once at the Hlms level, instead of datablock level.

If that's so, that greatly simplifies the solution, because you can send per-renderable data such as the texture indices in the buffers that are meant to be per renderable (mCurrentMappedConstBuffer or mCurrentMappedTexBuffer); and get the samplerblock once at the Hlms level, instead of datablock level.

The material model does not fit well because this model assumes it's going to be reused/shared with multiple Items.

Your idea sounds fine, I cannot judge it well because you know better what your goals are and the problems to be solved. Just one thing:
the 16x16 texture essentially tells me which 2D array and its index to sample
If you mean:

Code: Select all

float index = texture.Load( uv );
float4 diffuseCol = diffuseMap.Sample( bilinearSampler, uv, index );
Then all is well.

But if you mean something like this:

Code: Select all

float index = texture.Load( uv );
float4 diffuseCol = diffuseMap[index].Sample( bilinearSampler, uv );
You're going to hit a wall because it's not valid Shader Model 5.0 syntax (HW does not support it). You can do that with bindless textures (Vulkan/DX12) but not with DX11/GL.

Btw
From what I can tell, HlmsManager will now simply require slightly more than 16k more memory (if my math is right) to store the additional data based on the two 4096 entry vectors and the several other vectors which are negligible.
I don't think I follow. mRefCount is used when the same sampler is retrieved multiple times.
The entries you're talking about (like mSamplerblocks[OGRE_HLMS_NUM_SAMPLERBLOCKS] and co.) hold unique samplerblocks.

One thing is to use the same samplerblocks more than 65k times, another is to use more than 65k samplerblocks all of them with different settings (eg. one uses bilinear filtering, the other point filtering, the other trilinear, the other aniso, another has rare min/mag filter settings, etc) these are two different problems.

And mRefCount only means the same samplerblocks is being used too many times. Did you try just changing mRefCount's type and see what happens? (it should work, I think)

The reason I'm asking this is because I want to rule out any other bug (like leaks, corruption, etc)
crancran
Greenskin
Posts: 138
Joined: Wed May 05, 2010 3:36 pm
x 6

Re: [2.2]: HLMS datablock reference count type

Post by crancran »

dark_sylinc wrote: Wed Jan 16, 2019 7:14 am It would seem you're using your own Hlms implementation?

Because if so, then you can retrieve the samplerblock once at the Hlms level, instead of datablock level.

If that's so, that greatly simplifies the solution, because you can send per-renderable data such as the texture indices in the buffers that are meant to be per renderable (mCurrentMappedConstBuffer or mCurrentMappedTexBuffer); and get the samplerblock once at the Hlms level, instead of datablock level.
That would certainly be a much easier change to implement and test based on the current way I render for sure. I'll give that a try and see how things work out before trying to dive any deeper.
dark_sylinc wrote: Wed Jan 16, 2019 7:14 am The material model does not fit well because this model assumes it's going to be reused/shared with multiple Items.
Yep, I suspect that's pretty normal with terrain texturing, which is why I was stepping back as far as I was.

While the refcount on the samplerblock raised this discussion, I also don't think its necessarily prudent to be constructing as many materials as I do in order to texture the terrain. One terrain tile is essentially 512x512 world units of space and for that single tile we define 256 materials and 256 renderables attached to a single movable object.

There is a valid use case to render per cell but I believe that only makes the most sense for cells closest to the camera, such as the 5x5 or 9x9 cells around the camera to allow for the highest fidelity possible. I believe beyond that point, a tile should likely try and combine the remaining cells into a much larger single renderable where perhaps I use the technique I described before.

Obviously I haven't profiled things yet and perhaps I should wait - but I would suspect trying to do a hybrid as above, I would find that I would have lower memory consumption (lower number of materials in play) and rendering should be faster because there would be fewer renderables per movable object representing the terrain mesh.
dark_sylinc wrote: Wed Jan 16, 2019 7:14 am
From what I can tell, HlmsManager will now simply require slightly more than 16k more memory (if my math is right) to store the additional data based on the two 4096 entry vectors and the several other vectors which are negligible.
I don't think I follow. mRefCount is used when the same sampler is retrieved multiple times.
The entries you're talking about (like mSamplerblocks[OGRE_HLMS_NUM_SAMPLERBLOCKS] and co.) hold unique samplerblocks.

One thing is to use the same samplerblocks more than 65k times, another is to use more than 65k samplerblocks all of them with different settings (eg. one uses bilinear filtering, the other point filtering, the other trilinear, the other aniso, another has rare min/mag filter settings, etc) these are two different problems.

And mRefCount only means the same samplerblocks is being used too many times. Did you try just changing mRefCount's type and see what happens? (it should work, I think)

The reason I'm asking this is because I want to rule out any other bug (like leaks, corruption, etc)
My apologies if my statement was a bit vague.

I was simply trying to point out that by changing the data type from uint16 to uint32 wouldn't have a substantial memory impact. HlmsManager reserves two vectors, Macro and Blend blocks with an element size of 4096. Those two vectors alone would reserve an additional 16k by changing the data type alone. The other vectors in the class reserve substantially smaller numbers of elements so the memory consumption by the data type change is negligible.
Post Reply