5

Closed

DGSLEffect for supporting VS 2012 Content Pipeline

description

Currently the CMO loader maps to BasicEffect (and eventually SkinnedEffect) which means it always uses built-in shaders. This ignores any pixelShader in the material definition which removes the DGSL authored shaders from the model.
Closed Oct 15, 2013 at 2:19 AM by walbourn

comments

walbourn wrote Mar 8, 2013 at 9:40 PM

The idea here is to have another built-in Effect which includes the Vertex Shader from the VSStarterKit, and then loads the Pixel Shader from the material. This would be applied via a new DGSLEffectFactory when passed to the CMO loader, or you would continue to get the existing behavior if using the standard EffectFactory with the CMO loader.

walbourn wrote Mar 8, 2013 at 9:43 PM

Note DGSLEffectFactory would need to include the VSStarterKit's fallback for Feature level 9.x support since DGSL shaders only currently support SM 4.0.

JochenCP wrote Mar 11, 2013 at 2:02 PM

Hi,

I would like to add my 2 cents here.

Imho this problem is just an incarnation of a different problem, which would be:
"I need customization." ( In this case, where do I get my data( assets ) from )

So why not take the idea a little further and supply something like CustomEffect and EffectFactoryAssetAccess.

DGSLEffect could then be a specialized implementation of CustomEffect. that handles things the DGSL way.

The code below is from my approach implementing custom effects.

Assumed that:
  • visualResourceId_ is some kind of stringified asset id (In the simplest of all cases just the models filename) and
  • GetEffectFactory() Returns a EffectFactoryAssetAccess,
It is used like this:

For Loading
auto assetData = assetAccess_.GetAsset( visualResourceId_ + ".cmo" );
auto effectFactory = GetEffectFactory();

effectFactory->SetModelContextKey( visualResourceId_  );
model_ = DirectX::Model::CreateFromCMO( GetD3DDevice(), assetData.data(), assetData.size(), GetEffectFactory(), true, true );
For Rendering:
model_->UpdateEffects( [&]( DirectX::IEffect* i_effect ) { UpdateEffect( i_effect, visualResourceId_  ); } );
model_->Draw( rendererFactory_.GetD3DDeviceContext(), GetCommonStates(), GetWorldMatrix(), GetViewMatrix(), GetProjectionMatrix(), false, [&] ( void ) { UpdateRenderStates(); }  )
With an Implementation like this:
class IAssetAccess
{
public:
    typedef std::vector< std::uint8_t > DataContainerType;
        
        virtual const DataContainerType& GetAsset( const std::wstring& i_assetKey ) = 0;
    virtual const DataContainerType& GetAsset( const std::wstring& i_contextKey, const std::wstring& i_assetKey ) = 0;
}

class ITextureFactory
{
public:
    TextureFactory( ID3D11Device* i_device, IAssetAccess& i_assetAccess );

    void ClearContextKeys( void );
    void SetModelContextKey( const std::wstring& i_contextName );
    void SetEffectContextKey( const std::wstring& i_contextName );

    ID3D11ShaderResourceViewPtr CreateTexture( const std::wstring& i_textureName );
};

class EffectFactoryAssetAccess
{
        EffectFactoryAssetAccess( ID3D11Device* i_device, IAssetAccess& i_assetAccess ) :
                textureFactory_( new TextureFactory( i_device, i_assetAccess ) )
        {
             // ripped out for simplicity reasons
        }

    void SetModelContextKey( const std::wstring& i_contextName )
        {
             // ripped out for simplicity reasons
        }


