Purpose of setTextureReg vs CbTexture command Topic is solved

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


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

Purpose of setTextureReg vs CbTexture command

Post by bishopnator »

I am trying to understand the logic of setTextureReg and why it cannot be done automatically together with issuing the CbTexture commands in fillBuffersForV1/fillBuffersForV2. From the implementation is look like all the setTextureReg calls must be placed in notifyPropertiesMergedPreGenerationStep (because it is called from createShaderCacheEntry where the clearing of texture registers and names is done).

The problem to understand the topic, that the structure of checks and increments of texUnit variable must be in sync with issuing of the CbTexture commands in fillBuffersFor implemention. I expect that the implementations are in sync, but just look there it doesn't look like they are.

In HlmsPbs::notifyPropertiesMergedPreGenerationStep

Code: Select all

        int32 texUnit = mReservedTexBufferSlots;

    if( getProperty( tid, HlmsBaseProp::ForwardPlus ) )
    {
        if( mVaoManager->readOnlyIsTexBuffer() )
            setTextureReg( tid, PixelShader, "f3dLightList", texUnit++ );
        else
            setProperty( tid, "f3dLightList", texUnit++ );

        setTextureReg( tid, PixelShader, "f3dGrid", texUnit++ );
    }

    texUnit += mReservedTexSlots;

    bool depthTextureDefined = false;

    if( getProperty( tid, HlmsBaseProp::UsePrePass ) )
    {
        setTextureReg( tid, PixelShader, "gBuf_normals", texUnit++ );
        setTextureReg( tid, PixelShader, "gBuf_shadowRoughness", texUnit++ );

        if( getProperty( tid, HlmsBaseProp::UsePrePassMsaa ) )
        {
            setTextureReg( tid, PixelShader, "gBuf_depthTexture", texUnit++ );
            depthTextureDefined = true;
        }
        else
        {
            ++texUnit;
        }

        if( getProperty( tid, HlmsBaseProp::UseSsr ) )
            setTextureReg( tid, PixelShader, "ssrTexture", texUnit++ );
    }

And corresponding code in HlmsPbs::fillBuffersFor:

Code: Select all

            size_t texUnit = mReservedTexBufferSlots;

        if( !casterPass )
        {
            constBufferSlot += mAtmosphere->bindConstBuffers( commandBuffer, constBufferSlot );

            if( mGridBuffer )
            {
                *commandBuffer->addCommand<CbShaderBuffer>() =
                    CbShaderBuffer( PixelShader, (uint16)texUnit++, mGlobalLightListBuffer, 0, 0 );
                *commandBuffer->addCommand<CbShaderBuffer>() =
                    CbShaderBuffer( PixelShader, (uint16)texUnit++, mGridBuffer, 0, 0 );
            }

            texUnit += mReservedTexSlots;

            if( !mPrePassTextures->empty() )
            {
                *commandBuffer->addCommand<CbTexture>() =
                    CbTexture( (uint16)texUnit++, ( *mPrePassTextures )[0], 0 );
                *commandBuffer->addCommand<CbTexture>() =
                    CbTexture( (uint16)texUnit++, ( *mPrePassTextures )[1], 0 );
            }

            if( mPrePassMsaaDepthTexture )
            {
                *commandBuffer->addCommand<CbTexture>() = CbTexture(
                    (uint16)texUnit++, mPrePassMsaaDepthTexture, 0,
                    PixelFormatGpuUtils::isDepth( mPrePassMsaaDepthTexture->getPixelFormat() ) );
            }

            if( mDepthTexture )
            {
                *commandBuffer->addCommand<CbTexture>() =
                    CbTexture( (uint16)texUnit++, mDepthTexture, mDecalsSamplerblock,
                               PixelFormatGpuUtils::isDepth( mDepthTexture->getPixelFormat() ) );
            }

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

Almost every condition is written differently. In notifyPropertiesMergedPreGenerationStep, the properties are checked, but in fillBuffersFor, the member variables are checked. It is really hard to track.

Anyway, my question is whether it is not feasible to make everything on once place - specifically in fillBuffersFor - when CbTexture command is issued, together there also update the texture register. Probably it is necessary to cache the texture registers together with properties, but it would be at least at one place. Issuing of the CbTexture command and calling of setTextureReg can be wrapped in the helper function.

Why the textures are set for every renderable? Isn't it more efficient to track the active textures and update the slots only if needed? Or is it done somewhere? If CbTexture command is issued, later when RenderSystem::_setTexture is called from CommandBuffer::execute_setTexture, it just sets the texture.

Edit: I see that calling setProperty within fillBuffersFor is not possible, so try to think in other direction - after calling setTextureReg, somehow translate those calls automatically to the CbTexture commands. E.g. if setTextureReg accepts also texture+sampler, it could be done .. I am trying to come out with the idea how to simplify the implementation on the "user" side if new HLMS is implemented (as I have in my app).

Edit2: The HlmsPbs uses some "global" or "pass" textures which are set only if HLMS type is changed - so there is no need to check which textures are active at the texture slots and then the datablock textures which are baked into texture sets and described by datablock->mTexturesDescSet. This is actually checked against the member variable mLastDescTexture to avoid useless rebindings.

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

Re: Purpose of setTextureReg vs CbTexture command

Post by dark_sylinc »

Hi. It seems you're figuring out the reasons in your edits.

There are two main issues:

  1. Texture update frequency. Some textures change per pass. Others change per object or per material.

  2. How textures are bound together at lower level. Both D3D11 and OpenGL table use a texture table. So we call d3ddevice->SetPSTexture( slot, textureptr );. But Vulkan and D3D12 transitioned to descriptors. In OgreNext we provide a limited texture table emulation because it simplifies a lot of stuff for code already written. This functionality is useful for per-pass textures (where baking descriptors isn't worth it because of its dynamism) and low level materials (old code that heavily assumes D3D11/GL API semantics). Our emulated table implementation assumes that texture binding is infrequent and won't have more than 32 textures. But for the rest we just fire:

Code: Select all

*commandBuffer->addCommand<CbTextures>() = CbTextures(
                        (uint16)texUnit, datablock->mCubemapIdxInDescSet, datablock->mTexturesDescSet );

For D3D11 and GL, the code just loops through the table. But in Vulkan, it just boils to a glorified memcpy() (actually if I had better designed our abstraction of Descriptors, it would just be a vkCmdBindDescriptorSets() call).

And that's without considering samplers (which in GL textures and samplers are separated, but in D3D11 they are split and it's recommended to reuse samplers).

Basically the setTextureReg is too detached from textures. Actually, setTextureReg() used to be just a setProperty() call. I had to write setTextureReg literally because of OpenGL, as I kept forgetting to update the params->setNamedConstant() call. I realized that could be automated, so I did. And now that job lives in Hlms::applyTextureRegisters.

That's basically setTextureReg: A regular setProperty() with the extra job of handling Hlms::applyTextureRegisters.

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

Re: Purpose of setTextureReg vs CbTexture command

Post by bishopnator »

I just realized, that my setTextureReg was placed in wrong override - preparePassHash instead of notifyPropertiesMergedPreGenerationStep and the rendering still works even if the applyTextureRegisters does nothing (when I debugged it). Is the applyTextureRegisters in GL3Plus needed if my GLSL shaders use binding locations for the textures?

Code: Select all

layout(binding = 2) uniform sampler2D lineStyles;

I call CbTexture exactly with the same slot value as referred in the shader. I suppose that the setNamedConstant is needed for older shaders (profiles) where binding locations were no available yet, right?

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

Re: Purpose of setTextureReg vs CbTexture command

Post by dark_sylinc »

Is the applyTextureRegisters in GL3Plus needed if my GLSL shaders use binding locations for the textures?

Yes, because our C++ will try to set the locations via code (and default to 0 if unset...). If you make them explicit in GLSL, the behavior may vary per driver (depending on what the driver gives higher priority).

This only applies to samplerXD and family of texutre variables. It does not apply to TBO (texture buffer objects) even though TBOs and samplerXD shared slot units in OpenGL.

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

Re: Purpose of setTextureReg vs CbTexture command

Post by bishopnator »

Where are the uniforms updated to shaders? In GLSLMonolithicProgram::updateUniforms? I found only there the calls to glUniform1iv which I think should set the binding locations for the samplers. This method is somehow not called in my application with GL3Plus RS. The shader instance is GLSLShader and its bindParameters calls GLSLMonolithicProgram::updateUniforms, but also this one is not called. Am I doing something wrong?

I want to check whether the 0 slot is really uploaded to my pixel shader even that GLSL specifies the binding location 2.

Edit: The createShaderCacheEntry override in HLMS (e.g. HlmsPbd or HlmsUnlit) calls the mRenderSystem->bindGpuProgramParameters for the vertex/pixel shaders. There is also setting of uniform block bindings of PassBuffer, MaterialBuf and InstanceBuffer. In my case I am specifying the binding locations always in the shaders and hence it seems that it is just enough to bind the buffers and textures accordingly without updating the locations from c++ code. I don't use any uniforms but rather upload all specific data through those buffers (I find it better, easier to read and maintain). I even didn't come to the idea to override this createShaderCacheEntry in my HLMS implementation and it still works for both D3D11 and GL3Plus render systems. The google gave me similar answer about the locations in the shader, e.g. link.