What's the difference between an .fx and .hlsl file anyhow?

Jul 11, 2014 at 6:02 PM
Edited Jul 11, 2014 at 6:13 PM
This relates to a question I got today:
...is it best to separate each shader (i.e. Vertex, Geometry, Tessellation, Pixel) in each file? I can’t seem to find a best practice around this. I see some examples that have all shaders in one .hlsl or .fx file and some in each vs.hlsl, ps.hlsl or vs.fx, px.fx files. Thanks!
Historically the extension '.fx' refers to a HLSL shader intended to be built using the legacy effects fx_4_0, fx_5_0, etc. profiles; while '.hlsl' refers to individual HLSL shaders. The reality is that there's no consistency here. You will see lots of '.fx' files without any technique/pass statements.

Unlike C/C++ source files, HLSL files can contain a lot of stuff that gets ignored in a given invocation of the compiler. If you have a big .fx file with a dozen shaders in it and a bunch of technique/passes, you can compile the same file many times with different entry-points and profiles to generate lots of shaders.

It's really all a matter of how you want to build your shader content. For example, DirectX Tool Kit uses a command-line script to build all the possible combinations of shaders: currrently it builds 86 shader blobs from 7 .fx files (and actually it can build them twice: once with the Windows compiler and once with the Xbox One XDK compiler, but only one set of these shader blobs is included in a given build of the DirectXTK.lib anyhow).

If you use the Visual Studio 2012/2013 content pipeline to build your shaders, you do have to deal with a limitation of MSBuild which assumes each source file is built once for each result. So in that case, you'd want an individual '.hlsl' file for each shader combination (which for the DirectX Tool Kit example would mean 86 '.hlsl' files). One way to handle this would be to keep the shader source itself in a single file, and then have a bunch of .hlsl files that #include the 'source' file. Something like:

#include "BasicEffect.fx"
#include "BasicEffect.fx"
You then compile each of these .hlsl files once with the proper shader profile and entry-point. This approach works fine for smaller projects, but doesn't scale well to large complex shaders without using legacy fx profiles. In that case, other build solutions like DirectX Tool Kit's build solution is probably better.

If you are using legacy fx profiles, you of course only have to compile each .fx once and it will generate all the shaders it specifies in technique/passes in one go.