        std::shared_ptr<DirectX::IEffect> EffectFactoryAssetAccess::CreateEffect( const  DirectX::EffectFactory::EffectInfo& i_info, ID3D11DeviceContext* /*i_deviceContext*/ )
        {
            // test if the effect is in our cache...
                // ripped out for simplicity reasons

            // (re)create the effect
            auto effect = MakeCustomEffect( i_info.name  );

                // set the context key for current effect
                textureFactory_->SetModelContextKey( modelContextKey_ );
                textureFactory_->SetEffectContextKey( i_info.name );

            // load and assign vertex shader
            auto vertexShaderData = assetAccess_.GetAsset( "VertexShader", std::wstring( i_info.name ) );
            effect->SetVertexShader( std::move( vertexShaderData ), *textureFactory_ );
    
                // load and assign pixel shader
                auto pixelShaderData = assetAccess_.GetAsset( "PixelShader" std::wstring( i_info.name ) );
                effect->SetPixelShader( std::move( pixelShaderData ), *textureFactory_ );
    
                textureFactory_->ClearContextKeys();

            effectCache_[ i_info.name ] = effect;
    
            return effect;
        }

        // overload in case you want a more specialized effect to be created.
        virtual std::shared_ptr<DirectX::IEffect> MakeCustomEffect( const WCHAR* i_effectName )
        {
                std::make_shared<CustomEffect >( d3DDevice_, textureFactory_, const WCHAR* i_effectName  );
        }
}
answering some questions in advance...

Why is EffectFactoryAssetAccess::CreateTexture() not implemented?

well we don't need it, we ignore the texturename passed in via EffectInfo completly, just like we ignore any other parameters in EffectInfo.

We are a custom effect, we might need more than one texture or no texture at all. It all depends.

What are these context keys for, i just don't get it?

Think of the following situation:

You wan't to display two models using the same effect, but each model should have it's own set of textures (normal map, light map, colormap, etc... ). If so we need context to be able to find the correct textures. Well what you see here is just my approach.


So what should CustomEffect::SetXXXShader(), CustomEffect::Apply() and CustomEffect::GetVertexShaderBytecode() look like?

To start with the simplest GetVertexShaderBytecode is fairly obvios: return the byte code passed in via SetVertexShader().

SetXXXShader() and Apply() really shouldn't do very much at all. Better put what you need in a derived specialized class like DGSLEffect.

What does your Version of CustomEffect look like?

Well it's custom, that's what I had in mind here. An approach to enable customization.

But to be a little more precise, there are many. However they all look similar in a way that they use ID3D11ShaderReflection* to gather information about the used shader vaiables and textures, that are assigned to via Custom interfaces - just like IEffectMatrices.

Here is an example:
class ActiveAreaRenderTaskEffect:
    public DirectX::IEffect,
    public DirectX::IEffectMatrices,
    public IWorldTimeEffect
{
...

void ActiveAreaRenderTaskEffect::SetWorldTime( WorldTimeTypeSecond i_worldTime )
{
    // scale down to days for shaders, to keep values in a reasonable accuracy range
    float dayTimeSeconds = static_cast< float >( MakeDayTimeSeconds( i_worldTime ) );
    SetVertexShaderValue( vsDayTimeSecondsMapping_, dayTimeSeconds );
}

}
It turned out to be little more than 2 cents, apologies for the wall of text.

Also my apologies for drifting from the original DGSLEffect question, but I really think a DGSLEffect would be best implemented as a specialized Customization, where enabling "simpler" customization would also open the door for other useful effect implementations.

In case you find my comments any useful, please allow me one more note:
You might also wan't to consider supplying some more customization friendly CreateFromCMO and Draw methods.

CreateFromCMO could simply drop (..., bool ccw, bool pmalpha ),

and Draw would look something like:

void Draw( In ID3D11DeviceContext* deviceContext, std::function<void(IEffect*)> updateEffect std::function<void()> setState ) const;

where setting states is up to the CusomEffect completly, which itself could use CommonStates of course, but is free to not use them.


Best Jochen

ShawnHargreaves wrote Mar 11, 2013 at 6:36 PM

why not take the idea a little further and supply something like CustomEffect and EffectFactoryAssetAccess
Thanks for the feedback!

