ShaderReflection; stripping information from an Effect

Started by
27 comments, last by MJP 11 years, 6 months ago

But now if I understand correctly the whole concept of "Techniques" and "Passes" is just an abstraction for selecting different bits of shader bytecode to bind to the device? And that would also mean I'm going to have to parse the technique/pass definitions and implement a whole new technique and pass system in the engine to use it? If that's the case I can no longer use EffectPass.Apply() anymore either... thus I'm going to need to... I dunno... bind shader bytecode individually to the device interface's "PixelShader", "VertexShader", and "GeometryShader" (and the other stages for D3D11, of course) objects? I've never even seen anyone do this before, hmmm... However, if I understand correctly doing all this work will be worth it because I will have a very powerful/flexible system where I can dynamically piece together complete effects from fragments on-the-fly and (theoretically) generate tons of effect permutations from fragments?


That's pretty much the idea behind it, yes smile.png
You could write a parser for .fx files, but that might not be an ideal situation. In my shader system I separate the actual shader code (in .hlsl files, not .fx files) and the shader program definition (ie. the matching of vertex shaders, pixel shaders, geometry shaders, etc.) into 2 different files so you don't get one huge file with too much differing data in it. This allows for very easy mixing and matching.
The shader program definition file is just a plain XML file specifying where to look for the source code (can be multiple plain .hlsl files), which entry point to use for each stage, which preprocessor defines to use, which compilation flags to use, etc. Blend, rasterizer and depth states are handled by the pipeline itself and not by the shader, and I have no concept of passes or techniques. I do allow for multiple shader 'setups' which correspond to a specific render pipeline setup so I can easily change how my pipeline works on the fly, but that's not really all that relevant here I suppose.

Because the shader definition file is just plain XML it becomes very easy to push it through my content pipeline without having to write any fancy parsers. I just use a generic serialization system to store all the values in my XML file, and I use these values to let my compiler build a binary shader definition file (which also encompasses my shader binaries) which can be loaded into my engine very fast with a minimal amount of parsing.
This basically comes down to that tool you mentioned at the end of your post smile.png

I gets all your texture budgets!

Advertisement

And even then, you can still use the FX file format - the difference is you'd have an offline compiler tool that parses the format for techniques/passes in order to acquire the necessary shader profiles + entry points. So with the profile/entry point in hand, you can run the FX file through the D3DCompiler to get a ShaderByteCode object for each shader, then use that to create a reflection object to query all your meta data. Then write out the reflected meta data to a file, that gets consumed by your application at runtime - which would be your own implementation of Effects11 (or something completely different, either way...you use the meta data to automatically setup your constant buffers, bind resources, and manage the shader pipeline by directly using the Direct3D11 shader interfaces).

For parsing, you can use something like Tiny Parser Generator, which is a really neat little tool if you know how to create a grammar. This is a problem that I've been working on myself for my own software and the approach has been working out pretty well (and frankly, I love TinyPG). I also believe the MonoGame folks have adopted this method, so that may be a good place to gather some ideas.

You are right, that is basically what has been done recently in SharpDX.Toolkit to support parsing of HLSL FX shaders:

- I decided to almost parse fx file format (only the content of technique/passes) in order to keep things quite compatible with legacy shaders and ease porting (though the syntax is slightly simplidied and different, check for example BasicEffect.fx link below)
- Write a parser, done by hand (nothing more than a couple of hundred of lines) and is mainly parsing technique/passes and the content of a pass, but the syntax supported is slightly closer to D3D9, producing an AST.
- Write a custom metadata fileformat (EffectData in the toolkit) that is able to save the whole description of a shader (shader bytecodes, constant buffers, value & resource variables, techniques/passes), nice thing is that It supports multiple effects inside a same archive that can be merged (like a zip of effects)
- Write a compilerthat takes the AST produced by the parser and compile all shaders and put them in the metadata format.
- A command line tool "fxc.exe" like called "tkfxc.exe" which basically allow to generate an archive of effects (unlike legacy FX, I can then put several effects inside the same tkfxo file), it is also outputing a friendly colored output as the original fxc.exe.
- Then you have to rewrite the whole Effect framework

