JeremyAnsel/JeremyAnsel.HLSL.Targets

How to properly embed shaders as resources?

samhocevar opened this issue · 3 comments

Hello! I am trying to embed a compiled shader as a resource. My first attempt was to simply put this in the .csproj:

  <ItemGroup>
    <HLSLShader ShaderProfile="ps_3_0" Include="Internal/TintEffect.hlsl" ObjectFileOutput="$(OutputPath)\TintEffect.ps" />
    <EmbeddedResource Include="$(OutputPath)\TintEffect.ps" LogicalName="TintEffect.ps" Visible="False" />
  </ItemGroup>

This works as expected; the .hlsl file gets compiled to a .ps file in $(OutputPath), which is then embedded as a resource.

However, the issue is that this happens too late in the build stage, and causes MSBuild to always consider the project as out-of-date, for instance:

1>FastUpToDate: Input EmbeddedResource item 'C:\Users\sam\emoji.wpf\Emoji.Wpf\bin\Debug\net40\TintEffect.ps'
     (2023-01-30 14:10:59.853) has been modified since the last successful build started (2023-01-30 14:10:59.754),
     not up-to-date. (Emoji.Wpf)

So I tried putting the group inside a target that is run sooner:

  <Target Name="CompileShaders" BeforeTargets="BeforeBuild">
    <ItemGroup>
      <HLSLShader ShaderProfile="ps_3_0" Include="Internal/TintEffect.hlsl" ObjectFileOutput="$(OutputPath)\TintEffect.ps" />
      <EmbeddedResource Include="$(OutputPath)\TintEffect.ps" LogicalName="TintEffect.ps" Visible="False" />
    </ItemGroup>
  </Target>

But now the project is no longer rebuilt even if I modify TintEffect.hlsl. Either it’s always rebuilt, or it’s never rebuilt. Other things I also tried with no success:

  • using BeforeTargets="BeforeRestGen"
  • explicitly setting Inputs= and Outputs= in <Target>

Can you recommend best practices when using your project? Is it possible to achieve what I want to do?

Hello,

Setting Inputs and Outputs to the target no longer works in recent versions of Visual Studio when the FastUpToDate feature is enabled.

Now you have to use the UpToDateCheckInput, UpToDateCheckOutput, and UpToDateCheckBuilt items.
See https://github.com/dotnet/project-system/blob/main/docs/up-to-date-check.md

You can try:

  <Target Name="CompileShaders" BeforeTargets="BeforeBuild">
    <ItemGroup>
      <HLSLShader Include="Internal\TintEffect.hlsl">
        <ShaderProfile>ps_3_0</ShaderProfile>
        <ObjectFileOutput>$(OutputPath)TintEffect.ps</ObjectFileOutput>
      </HLSLShader>
      <EmbeddedResource Include="$(OutputPath)TintEffect.ps" LogicalName="TintEffect.ps" Visible="False" />
    </ItemGroup>
  </Target>

  <ItemGroup>
    <UpToDateCheckBuilt Original="Internal\TintEffect.hlsl" Include="$(OutputPath)\TintEffect.ps" />
  </ItemGroup>

Thanks, I wasn’t aware of these attributes, they appear to be exactly what I needed! I have been unable to implement your suggestion, though, because it always fail to build, and I realised that it’s because of a recent Visual Studio 2022 upgrade. It seems that its embedded version of MSBuild uses a different System.Runtime and any project that uses JeremyAnsel.HLSL.Targets now fails:

error MSB4018: The "HLSLShaderCompile" task failed unexpectedly.
error MSB4018: System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.
error MSB4018: File name: 'System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
error MSB4018:    at JeremyAnsel.HLSL.Targets.HLSLShaderCompile.Execute()
error MSB4018:    at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
error MSB4018:    at Microsoft.Build.BackEnd.TaskBuilder.<ExecuteInstantiatedTask>d__26.MoveNext()

Building the same project with dotnet.exe works fine, though. I believe the NuGet package is maybe just missing a target framework. Do you want me to create a new GitHub issue for this?

Hello,
I've published a new version.
I've replaced net6.0 with netstandard2.0.
Can you try it?