visual-studiohlslfragment-shadermsbuild-task

Build pixel shader project in VS2022


I have an old class library project that was building fine in a previous version of Visual Studio (2017 IIRC). The project contains an HLSL-based pixel shader that generates a radial color picker. I have just ported it to VS2022 and it is no longer building telling me that it cannot find a build task. Here is the error message:

The "ShaderBuildTask.PixelShaderCompile" task could not be loaded from the assembly ShaderBuildTask, Version=1.0.3072.18169, Culture=neutral, PublicKeyToken=44e467d1687af125. Could not load file or assembly 'ShaderBuildTask, Version=1.0.3072.18169, Culture=neutral, PublicKeyToken=44e467d1687af125' or one of its dependencies. The system cannot find the file specified. Confirm that the declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.AngularGradientShaderEffect

I have already installed the required shader compiler (ShaderBuildTaskSetup.msi) on my machine. Reloading the project and restarting VS don't help either.

Has anyone used pixel shaders in VS2022 to see what I'm missing?


Solution

  • The ShaderBuildTask MsBuild task assembly that one can see used and talked about in various places on the Internet is dated more than 10 years ago and was written in C++ for an x86 processor. It also depends on files that may not be there or cannot be loaded easily today (DirectX9, etc.).

    This assembly contains only one task wich is PixelShaderCompile. Its role is to take .fx files ("effect files") from a project (BTW not necessarily a C# project), compile them as .ps files (using the D3DXCompileShader function or similar) and add them to the project as resources.

    This work can be replaced by the Windows SDK's Effect Compiler tool (fxc.exe) and the MsBuild Exec task. For example this task will compile all .fx files in the project as .ps file, just like PixelShaderCompile does:

    <Exec Command="&quot;$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.22621.0\x64\fxc.exe&quot; /T ps_3_0 /Fo @(Effect -> '%(Filename).ps') @(Effect)"/>
    

    Note the Windows SDK is required and the path must be adapted to its current version (here 10.0.22621.0).

    So, to integrate this task you must:

    1> Remove the

    <UsingTask TaskName="ShaderBuildTask.PixelShaderCompile" AssemblyName="ShaderBuildTask, Version=1.0.3072.18169, Culture=neutral, PublicKeyToken=44e467d1687af125" />
    

    or similar line from the project file (here .csproj).

    2> Replace the lines

    <Target Name="EffectCompile" Condition="'@(Effect)' != '' ">
      <PixelShaderCompile Sources="@(Effect)">
        <Output TaskParameter="Outputs" ItemName="Resource" />
      </PixelShaderCompile>
    </Target>
    

    by something like this:

    <Target Name="EffectCompile" Condition="'@(Effect)' != '' ">
      <Exec Command="&quot;$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.22621.0\x64\fxc.exe&quot; /T ps_3_0 /Fo %(Effect.RelativeDir)%(Effect.FileName).ps %(Effect.Identity)"/>
    
      <!-- Add this if you need to embed the file as WPF resources -->
      <ItemGroup>
        <Resource Include="%(Effect.RelativeDir)%(Effect.FileName).ps" />
      </ItemGroup>
    </Target>
    

    Note one bonus benefit of this is you can tweak the fxc.exe command line to some specific needs which you coudn't do with the PixelShaderCompile task.