[2.1] Adding HLMS customisations per datablock

Design / architecture / roadmap discussions related to future of Ogre3D (version 2.0 and above)
Post Reply
al2950
OGRE Expert User
OGRE Expert User
Posts: 1170
Joined: Thu Dec 11, 2008 7:56 pm
Location: Bristol, UK
x 65

[2.1] Adding HLMS customisations per datablock

Post by al2950 » Wed Jan 27, 2016 6:51 pm

Me again :)

I know adding some form of interface or callback system to allow customisations per Renderable would be a very bad idea, however I have been looking at ways to be able to add customisations per datablock, without changing any Ogre code.

So, you can add custom piece files that will be parsed based on what properties are set, so my question is could we have custom properties per datablock which could be set from a material file, this would affectively allow (along with custom piece files) customisations per renderable.

I am sure I have missed something critical, potentially render queue sorting assumptions, but any thoughts!?
0 x

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

Re: [2.1] Adding HLMS customisations per datablock

Post by dark_sylinc » Wed Jan 27, 2016 10:22 pm

As for making the changes per Datablock:

At shader level: I realize there is no custom_ps_ piece for adding more data. This can be easily be fixed.
At C++ level: I don't think a listener is a good idea. It wouldn't be clean and would be complicated. The best method I see is deriving from both HlmsPbs and HlmsPbsDatablock:

Code: Select all

class HlmsMyDatablock : public HlmsPbsDatablock
{
     //Override what we send to the GPU (usually to send [i]more[/i] data)
     virtual void uploadToConstBuffer( char *dstPtr );
}

class HlmsMy : public HlmsPbs
{
     //Override to return a HlmsMyDatablock.
     virtual HlmsDatablock* createDatablockImpl( IdString datablockName,
											const HlmsMacroblock *macroblock,
											const HlmsBlendblock *blendblock,
											const HlmsParamVec &paramVec );
}
A small modification to Ogre would be required so that ConstBufferPool() gets the HlmsMyDatablock::MaterialSizeInGpuAligned instead of HlmsPbsDatablock::MaterialSizeInGpuAligned.
And then you're set.

As for making the changes per Renderable:
Again, no listener, this time the reason is performance. But this time it involves overloading HlmsPbs::fillBuffersFor so we can send more information via mCurrentMappedConstBuffer or mCurrentMappedTexBuffer.
This method is the biggest of all (in terms of lines of code and work being done), so overloading it could involve a lot of copy pasting. It's definitely the most intrusive / less modular section; and the most difficult to maintain if the original codebase in the repo changes (however, it has been very stable by now).
0 x

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

Re: [2.1] Adding HLMS customisations per datablock

Post by dark_sylinc » Wed Jan 27, 2016 10:29 pm

