Jump to content
  • Advertisement
Sign in to follow this  
Tispe

Multiple shaders in the same HLSL file (no effect framework)

This topic is 2077 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi again :)

 

Currently I have one .hlsl file for the vertex shader and one .hlsl file for my pixel shader as resources. I do not use the effect framework. I am going to add more shaders to my game and instead of creating more files I thought I could just place new shaders into the same files I already use. But then if that is possible, the globals/constants would then be the same for both shaders. And if then, do I use more then one constant table, or just one per .hlsl file? 

 

Since D3DXCompileShaderFromResource's 5th parameter is the name of the shader function I thought I could add more shaders within the same file.

D3DXCompileShaderFromResource(
		NULL,
		MAKEINTRESOURCE(IDR_VERTEXSHADER),    //filepath
		NULL,				//macro's
		NULL,				//includes
		"vs_main",			//main function
		"vs_3_0",			//shader profile
		D3DXSHADER_DEBUG|D3DXSHADER_SKIPOPTIMIZATION,				//flags
		&code,				//compiled operations
		&debugcode,			//errors
		&VSConstantTable);

Is this possible, and how many Constant Tables should I need?

 

Later on when I am happy with the shaders, I will compile them offline and pack the shader binary as a resource. Any things I need to consider at this stage aswell?

Share this post


Link to post
Share on other sites
Advertisement

The constants used by each individual shader should be independent of one another.  For example, if you have 10 constants, and only 5 of them are shared among shaders and the other five are mutually exclusive, then you would get less than 10 (but greater than or equal to 5) constants required for each shader.

 

With that said, I would highly recommend against doing this.  You don't gain anything by putting them into one file, and it adds complexity when you want to dig through a shader.  This is somewhat analogous to putting all of the classes in your C++ code into one header and one CPP file - the same arguments against doing this apply to your idea here.

 

Instead I would group all of the shaders that will be used together (i.e. one pipeline configuration) into a single file.  This is pretty easy to manage, and if you will have shared shaders (for example multiple pipeline configurations use the same vertex shader) then you can always make that shared shader into its own file and just use an include mechanism to bring it into the overall file.

Share this post


Link to post
Share on other sites

Shader constants are global to the device in D3D, not per shader, so if you've two shaders that both use a constant in register slot 0, updating register slot 0 via device->Set*ShaderConstantF (which the constant table is just a software abstraction of) will make the updated value available to both shaders.  The exception to this is that vertex shaders and pixel shaders have separate constant slots.

 

The easiest way I've found to manage all of this is to decide up-front that you're going to use dedicated slot numbers for specific data (e.g. constant slot 0 for vertex shaders always contains your MVP, slot 0 for pixel shaders always contains a colour value, etc) and be consistent about how you declare these in your HLSL sources (using a #include via an ID3DXInclude interface - which is really easy to set up and will even work with resources (which I see you're using) - can help a lot here).  Declare your shader constants in this include file with explicit registers (e.g. "float4x4 MVP : register(c0);") to keep things really consistent and robust.

 

Taking this a step further, you can #define VSCONSTANTMVP "c0" in your C++, then use "register(VSCONSTANTMVP)" in your HLSL (passing the define of VSCONSTANTMVP to HLSL via a D3DXMACRO).  Going even further again, you can #define VSCONSTANTMVP 0 in C++, build the "c0" string dynamically (using your favourite string library) before compiling the shader, pass that as the D3DXMACRO, then using device->SetVertexShaderConstantF (VSCONSTANTMVP, ... to set the constant value in C++.  You won't even need a constant table using this method, and I find that keeping things explicit in such a manner makes everything more robust which is a fair tradeoff for a little extra work.

 

If you really want to put multiple shaders into the same file, you can use #ifdef and pass the define for the shader you're currently compiling via the D3DXMACRO parameter to your D3DXCompileShaderFromResource call.  One way of doing this is to keep related vertex shaders and pixel shaders together, and use #ifdef VERTEXSHADER and #ifdef PIXELSHADER.  Another is to use a specific define for each shader.  Ultimately you get to choose which method is most suitable for your own use case.

Share this post


Link to post
Share on other sites


Instead I would group all of the shaders that will be used together (i.e. one pipeline configuration) into a single file.

 

Good, this means I can just add another function and compile it passing NULL as constant table, use the first VSConstantTable to set constants (only MVP for shadow map change) for both shaders?

D3DXCompileShaderFromResource(
		NULL,
		MAKEINTRESOURCE(IDR_VERTEXSHADER),    //Same file
		NULL,					
		NULL,					
		"vs_MyOtherFunction",			//Shadow map function
		"vs_3_0",				
		D3DXSHADER_DEBUG|D3DXSHADER_SKIPOPTIMIZATION,				
		&code,					
		&debugcode,				
		NULL);			//Dont't need another one???

Share this post


Link to post
Share on other sites

I'm doing it very similarly to what mhagain said, plus I have my own little "effect framework" that allows me to add specific code at the beginning of an .fx file into /* */ which then is parsed and can control separate "techniques" - each can declare its own macros for #ifdef branching, set render states etc.

I have related vertex and pixel shaders in the same file and I don't fully understand this:

If you really want to put multiple shaders into the same file, you can use #ifdef and pass the define for the shader you're currently compiling via the D3DXMACRO parameter to your D3DXCompileShaderFromResource call.  One way of doing this is to keep related vertex shaders and pixel shaders together, and use #ifdef VERTEXSHADER and #ifdef PIXELSHADER.  Another is to use a specific define for each shader.  Ultimately you get to choose which method is most suitable for your own use case.

Why would you need the #ifdef VERTEXSHADER etc? You pass the name of the function anyway ("vs_main" in the first Tispe's post), don't you? And if you want to use the same register for different purposes in vertex and pixel shader, you can specify it explicity in HLSL like register(ps, c4). But I'm absolutely not an expert, so I'm probably just missing some important point in your post.

Share this post


Link to post
Share on other sites

Good, this means I can just add another function and compile it passing NULL as constant table, use the first VSConstantTable to set constants (only MVP for shadow map change) for both shaders?

Constant table is just a "list" of all constants used in the particular shader, with names of the variables, corresponding starting register numbers and register counts (matrices take 4), register types (float, boolean...), default values and some other stuff.

So theoretically it should be possible to use constant table created from compiling one shader also for another shader, if you know that it uses the same variables. But I've never tried it so I cannot guarantee that D3DX doesn't tie them together somehow internally. You'll have to try it ;) Also - if you declare the variables in HLSL without the register(xx) keyword, then you IMHO cannot be sure that the variables will be in the same registers in both shaders! In this case the constant table would not work for all of them. It would probably be fine if the shaders really had the very same global variables declared in the same order and all used in all the shaders.

 

But you don't have to use constant tables at all, you can set the variables manually by device->SetVertexShaderConstantF and similar methods, which the constant table is doing internally anyway. The difference is that using constant table you can access the variables by their names as they appear in HLSL, but using SetxxxShaderConstantX you must know the register number. But if you do (if you follow what mhagain said), then why not to do it smile.png

Edited by Tom KQT

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!