Some StitchUp updates

The short version

There is a new build of StitchUp available here.

It includes these new features: * The parser supports initial values for parameters. * The parser supports C-style single line comments. * The build output window in Visual Studio now shows a clickable message for the generated effect

The long version

After my last post introducing StitchUp, I received some feedback, including a very detailed comment from "Alejandro" (sorry Alejandro, I'm not implying I'm on first name terms with you, I just don't know your surname). I started to reply to his comment, but it got quite big, so I thought I'd write a new post, which will allow me to wax more lyrical about some new features. I also think some of his questions might be interesting for a wider audience.

So, here are his questions, and my responses:

In the [params] body, how can I set a starting default value? like: [params] float4 color : DIFFUSE_COLOR; // = { 1,1,1,1}; ?

It wasn't possible before, but it is now. I've modified the parser to include support for initial values. So the following declarations are now possible - note that this is only valid within the [params] block, since they are the only variable declarations that initial values make sense on.

[params]
float3 lightDir : LIGHT_DIRECTION = { 1.0f, -1, 1 };
float3 lightDir = { 1.0f, -1, 1 };
float3 lightDir = float3(1.0f, -1, 1);

Next question:

... the BasicMaterial fragment doesn't import anything but has a [vertex] attribute and interpolator. Is there some magic wand going around behind the scenes? Like: "No one output-ed an uv and you need it, so I'll just give to you"?.

Yes, that is exactly what is happening. The code generator follows these rules:

// -------- vertex shader basic_material2 --------
void basic_material2_vs(basic_material2_VERTEXINPUT input, inout VERTEXOUTPUT output)
{
    output.basic_material2.uv = input.uv;
}

Next question:

Is it still possible to #include an .fxh with basic functions and methods? I tried but failed big time. Or the correct practice would be to think about "Helper Fragments" and you would be actually "importing the methods"? (for things like random, poisson things, etc).

Yes, this is supported, in two ways.

  1. You can create a fragment with only a [headercode] block. See DirectionalLight.fragment in the demo project for an example. I think this is the most elegant solution.
  2. You can use #include within an __hlsl__ block, but because of the way I'm calling EffectProcessor with a dynamically generated effect, you'll need to use absolute path names (i.e. C:\...\MyInclude.fxh).

Next question:

Is there a way to see the stitched .fx file (ouput somewhere in some folder). I only see it when there is an error (usually name mismatch). Visual studio keeps it in a temporal folder.

The output is always called StitchedEffect.fx, and it's always saved in your temp folder. You're right that Visual Studio only shows you a clickable error message if there's an error. I've actually asked on the App Hub forums how to do this for informational messages. In the meantime, I have added a message to the build output log, which you can double-click on to open the stitched effect file. The message looks like this:

C:\Users\Tim Jones\AppData\Local\Temp\StitchedEffect.fx :   Stitched effect generated (double-click this message to view).

Next question:

When assigning Visual C++ as the .fragment text editor (to get a little bit of syntax highlighting) commenting a line outside of the hlsl body (via ctrl+e, c) will always crash Visual Studio. (Is there a way to assign NShader as the syntax highlighter for the .fragment? that would be awesome)

The first little issue is that the StitchUp parser didn't allow for comments. I've implemented that now, so you can use C-style single-line comments in your .fragment files:

float3 lightDir : LIGHT_DIRECTION = { 1.0f, -1, 1 }; // This is a comment.
//float3 lightDir : LIGHT_DIRECTION = { 1.0f, -1, 1 };

I hadn't heard of NShader before - that's a nice project. The only way I could see to add support for .fragment files was to change the original source code. Since the developers of NShader are unlikely to be interested in supporting StitchUp-specific extensions, I have created a fork of NShader. You can download the StitchUp-specific VSIX file. So far I have only added support for the .fragment extension, but it would be nice to add support for StitchUp keywords, such as export, import and output. Oh, and you probably don't want to install it at the same time as the official NShader package - in any case, Visual Studio probably won't let you, because I haven't changed of the package GUIDs.

After installing this custom version of NShader, your .fragment files should look like this:

image

Hope this helps - as always, please post thoughts, feedback, etc. below.

13 comments

Gravatar Alejandro Martinez
Nov 19, 2010
04:04

No problem at all "Tim Jones".

That was fast. Well now thats two jaw-drops in a row.

I’ve modified the parser to include support for initial values. Excellent! That perhaps was the only thing that was missing from straight hlsl.

If the fragment doesn’t include a vertex shader, then auto-generate one. Ok, I see the point in that and the nice shortcut that it offers. A shader material that needs 2 UV channels? no problem, just use them! Thanks for clearing this up. Good to know the available paths.

You can create a fragment with only a [headercode] block. See DirectionalLight.fragment in the demo project for an example. I think this is the most elegant solution. Seconded. Saw the light struct and how it ends up in the stitched file. There are still some minor issues though, declaring a method will appear correctly in the stitched file, however using it (i.e. in the phong fragment) will result in a compile error at the compile ps2_0 ps() line (at least that's what it says). The same happens for normal variables like i.e "float3 defaultDirection" [params] fields can't be made static or const, however they can in the [headercode] since they are inside an __hlsl_ body and are handled automatically. This behaviour makes perfect sense, since fragment [params] shouldn't intend to be static (although they could?).

  • Comments worked perfectly. No more issues there.

So far I have only added support for the .fragment extension, but it would be nice to add support for StitchUp keywords, such as export, import and output.

NShader is working as expected. I was looking through the fork source code, perhaps its only a matter of creating a new map file with the added keywords, load it through the map.load method and done. I hope Alexander Mutel (NShader) manages to pull off autocompletion. The round trip would be complete!.

  • I see that you build all .fragment files and add them to the content folder along with the final stitched file. Is there a reason behind this? like custom stitching at runtime depending on machine specs?. On general purpose I will likely be interested in the final file only. however they are tiny files and don't pose a problem at all. Just curious.

  • Clickable file: perhaps what would you want is to copy the resulting .fx file to the same folder path the .stitchedeffect sits (not included in the content project, or included with the compile flag disabled). That way it has easy access to open it and watch it, and perhaps see it through an easy disassembler to see how the final driver compiles the .fx file (this way you can see how many registers, preshaders, etc are being used). Like Renaud Bédard Disassembler app http://theinstructionlimit.com/?p=34

Again, awesome work man. Code generation tools are indeed a very very powerful arsenal.

Gravatar Yufei
Nov 19, 2010
07:46

Hi,

Stitch up is really the kind of tool I am looking for. After tried a little bit, I come up with a feedback:

The only parameter type supported are float(n), bool and matrix, is it possible to support floatNxN and arrays. We don't always need float4x4 but sometime float3x3 or float4x3 as well. And arrays are useful for vertex skinning.

Gravatar Yufei
Nov 19, 2010
09:19

And another one that might be a Bug:

Global variables can't be chained, say I had a Position.fragment that exports a variable:

fragment Position;

[vertex] float4 Position : POSITION;

[vs 20] __hlsl_ void main(INPUT input, inout OUTPUT output) { export(float4, InputPosition, input.Position); } hlsl

and I had a PositionTransform.fragment that did some manipulations but doesn't output the result, instead it export the result to be used by later passes:

fragment PositionTransform;

[parameters] matrix World : WORLD; matrix ViewProjection : VIEWPROJECTION;

[vs 20] __hlsl_ void main(INPUT input, inout OUTPUT output) { float4 position; import(InputPosition, position = InputPosition);

position = mul(position, World);
export(float4, PositionWorld, position);

position = mul(position, ViewProjection);
export(float4, InputPosition, position);

} hlsl

But the generated .fx code is:

void PositionTransform2_vs(PositionTransform2_VERTEXINPUT input, inout VERTEXOUTPUT output) { float4 position; // metafunction: import(InputPosition, position = InputPosition); position = Position0_export_InputPosition; position = PositionTransform2_export_InputPosition;

position = mul(position, PositionTransform2_World);
// metafunction: export(float4, PositionWorld, position);
PositionTransform2_export_PositionWorld = position;

position = mul(position, PositionTransform2_ViewProjection);
// metafunction: export(float4, InputPosition, position);
PositionTransform2_export_InputPosition = position;

}

And this line is where the bug is:

position = PositionTransform2_export_InputPosition;

Hope you can fix it :-)

Gravatar Yufei
Nov 19, 2010
10:45

And the [vs 2_0] & [ps 2_0] really doesn't help much, since even if a single fragment support vs 2_0, the final composed vertex shader might not because of limited instruction slots. Perhaps you can remove these attributes and try to compile againest 2.0, 3.0 and maybe 4.0 to find the minimum profile required by the shader.

Thanks.

Gravatar MadoxLabs
Nov 20, 2010
16:28

Hey Tim - amazing tool! I can't thank you enough for not only making this but releasing the code. I'v been playing with it for the last 2 days and here are some comments:

  • like other posted, there needs to be at least support for float3x3 and float 4x3. And the chained exports is a great idea.

  • texture states is a big requirement for things like shadowmaps and heightmaps etc. I modified your code to allow an initial value on texture variable declarations. The initial value becomes the sampler state such as:

texture2D shadowmap : shadowmap = { MinFilter = Point; MagFilter = Point; }

turns into

texture2D BasicShader0_shadowmap : shadowmap ; sampler BasicShader0_shadowmap _sampler = sampler_state { Texture = ; MinFilter=Linear;MagFilter=Linear;};

  • I also added support for Texture3D and TextureCube

  • My render engine relies on Pass annotations to setup the GraphicsDevice. It would be great to expand .stitchedeffect to contain technique/pass definitions so I can add annotations. This would also allow multiple techniques. Something like:

technique A { pass A { fragments...
} }; technique B { pass A { fragments...
} };

  • pixel output should be able to be defined with multiple COLORs to suport MRT.

And some nitpicky comments:

  • I'm not a big fan of the hlsl delimiters. Shawn's original code didnt have them :)
  • Not a big fan of the output() behaviour but I see the need for it (so far!). Also its inconsistant when output.position doesnt use output().