al2950 wrote:I am sure I have missed something critical, potentially render queue sorting assumptions, but any thoughts!?
If Renderable A has properties x, y & z set; and renderables B, C, D have properties y & z set; BCD will be sorted together, A will be sorted to be rendered after or before BCD (unless they are transparent, in which case distance to camera takes precedence)
If each renderable has a different set of properties (i.e. you have a property that is set to the value of movableObject->getId()); that would be very bad (each Renderable gets its own shader, and can't be batched together).

As for fillBuffersFor; issuing a command (via CommandBuffer::addCommand) inside this function breaks (splits) auto instancing. If you issue a command for every Renderable, there won't be any instancing being done.
0 x

al2950
OGRE Expert User
OGRE Expert User
Posts: 1170
Joined: Thu Dec 11, 2008 7:56 pm
Location: Bristol, UK
x 65

Re: [2.1] Adding HLMS customisations per datablock

Post by al2950 » Thu Jan 28, 2016 3:17 pm

OK, I think I understand all. But just to give it some context for others here is an example

I am adding some wet surface code to the shaders as I discussed here;
http://ogre3d.org/forums/viewtopic.php?f=25&t=84774

Ideally I want to add a 'porosity' property to each material. Doing this via the hlms properties is a bad idea, as it would effectively create a different shader for every different 'porisity' value, which would in turn heavily hinder ogre's ability to auto instance and reduce draw calls?? This should be done by adding an extra data value to the const material buffers, which will require overriding the base PBS implementation.

I think I have got that correct, please let me know if not.
0 x

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

Re: [2.1] Adding HLMS customisations per datablock

Post by dark_sylinc » Thu Jan 28, 2016 5:46 pm

You are correct on all accounts.

Of course if you only think there will be only 3 or 4 different porosity values, you can choose to embed it as a property as the impact won't be noticeable. But if you have far too many, you'll be hindering our ability to auto instance greatly.
0 x

User avatar
TaaTT4
OGRE Contributor
OGRE Contributor
Posts: 142
Joined: Wed Apr 23, 2014 3:49 pm
x 4

Re: [2.1] Adding HLMS customisations per datablock

Post by TaaTT4 » Fri Sep 15, 2017 2:58 pm

dark_sylinc wrote: As for making the changes per Datablock:

At shader level: I realize there is no custom_ps_ piece for adding more data. This can be easily be fixed.
I'm resuming this old thread for asking this.
Is it possible to add a @insertpiece( custom_materialBuffer ) (or something similar) in the Material struct declaration?
0 x
Senior game programmer at Vae Victis
Working on Racecraft

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

Re: [2.1] Adding HLMS customisations per datablock

Post by xrgo » Fri Mar 09, 2018 8:10 pm

hello!!! Posible feature request:
I need to set some custom values per datablock, and I noticed that there is an interesting

Code: Select all

float   mReserved[3][4];
that could be renamed to:

Code: Select all

float   mUserValues[3][4];
and add a setter/getter:

Code: Select all

void HlmsPbsDatablock::setUserValue( uiont_8, index, Ogre::Vector3 value )
and then in my custom pieces I can do:

Code: Select all

material.userValue[i].xyzw
to read my values =D

this would be very useful, any reason why has not done before? why is this reserved?

thanks!
0 x

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

Re: [2.1] Adding HLMS customisations per datablock

Post by dark_sylinc » Fri Mar 09, 2018 8:51 pm

Not entirely a bad idea.

The thing is, 256-bytes per material has measurable higher performance thus that's why they're reserved. We may have more settings in the future but I can't remember exactly why.

I'm a little occupied now to do it myself so PRs to implement this are welcome.
0 x

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

Re: [2.1] Adding HLMS customisations per datablock

Post by xrgo » Fri Mar 09, 2018 9:25 pm

thanks! I will PR, I was just looking on how to do PRs =D, expect one soon
0 x

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

Re: [2.1] Adding HLMS customisations per datablock

Post by xrgo » Sat Mar 10, 2018 12:26 am

pull request up! https://bitbucket.org/sinbad/ogre/pull-requests/818
I hope I did it well =D

I also added custom_vs_preTransform to the vertex shaders, something I use in my custom pieces to make wind movement effect, might be useful for someone else.

Saludos!
0 x

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

Re: [2.1] Adding HLMS customisations per datablock

Post by xrgo » Wed Oct 31, 2018 12:49 am

Hello! I need to make a dynamic displace map in just a specific object, but maintaining all the pbs features, so... since there's is no way to add a custom texture to the vertex shader to a specific datablock I just did this:
dark_sylinc wrote:
Wed Jan 27, 2016 10:22 pm
.......
The best method I see is deriving from both HlmsPbs and HlmsPbsDatablock:
.......
this is my code:
yHlmsPbsExtended.cpp:

Code: Select all

#include "yHlmsPbsExtended.h"
#include "yHlmsPbsDatablockExtended.h"
#include "OgreRoot.h"
#include "OgreHlmsManager.h"
#include "CommandBuffer/OgreCommandBuffer.h"
#include "CommandBuffer/OgreCbTexture.h"
#include "CommandBuffer/OgreCbShaderBuffer.h"
#include "OgreTextureManager.h"

#include <iostream>


yHlmsPbsExtended::yHlmsPbsExtended( Ogre::Archive *dataFolder, Ogre::ArchiveVec *libraryFolders ) :
    HlmsPbs( dataFolder, libraryFolders )
{

    Ogre::HlmsSamplerblock samplerblock;
    samplerblock.mU = Ogre::TextureAddressingMode::TAM_CLAMP;
    samplerblock.mV = Ogre::TextureAddressingMode::TAM_CLAMP;
    samplerblock.mW = Ogre::TextureAddressingMode::TAM_CLAMP;
    samplerblock.mMaxAnisotropy = 8;
    samplerblock.mMagFilter = Ogre::FO_ANISOTROPIC;
    mSamplerblockWrap = Ogre::Root::getSingleton().getHlmsManager()->getSamplerblock(samplerblock);

}

const Ogre::HlmsCache* yHlmsPbsExtended::createShaderCacheEntry( Ogre::uint32 renderableHash,
                                                        const Ogre::HlmsCache &passCache,
                                                        Ogre::uint32 finalHash,
                                                        const Ogre::QueuedRenderable &queuedRenderable )
{


    const Ogre::HlmsCache* retVal = HlmsPbs::createShaderCacheEntry( renderableHash, passCache, finalHash, queuedRenderable );

    const bool useDisplaceTexture = getProperty( "displace_map" ) != 0;

    Ogre::GpuProgramParametersSharedPtr vsParams = retVal->pso.vertexShader->getDefaultParameters();
    if( useDisplaceTexture ){
        vsParams->setNamedConstant( "displaceMap", 13 );
    }
    mRenderSystem->bindGpuProgramParameters( Ogre::GPT_VERTEX_PROGRAM, vsParams, Ogre::GPV_ALL );

    return retVal;
}


void yHlmsPbsExtended::calculateHashForPreCreate( Ogre::Renderable *renderable, Ogre::PiecesMap *inOutPieces ){

    HlmsPbs::calculateHashForPreCreate( renderable, inOutPieces );

    assert( dynamic_cast<yHlmsPbsDatablockExtended*>( renderable->getDatablock() ) );
    yHlmsPbsDatablockExtended *datablock = static_cast<yHlmsPbsDatablockExtended*>(
                                                    renderable->getDatablock() );

    if( !datablock->getDisplaceTexture().isNull() ){
        setProperty( "displace_map", 1 );
    }

}

Ogre::uint32 yHlmsPbsExtended::fillBuffersForV2( const Ogre::HlmsCache *cache,
                                         const Ogre::QueuedRenderable &queuedRenderable,
                                         bool casterPass, Ogre::uint32 lastCacheHash,
                                         Ogre::CommandBuffer *commandBuffer )
{

    Ogre::uint32 retVal = HlmsPbs::fillBuffersForV2( cache, queuedRenderable, casterPass, lastCacheHash, commandBuffer );

    assert( dynamic_cast<const yHlmsPbsDatablockExtended*>( queuedRenderable.renderable->getDatablock() ) );
    const yHlmsPbsDatablockExtended *datablock = static_cast<const yHlmsPbsDatablockExtended*>(
                                            queuedRenderable.renderable->getDatablock() );


    if( OGRE_EXTRACT_HLMS_TYPE_FROM_CACHE_HASH( lastCacheHash ) != mType )
    {
        if( !datablock->getDisplaceTexture().isNull() ){
            const Ogre::TexturePtr &displaceTex = datablock->getDisplaceTexture();
            *commandBuffer->addCommand<Ogre::CbTexture>() = Ogre::CbTexture( 13, true, displaceTex.get(), mSamplerblockWrap );
        }
    }

    return retVal;
}

Ogre::HlmsDatablock* yHlmsPbsExtended::createDatablockImpl( Ogre::IdString datablockName,
                                                   const Ogre::HlmsMacroblock *macroblock,
                                                   const Ogre::HlmsBlendblock *blendblock,
                                                   const Ogre::HlmsParamVec &paramVec )
{
    return OGRE_NEW yHlmsPbsDatablockExtended( datablockName, this, macroblock, blendblock, paramVec );
}
.h:

Code: Select all

#ifndef YHLMSPBSEXTENDED_H
#define YHLMSPBSEXTENDED_H

#include "OgreHlmsPbs.h"

class yHlmsPbsDatablockExtended;

class yHlmsPbsExtended : public Ogre::HlmsPbs
{
public:
    yHlmsPbsExtended( Ogre::Archive *dataFolder, Ogre::ArchiveVec *libraryFolders );

    const Ogre::HlmsCache* createShaderCacheEntry( Ogre::uint32 renderableHash,
                                                     const Ogre::HlmsCache &passCache,
                                                     Ogre::uint32 finalHash,
                                                     const Ogre::QueuedRenderable &queuedRenderable ) override;

    void calculateHashForPreCreate( Ogre::Renderable *renderable, Ogre::PiecesMap *inOutPieces ) override;

    Ogre::uint32 fillBuffersForV2( const Ogre::HlmsCache *cache,
                                             const Ogre::QueuedRenderable &queuedRenderable,
                                             bool casterPass, Ogre::uint32 lastCacheHash,
                                             Ogre::CommandBuffer *commandBuffer ) override;


    Ogre::HlmsDatablock* createDatablockImpl( Ogre::IdString datablockName,
                                                        const Ogre::HlmsMacroblock *macroblock,
                                                        const Ogre::HlmsBlendblock *blendblock,
                                                        const Ogre::HlmsParamVec &paramVec ) override;

    const Ogre::HlmsSamplerblock* mSamplerblockWrap;
};

#endif // YHLMSPBSEXTENDED_H
and yHlmsPbsDatablockExtended.cpp:

Code: Select all

#include "yHlmsPbsDatablockExtended.h"

#include <iostream>

yHlmsPbsDatablockExtended::yHlmsPbsDatablockExtended( Ogre::IdString name, Ogre::HlmsPbs *creator,
                                                      const Ogre::HlmsMacroblock *macroblock,
                                                      const Ogre::HlmsBlendblock *blendblock,
                                                      const Ogre::HlmsParamVec &params ) :
    Ogre::HlmsPbsDatablock( name, creator, macroblock, blendblock, params )
{
    mDisplaceTexture.setNull();
}

void yHlmsPbsDatablockExtended::setDisplaceTexture( Ogre::TexturePtr texture ){
    mDisplaceTexture = texture;
}

const Ogre::TexturePtr &yHlmsPbsDatablockExtended::getDisplaceTexture() const{
    return mDisplaceTexture;
}
.h:

Code: Select all

#ifndef YHLMSPBSDATABLOCKEXTENDED_H
#define YHLMSPBSDATABLOCKEXTENDED_H

#include "OgreHlmsPbsDatablock.h"

class yHlmsPbsDatablockExtended : public Ogre::HlmsPbsDatablock
{
    friend class yHlmsPbsExtended;
public:
    yHlmsPbsDatablockExtended( Ogre::IdString name, Ogre::HlmsPbs *creator,
                               const Ogre::HlmsMacroblock *macroblock,
                               const Ogre::HlmsBlendblock *blendblock,
                               const Ogre::HlmsParamVec &params );

    void setDisplaceTexture( Ogre::TexturePtr texture );
    const Ogre::TexturePtr &getDisplaceTexture() const;

private:
    Ogre::TexturePtr mDisplaceTexture;
};

#endif // YHLMSPBSDATABLOCKEXTENDED_H
and the corresponding @property( "displace_map" ) in the vertex shader:

Code: Select all

  @property( displace_map )
   uniform sampler2DArray displaceMap[1]; //yes! the texture is TEX_TYPE_2D_ARRAY for no real reason
  @end
  .....
  @property( displace_map )
    worldPos.xyz += ( vec4( @insertpiece(local_normal) * texture( displaceMap[0], vec3( uv0, 0 ) ).x*0.1, 0 ) * worldMat ).xyz ;
  @end
  
and it was working!!! until yesterday that I updated Ogre to the current version, (I was using a version from many months ago)

I think that I am doing something wrong and it was working just by luck before... or there is something broken in the current version?

the @property is working, it is entering the right parts of the Hlms code, but in the vertex shader I don't get the texture, there might be a binding problem
0 x

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

Re: [2.1] Adding HLMS customisations per datablock

Post by dark_sylinc » Wed Oct 31, 2018 1:03 am

Does this work as expected?

Code: Select all

const bool useDisplaceTexture = getProperty( "displace_map" ) != 0;
We may have accidentally cleaned up the properties thus it may always be returning false (just a guess)

Another possibility is that binding slot "13" is now too small if your material uses many textures and there were more slots added.

If RenderDoc is showing you that the code is generated correctly, then it could be indeed a binding problem.
If you have spare computing power to recompile Ogre several times, try using hg bisect to find when it broke in no time.
0 x

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

Re: [2.1] Adding HLMS customisations per datablock

Post by xrgo » Wed Oct 31, 2018 2:05 am

Something spooky is happening (might be because of Halloween?)
I checked again and its not entering this if (in fillBuffersForV2):

Code: Select all

    if( OGRE_EXTRACT_HLMS_TYPE_FROM_CACHE_HASH( lastCacheHash ) != mType )
    {
        if( !datablock->getDisplaceTexture().isNull() ){
but it is entering this part (in calculateHashForPreCreate):

Code: Select all

    if( !datablock->getDisplaceTexture().isNull() ){
        setProperty( "displace_map", 1 );
    }
Then I remember that yesterday the scene was almost empty (when I was using the prev ogre version), now has some other objects, so I removed most of them and its working again! (so maybe its not about the ogre version)
Obviously that's not a solution, I need those objects, so I am guessing I am missing some hash stuffs regarding textures, I'll look in to that now

about the slots, that datablock is not using many textures so I don't think so, anyways I tried with other values and no luck (btw, the limit is 15? iirc)

thanks!!
0 x

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

Re: [2.1] Adding HLMS customisations per datablock

Post by xrgo » Wed Oct 31, 2018 2:20 am

I implemented calculateHash(), copy pasted everything and added this:

Code: Select all

    if( !mDisplaceTexture.isNull() ){
        hash += Ogre::IdString( mDisplaceTexture->getName() );
    }
also tried adding this:

Code: Select all

void yHlmsPbsDatablockExtended::setDisplaceTexture( Ogre::TexturePtr texture ){
    mDisplaceTexture = texture;
    calculateHash();  //<<<<<<<<<<<<<
    flushRenderables();  //<<<<<<<<<<<<<
}
no luck =(
0 x

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

Re: [2.1] Adding HLMS customisations per datablock

Post by dark_sylinc » Wed Oct 31, 2018 4:49 am

Oh!
Now I see the problem.

When you do:

Code: Select all

if( OGRE_EXTRACT_HLMS_TYPE_FROM_CACHE_HASH( lastCacheHash ) != mType )
{
This is only executed ideally once (in practice multiple times, depending on whether there are materials using other types of Hlms interleaved with objects using HlmsPbs).
So in other words whatever happens inside is meant for commands that have global impact. Like for example a shadow map or forward+ (which is used by all objects/materials and may occasionally be ignored by an object, but even if ignored the generated shader is prepared to assume the slots are taken, etc)

However when you do:

Code: Select all

if( OGRE_EXTRACT_HLMS_TYPE_FROM_CACHE_HASH( lastCacheHash ) != mType )
    {
        if( !datablock->getDisplaceTexture().isNull() ){
This is not global: it is grabbing the datablock from the Renderable being processed. So if the first Renderable being rendered (or the first Renderable after switching back from a different Hlms type) does not have a displacement texture, it won't work. Whether this works is dependent on objects and their distance to camera (among other things).

If the displacement texture is just one for everybody, then fix this issue (by holding the displacement texture in yHlmsPbsExtended instead of using the pointer from datablock->getDisplaceTexture). Even if it's global, keep your change to calculateHash though, it could help Ogre with render queue sorting to have faster rendering.
If there can be multiple/different displacement textures, then you'll have to drop the "if( OGRE_EXTRACT_HLMS_TYPE_FROM_CACHE_HASH( lastCacheHash ) != mType )", because that's wrong and use this:

Code: Select all

uint32 lastTextureHash = mLastTextureHash; //Save the value because it will be modified in HlmsPbs::fillBuffersForV2
...
Ogre::uint32 retVal = HlmsPbs::fillBuffersForV2( cache, queuedRenderable, casterPass, lastCacheHash, commandBuffer );
...
if( datablock->mTextureHash != lastTextureHash && !datablock->getDisplaceTexture().isNull()  )
{
...
}
Btw if you somehow manage to sneak the displacement texture into mBakedTextures you may even not have to do this (but now the texture unit count will not be fixed at 13). Sounds complicated though. Forget I said anything :P
1 x

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

Re: [2.1] Adding HLMS customisations per datablock

Post by xrgo » Wed Oct 31, 2018 5:09 am

I love you, its working now
0 x

Post Reply