Getting all this efficiently written is really really a long way (correct handling of constant buffers update, minimize calls to XXSetShaderResource/SetSamplers...for each shader stages...etc.), but in the end the result is pretty close to XNA effects (thus, I have been able to reuse all StockEffects from XNA, for example BasicEffect.fx is very similar to the original one, only techniques/passes declaration is slgihtly changing) but a bit more flexible as It can provide some extras features like linking of shader declared from another shader (export/import of a particular shader shareable between effects), or constant buffer pooling, or automatic access to all non-builtin variable declared in the effect pass at runtime (if you declare Toto = 1.0f; in the effect pass, you will be able to get this value from C#).
Very nice, I didn't realize you've released code for your toolkit (I heard mentions of it a while ago). But yeah, exactly the process that I've went with.

FYI, TinyPG produces code for a scanner/parser/parsetree, so there isn't any runtime dependencies. All you write is a grammer (e.g. terminals, production rules to parse and evaluate technique/pass expressions, etc). It's a lot simpler than writing out your own parser by hand.

Very nice, I didn't realize you've released code for your toolkit (I heard mentions of it a while ago). But yeah, exactly the process that I've went with.
FYI, TinyPG produces code for a scanner/parser/parsetree, so there isn't any runtime dependencies. All you write is a grammer (e.g. terminals, production rules to parse and evaluate technique/pass expressions, etc). It's a lot simpler than writing out your own parser by hand.

TinyPG is indeed really nice and the generated code is concise and relatively efficient. But there are a couple of things that would require probably some changes to TinyPG in order to parse correctly HLSL with preprocessor:

  1. For example, in my case, I parse and build an AST only for a technique block, everything else is eaten by the parser but skipped. I'm only checking that all braces {}[]() are matching until a technique is found.
  2. I'm handling preprocessor (though D3DCompiler.PreProcess) and parsing #line directive in order to provide the exact line/file error from an included file. This would probably required some changes in TinyPG generated code or at least quite some code to re-process all parse tree errors and adjust line (and provide file information). Also a preprocessor doesn't fit well with a general grammar as it can be interleaved everywhere, so most of the time, you need some entry point between the scanner and the parser in order to handle them correctly (and I don't think that TinyPG has this kind of extension points).
You guys are the best! :-)

If I'm understanding everything correctly the days of writing big .fx files full of various techniques and passes are waning, and we're heading towards the use of these "shader fragments" to give us absolute power over rendering with effects? If that is indeed the direction the industry is going in then I won't feel so bad about all the work I now have to do lol...

@ xoofx:

This toolkit is already a finished part of SharpDX? I'm already targeting SlimDX, but from the sound of it I could possibly use your code for my effect importer tool?

EDIT:

BTW, I'm about to look in the DirectX SDK for Microsoft's Effects Framework source to see how they wrote the framework... hopefully it will be of use to teaching me how to work with shaders at this new, lower level... Could someone post the path to the source within the SDK in case I have any trouble finding it?
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________

BTW, I'm about to look in the DirectX SDK for Microsoft's Effects Framework source to see how they wrote the framework... hopefully it will be of use to teaching me how to work with shaders at this new, lower level... Could someone post the path to the source within the SDK in case I have any trouble finding it?


It can be found in <DXSDK Directory>\Samples\C++\Effects11

The code itself isn't pretty though IMO, so be warned

I gets all your texture budgets!


It can be found in \Samples\C++\Effects11

The code itself isn't pretty though IMO, so be warned


Yep, I'd just found it a few mins ago and thought the same thing... :P

But it's the functionality and ideas I need, not their code verbatim...
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________
Could someone please help me understand how on earth I compile the REST of my effect programs (e.g., the global variables, pre-processor directives, includes (e.g., fxh files), etc)? I still don't see how I put all this together by just compiling individual shader functions, or what the hell I'm supposed to do lol....

EDIT:
This just gets more and more confusing... everywhere I look online it appears that virtually everyone is using the Effects Framework despite its depreciation... there's little/nothing available out there to help me... And Microsoft's Effects Framework implementation is huge... thousands and thousands of lines of code. It's going to take forever to figure out how to do this by reading their code... If I understood/knew the steps of how to piece things together and what I need to be doing writing the code would be the easy part... But I just dunno wtf I'm supposed to be writing lol...
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________

Could someone please help me understand how on earth I compile the REST of my effect programs (e.g., the global variables, pre-processor directives, includes (e.g., fxh files), etc)? I still don't see how I put all this together by just compiling individual shader functions, or what the hell I'm supposed to do lol....

EDIT:
This just gets more and more confusing... everywhere I look online it appears that virtually everyone is using the Effects Framework despite its depreciation... there's little/nothing available out there to help me... And Microsoft's Effects Framework implementation is huge... thousands and thousands of lines of code. It's going to take forever to figure out how to do this by reading their code... If I understood/knew the steps of how to piece things together and what I need to be doing writing the code would be the easy part... But I just dunno wtf I'm supposed to be writing lol...


The compiler interface provided by D3D takes care of all your compilation needs. It will take care of include files, global variables, compilation targets, compilation flags, etc.
Have a look at the various Compile methods in the ShaderByteCode class.

I wouldn't focus too much on microsoft's implementation of the effects framework, I can say from my own experience that a similar system including techniques and passes can be written with a much simpler implementation.

An effect is a collection of techniques, a technique is a collection of passes, and a pass encompasses your render state (shaders, rasterizer state, blend state, etc.)
SlimDX provides ways for you to bind data to your shaders using the shader wrapper classes (like VertexShaderWrapper), so that's a starting point for binding your constant buffers, samplers, etc. Where to put your separate variables into your constant buffer and which names, sizes and default values they have can be found from the shader reflection class.

Applying a pass means binding the state the pass encompasses to the D3D device, executing a technique means executing passes sequentially.

That's really all the info you'll need for building a system like the effects framework tbh.

I gets all your texture budgets!

Thanks again, Rad...

I just realized the DirectX SDK has some sample projects for "HLSL without FX". Now that I'm looking at it this is beginning to make more sense. It shows me what the heck is going on behind the scenes of the FXF which I have not seen very often.

What I'm still not getting though is how we have "shared constant buffers"... In an effect file we have global variables which can be (and are) consumed by multiple shaders of multiple stages. Now how do I achieve that? It would seem stupid to do something like this:

// VS.vsh
*constant buffer(s)*
someFunction();

// PS.psh
*same constant buffer(s)*
anotherFunction();

...and so on...

How do we mitigate this issue/problem?
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________

This topic is closed to new replies.

Advertisement