Gravatar MadoxLabs
Nov 20, 2010
16:31

There is a cut and paste error on the texture sampler code where Linear should be Point but you get the idea I hope.

Gravatar Tim Jones
Nov 21, 2010
16:41

Thanks for the feedback - it's great to hear that this might be useful to somebody (and maybe even somebodies).

Yufei andMadoxLabs

I have added support for matrix data types and arrays.

`Yufei - The chained export / import bug is an interesting one. There certainly was a bug - "import(InputPosition, position = InputPosition);" shouldn't have included the export from later in the fragment - this is fixed. However, as you've written it, the third fragment (which you didn't include) which imports the second export won't work, and it wouldn't have worked with Shawn's original system either. This is because a call to "import" will pick up ALL previous exports matching that name, not only the previous one. So you'd end up importing the exports from both Position.fragment and PositionTransform.fragment. That behaviour is both powerful and desired, but obviously doesn't help you for what you're trying to do. Will simply using a different name work?

`MadoxLabs - I have logged an issue for supporting sampler states, but I'd like to get some more feedback on the syntax before implementing it. I'm not totally comfortable with using a sampler_state definition to "initialize" a texture variable, but admittedly I can't think of a better solution.

I have started a discussion around the best way to implement techniques - please vote and comment with your thoughts.