I'm not understanding what would CustomEffect provide above and beyond the base IEffect interface? That has no implementation (because it's custom!) but can be implemented by anyone who wants to hook up custom behaviors or parameters to whatver shader(s) they desire.

walbourn wrote Mar 11, 2013 at 7:15 PM

IEffectFactory is already an interface for this kind of customization. "EffectFactory" is a concrete default implementation that always return a BasicEffect. This already makes it possible to apply custom effect policy to a SDKMESH or CMO file without having to modify the loader code. This applies to both Effect instances and Textures. The Effect system itself already has a number of interfaces (IEffect, IEffectMatrices, IEffectLights, and IEffectFog) so that custom effects can be communicated with in a generic sense. Really to get the kind of behavior you are describing, you are supposed to provide your own implementation of IEffectFactory to the loader and do all your magic changes based on material information from there (likely the 'material name' as a tag).

Note for animation control, we probably want an IEffectAnimation as well so that you can more easily write a SkinnedEffect replacement.

This particular work item is to implement another built-in Effect that implements the VS 2012 DGSL shader solution as embodied in the VS Starter Kit. As a convenience, it would include a DGSLEffectFactory to use with the CMO loader to be fully functionally equivalent to using the VS Starter Kit to display the CMO.

JochenCP wrote Mar 11, 2013 at 10:30 PM

I hope it is O.K. to reply in this issues comments section again. I don't want to clutter the work item to much.
Please let me know if you want to take this to an other place...


@ShawnHargreaves:
I'm not understanding what would CustomEffect provide above and beyond the base IEffect interface?
I see your point here, and I agree.

I kind of contradict myself when saying custom custom custom, but on the other hand introducing restrictions over Apply and GetVertexShaderByteCode...

Well it is there just to fulfill the minimal requirements of EffectFactoryAssetAccess. Effectiveliy covering the minimum requirements almost every effect I could think of has - which is a vertex shader and a pixel shader.


@walbourn:
Really to get the kind of behavior you are describing, you are supposed to provide your own implementation of IEffectFactory to the loader and do all your magic changes based on material information from there (likely the 'material name' as a tag).
I accidentally also ripped the ": public IEffectFactory " from

class EffectFactoryAssetAccess : public IEffectFactory, private NonCopyable

when I pasted the code in here in my last comment. EffectFactoryAssetAccess is this own implementation I apply to the loader. Sorry if this wasn't understandable in the first place.

Just to explain, why I even posted here:
I was looking at the neat Mesh Content Pipline and DGSL editor in 2012... what I was missing was a simple way to put it into my application. By looking around, I found DirectXTK could do it.

However it didn't really work out as smooth as I expected, on one side because of the lack of custom VS support in combination with DGSL editor, but that's a different story, and on the other side because I found the DirectXTK CMO Loader API as used in the example already is kind of specialized in a way that didn't match my requirements.

Sure it's like both of you say the necessary interfaces to utilize CMO loader code are there: IEffect and IEffectFactory, and I ended up deriving from these to get what I needed. I just wanted to supply some input on what helped me get closer to a solution and could also be useful to others.

"DGSLEffect for supporting VS 2012 Content Pipeline" is what I was initially looking for and I would love to use an out of the box DirectXTK solution over my custom one in the future. Especially if it plays well with things like multiple textures, abstract assets loading, render to texture.

Thanks for your comments.

Best Jochen

wrote Oct 15, 2013 at 2:19 AM

Resolved with changeset 31545: DirectXTK: Added DGSLEffect and DGSLEffectFactory

wrote Oct 16, 2013 at 6:20 PM

Associated with changeset 31566: DirectXTK: Updated DGSLEffect with SetAlphaDiscardEnable and SetVertexColorEnabled

wrote Oct 23, 2013 at 6:13 AM

Associated with changeset 31680: DirectXTK: DGSLEffect fixes and cleanup