I have added support for Texture1D, Texture3D and TextureCube.

I have added support for MRTs.

I have tried to explain my rationale behind the hlsl delimiters.

I have run out of "I have's" ;-)

Gravatar Tim Jones
Nov 21, 2010
17:12

@MadoxLabs - I meant to say, having read your article on your grand unified model renderer, I should point out that StitchUp doesn't (yet) support annotation syntax. If you'd like it to, please log an issue - annotations would certainly be useful to have.

Gravatar MadoxLabs
Nov 21, 2010
18:06

Haha, you found my post, eh? I didn't link it here directly because I felt like it isn't really ready for primetime yet. However, Stitchup doesn't need to support annotations more than passing them through to the final .fx file.

Initializing the texture that way was just a quick hack using the new feature you provided, but I think doing something more 'proper' would only add verbosity and nothing else. Maybe thats good though.

Do you mind me logging issues for other things I'd like you to check out?

Gravatar Tim Jones
Nov 21, 2010
18:15

@Madox - Actually, since StitchUp's parser is response for anything outside hlsl blocks, and since it currently doesn't know what annotations are, I would need to do some work to add support for them (similar to what I did for supporting arrays and initial values).

Yes, please do log issues - if I think they'll be useful for a majority [and if they're not too hard :)] then I'll do my best to implement them.

Gravatar MadoxLabs
Nov 22, 2010
02:24

OK, I started adding issues for things I ran across and made changes for. I'm trying to only enter ones useful for a majority and not ones that are just for me (like changing ReplaceOutputCalls() to work on 'output.' instead of 'output()')

Let me know if its too much. I was just about to start making my own tool when I found yours, so I'm pretty excited.

Gravatar Yufei
Nov 22, 2010
09:19

Yes, "import" do pick up ALL previous exports matching that name as I saw in your code, and I just modefied the PreProcesser and make it first process imports then exports, then everything magically works in my case. This way, the imports in the current fragment is not included.

And for data types, it seems you don't have much good uses for them, why not just use a string to represent everything, there are also Half(n) and whatever odd types available in HLSL.

Gravatar Tim Jones
Nov 22, 2010
09:29

`MadoxLabs - no, not too much :) I'll take a look at them in more detail shortly.

`Yufei - I made the same change in PreProcessor, and I've committed that already. What I meant was that your third fragment must have something like this:

import(InputPosition, position = InputPosition);

which would expand to:

position = Position0_export_InputPosition; position = PositionTransform2_export_InputPosition;

... which I guess works, but only because the second assignment overwrites the first one. If you were doing another operation (i.e. += or *) then it wouldn't do what you expect.

True, I could remove the data type parsing, but I quite like having it in there - it means I can pick up on syntax errors early in the process, and show nice error messages that link to the correct line and column in the .fragment file. Yes, it does mean I need to do some more work to support all HLSL data types. Please do log issues for data types that you'd like to see added - eventually StitchUp should support all HLSL data types. The only reason it doesn't right now is because I'm lazy ;-)

Make a comment

Sorry, commenting has been temporary disabled because of spam. If you have any questions, you can email me, and you can also find me on Twitter.