Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove shader compilation from reflection services, rename Dynamic library #622

Merged
merged 10 commits into from
Oct 17, 2023
16 changes: 8 additions & 8 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ jobs:
run: dotnet pack src\ComputeSharp.Core\ComputeSharp.Core.csproj -c Release
- name: Build ComputeSharp package
run: dotnet pack src\ComputeSharp\ComputeSharp.csproj -c Release
- name: Build ComputeSharp.Dynamic package
run: dotnet pack src\ComputeSharp.Dynamic\ComputeSharp.Dynamic.csproj -c Release
- name: Build ComputeSharp.Dxc package
run: dotnet pack src\ComputeSharp.Dxc\ComputeSharp.Dxc.csproj -c Release
- name: Build ComputeSharp.D3D12MemoryAllocator package
run: dotnet pack src\ComputeSharp.D3D12MemoryAllocator\ComputeSharp.D3D12MemoryAllocator.csproj -c Release
- name: Build ComputeSharp.D2D1 package
Expand All @@ -68,7 +68,7 @@ jobs:
needs: [build-solution]
runs-on: windows-2022

# Set the environment variable which is then looked up in ComputeSharp.Dynamic.
# Set the environment variable which is then looked up in ComputeSharp.Dxc.
# This is a workaround for https://github.com/actions/runner-images/issues/6531.
env:
CI_RUNNER_DOTNET_TEST_PLATFORM: x64
Expand Down Expand Up @@ -289,8 +289,8 @@ jobs:
path: artifacts
- name: Build and run ComputeSharp.NuGet
run: dotnet run --project tests\ComputeSharp.NuGet\ComputeSharp.NuGet.csproj -c Release -r win-x64 --no-self-contained -v n
- name: Build and run ComputeSharp.Dynamic.NuGet
run: dotnet run --project tests\ComputeSharp.Dynamic.NuGet\ComputeSharp.Dynamic.NuGet.csproj -c Release -r win-x64 --no-self-contained -v n
- name: Build and run ComputeSharp.Dxc.NuGet
run: dotnet run --project tests\ComputeSharp.Dxc.NuGet\ComputeSharp.Dxc.NuGet.csproj -c Release -r win-x64 --no-self-contained -v n
- name: Build and run ComputeSharp.Pix.NuGet
run: dotnet run --project tests\ComputeSharp.Pix.NuGet\ComputeSharp.Pix.NuGet.csproj -c Release -r win-x64 --no-self-contained -v n

Expand All @@ -306,11 +306,11 @@ jobs:
$env:COMPUTESHARP_NUGET_TESTS_PUBLISH_AOT='true';
dotnet publish tests\ComputeSharp.NuGet\ComputeSharp.NuGet.csproj -c Release -r win-x64 -v n;
tests\ComputeSharp.NuGet\bin\Release\net7.0\win-x64\publish\ComputeSharp.NuGet.exe
- name: Publish and run ComputeSharp.Dynamic.NuGet with NativeAOT
- name: Publish and run ComputeSharp.Dxc.NuGet with NativeAOT
run: >
$env:COMPUTESHARP_NUGET_TESTS_PUBLISH_AOT='true';
dotnet publish tests\ComputeSharp.Dynamic.NuGet\ComputeSharp.Dynamic.NuGet.csproj -c Release -r win-x64 -v n;
tests\ComputeSharp.Dynamic.NuGet\bin\Release\net7.0\win-x64\publish\ComputeSharp.Dynamic.NuGet.exe
dotnet publish tests\ComputeSharp.Dxc.NuGet\ComputeSharp.Dxc.NuGet.csproj -c Release -r win-x64 -v n;
tests\ComputeSharp.Dxc.NuGet\bin\Release\net7.0\win-x64\publish\ComputeSharp.Dxc.NuGet.exe
- name: Publish and run ComputeSharp.Pix.NuGet with NativeAOT
run: >
$env:COMPUTESHARP_NUGET_TESTS_PUBLISH_AOT='true';
Expand Down
10 changes: 5 additions & 5 deletions ComputeSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ComputeSharp.SwapChain.Shad
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComputeSharp.Tests.DisableDynamicCompilation", "tests\ComputeSharp.Tests.DisableDynamicCompilation\ComputeSharp.Tests.DisableDynamicCompilation.csproj", "{4A8FCE22-851C-442A-8BC9-3EC3FD00B637}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComputeSharp.Dynamic", "src\ComputeSharp.Dynamic\ComputeSharp.Dynamic.csproj", "{76C66257-FDAA-47AB-85E7-EF757EAB10C0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComputeSharp.Dxc", "src\ComputeSharp.Dxc\ComputeSharp.Dxc.csproj", "{76C66257-FDAA-47AB-85E7-EF757EAB10C0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComputeSharp.D2D1", "src\ComputeSharp.D2D1\ComputeSharp.D2D1.csproj", "{4D7D9BA8-A821-4B1C-9EE2-CBCF5849906E}"
EndProject
Expand All @@ -82,10 +82,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComputeSharp.D2D1.Tests.Ass
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComputeSharp.Pix", "src\ComputeSharp.Pix\ComputeSharp.Pix.csproj", "{14EF12A3-90E6-4683-BD71-9896AF87026B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ComputeSharp.Dynamic.NuGet", "ComputeSharp.Dynamic.NuGet", "{CBA545B4-31EF-4E9F-A1A8-2A1290B54046}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ComputeSharp.Dxc.NuGet", "ComputeSharp.Dxc.NuGet", "{CBA545B4-31EF-4E9F-A1A8-2A1290B54046}"
ProjectSection(SolutionItems) = preProject
tests\ComputeSharp.Dynamic.NuGet\ComputeSharp.Dynamic.NuGet.csproj = tests\ComputeSharp.Dynamic.NuGet\ComputeSharp.Dynamic.NuGet.csproj
tests\ComputeSharp.Dynamic.NuGet\Program.cs = tests\ComputeSharp.Dynamic.NuGet\Program.cs
tests\ComputeSharp.Dxc.NuGet\ComputeSharp.Dxc.NuGet.csproj = tests\ComputeSharp.Dxc.NuGet\ComputeSharp.Dxc.NuGet.csproj
tests\ComputeSharp.Dxc.NuGet\Program.cs = tests\ComputeSharp.Dxc.NuGet\Program.cs
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ComputeSharp.Pix.NuGet", "ComputeSharp.Pix.NuGet", "{C0B903AA-F32C-4760-B4B6-199AC4FE1C5A}"
Expand Down Expand Up @@ -148,7 +148,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Win32", "Win32", "{D991153C
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ComputeSharp.CodeFixing", "src\ComputeSharp.CodeFixing\ComputeSharp.CodeFixing.shproj", "{29F6E256-BA0E-4F3B-854E-50839D67C6DD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ComputeSharp.CodeFixers", "src\ComputeSharp.CodeFixers\ComputeSharp.CodeFixers.csproj", "{9B4448B1-200F-4966-8A13-A508691B3003}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComputeSharp.CodeFixers", "src\ComputeSharp.CodeFixers\ComputeSharp.CodeFixers.csproj", "{9B4448B1-200F-4966-8A13-A508691B3003}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ The sample app is available in the Microsoft Store, showcasing several pixel sha
| Name | Description |
| ------ | ------ |
| **ComputeSharp** | The main library, with compiled shaders support |
| **ComputeSharp.Dynamic** | An extension for **ComputeSharp**, enabling dynamic shader compilation at runtime and shader metaprogramming |
| **ComputeSharp.D3D12MemoryAllocator** | An extension for **ComputeSharp**, adding D3D12MA as the memory allocator for graphics resources. |
| **ComputeSharp.D3D12MemoryAllocator** | An extension library for **ComputeSharp**, adding D3D12MA as the memory allocator for graphics resources. |
| **ComputeSharp.Dxc** | An extension library for **ComputeSharp**, bundling the DXC compiler and enabling shader reflection |
| **ComputeSharp.Pix** | An extension library for **ComputeSharp**, enabling PIX support to produce debugging information |
| **ComputeSharp.WinUI** | A WinUI 3 library with controls to render DX12 shaders powered by **ComputeSharp** |
| **ComputeSharp.D2D1** | A library to write D2D1 pixel shaders entirely with C# code, and to easily register and create ID2D1Effect-s from them |
Expand Down
2 changes: 1 addition & 1 deletion src/ComputeSharp.Core/ComputeSharp.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<ItemGroup>
<InternalsVisibleTo Include="ComputeSharp, PublicKey=$(AssemblySignPublicKey)" />
<InternalsVisibleTo Include="ComputeSharp.D3D12MemoryAllocator, PublicKey=$(AssemblySignPublicKey)" />
<InternalsVisibleTo Include="ComputeSharp.Dynamic, PublicKey=$(AssemblySignPublicKey)" />
<InternalsVisibleTo Include="ComputeSharp.Dxc, PublicKey=$(AssemblySignPublicKey)" />
<InternalsVisibleTo Include="ComputeSharp.Pix, PublicKey=$(AssemblySignPublicKey)" />
<InternalsVisibleTo Include="ComputeSharp.WinUI, PublicKey=$(AssemblySignPublicKey)" />
<InternalsVisibleTo Include="ComputeSharp.D2D1, PublicKey=$(AssemblySignPublicKey)" />
Expand Down
30 changes: 6 additions & 24 deletions src/ComputeSharp.D2D1/Shaders/Interop/D2D1ReflectionServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Runtime.CompilerServices;
using ComputeSharp.D2D1.Descriptors;
using ComputeSharp.D2D1.Extensions;
using ComputeSharp.D2D1.Shaders.Translation;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;

Expand Down Expand Up @@ -31,33 +30,15 @@ public static unsafe D2D1ShaderInfo GetShaderInfo<T>()
{
Unsafe.SkipInit(out T shader);

string hlslSource = shader.HlslSource;
ReadOnlyMemory<byte> hlslBytecode = D2D1PixelShader.LoadBytecode<T>();

using ComPtr<ID3D11ShaderReflection> d3D11ShaderReflection = default;

// If the shader already has embedded bytecode, use that directly
if (shader.HlslBytecode is ReadOnlyMemory<byte> { Length: > 0 } hlslBytecode)
fixed (byte* hlslBytecodePtr = hlslBytecode.Span)
{
fixed (byte* hlslBytecodePtr = hlslBytecode.Span)
{
DirectX.D3DReflect(
pSrcData: hlslBytecodePtr,
SrcDataSize: (nuint)hlslBytecode.Length,
pInterface: Windows.__uuidof<ID3D11ShaderReflection>(),
ppReflector: (void**)d3D11ShaderReflection.GetAddressOf()).Assert();
}
}
else
{
// Otherwise, compile it with default shader profile and compile options and use that.
// Note that we're intentionally retrieving an ID3DBlob instance here to avoid having
// to allocate a ReadOnlyMemory<byte> object to throw away immediately, as we don't
// actually need to keep the bytecode for later.
using ComPtr<ID3DBlob> dynamicBytecode = D3DCompiler.Compile(hlslSource.AsSpan(), shader.ShaderProfile, shader.CompileOptions);

DirectX.D3DReflect(
pSrcData: (byte*)dynamicBytecode.Get()->GetBufferPointer(),
SrcDataSize: dynamicBytecode.Get()->GetBufferSize(),
pSrcData: hlslBytecodePtr,
SrcDataSize: (nuint)hlslBytecode.Length,
pInterface: Windows.__uuidof<ID3D11ShaderReflection>(),
ppReflector: (void**)d3D11ShaderReflection.GetAddressOf()).Assert();
}
Expand All @@ -72,7 +53,8 @@ public static unsafe D2D1ShaderInfo GetShaderInfo<T>()

return new(
CompilerVersion: new string(d3D11ShaderDescription.Creator),
HlslSource: hlslSource,
HlslSource: shader.HlslSource,
HlslBytecode: hlslBytecode,
ConstantBufferCount: d3D11ShaderDescription.ConstantBuffers,
BoundResourceCount: d3D11ShaderDescription.BoundResources,
InstructionCount: d3D11ShaderDescription.InstructionCount,
Expand Down
4 changes: 4 additions & 0 deletions src/ComputeSharp.D2D1/Shaders/Interop/D2D1ShaderInfo.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using System;

namespace ComputeSharp.D2D1.Interop;

/// <summary>
/// A model representing reflection info for a given D2D1 shader.
/// </summary>
/// <param name="CompilerVersion">The compiler version used to build the shader.</param>
/// <param name="HlslSource">The HLSL source code the shader was built from.</param>
/// <param name="HlslBytecode">The HLSL bytecode for the shader.</param>
/// <param name="ConstantBufferCount">The number of shader constant buffers.</param>
/// <param name="BoundResourceCount">The number of resource (textures and buffers) bound to a shader.</param>
/// <param name="InstructionCount">The number of intermediate-language instructions in the compiled shader.</param>
Expand Down Expand Up @@ -32,6 +35,7 @@ namespace ComputeSharp.D2D1.Interop;
public sealed record D2D1ShaderInfo(
string CompilerVersion,
string HlslSource,
ReadOnlyMemory<byte> HlslBytecode,
uint ConstantBufferCount,
uint BoundResourceCount,
uint InstructionCount,
Expand Down
77 changes: 77 additions & 0 deletions src/ComputeSharp.Dxc/Interop/ReflectionServices.LibraryLoading.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;

namespace ComputeSharp.Interop;

/// <inheritdoc/>
partial class ReflectionServices
{
/// <summary>
/// Initializes the DLL resolvers for the dxcompiler.dll and dxil.dll libraries.
/// </summary>
static ReflectionServices()
{
// Register a custom library resolver for the two DXC libraries. We need to either manually load the two
// libraries from the NuGet directory, if an RID is not in use, or we need to ensure that dxil.dll is
// loaded correctly in case the program was executed with the host being in another directory.
// This happens when doing eg. "dotnet bin\Debug\net8.0\MyApp.dll", which would crash at runtime.
NativeLibrary.SetDllImportResolver(typeof(ReflectionServices).Assembly, OnDllImport);
}

/// <summary>
/// The custom <see cref="DllImportResolver"/> for this assembly.
/// </summary>
/// <inheritdoc cref="DllImportResolver"/>
private static IntPtr OnDllImport(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{
if (libraryName is not "dxcompiler")
{
return IntPtr.Zero;
}

string rid = RuntimeInformation.ProcessArchitecture switch
{
Architecture.X64 => "win-x64",
Architecture.Arm64 => "win-arm64",
_ => default(NotSupportedException).Throw<string>()
};

// Test whether the native libraries are present in the same folder of the executable
// (which is the case when the program was built with a runtime identifier), or whether
// they are in the "runtimes\win-x64\native" folder in the executable directory.
string nugetNativeLibsPath = Path.Combine(AppContext.BaseDirectory, $@"runtimes\{rid}\native");
bool isNuGetRuntimeLibrariesDirectoryPresent = Directory.Exists(nugetNativeLibsPath);

if (isNuGetRuntimeLibrariesDirectoryPresent)
{
string dxcompilerPath = Path.Combine(AppContext.BaseDirectory, $@"runtimes\{rid}\native\dxcompiler.dll");
string dxilPath = Path.Combine(AppContext.BaseDirectory, $@"runtimes\{rid}\native\dxil.dll");

// Load DXIL first so that DXC doesn't fail to load it, and then DXIL, both from the NuGet path
if (NativeLibrary.TryLoad(dxilPath, out _) &&
NativeLibrary.TryLoad(dxcompilerPath, out IntPtr handle))
{
return handle;
}
}
else
{
// Even when the two libraries are correctly copied next to the executable in use, we load them
// manually to ensure the operation is successful. This is to avoid failures in cases such as when
// doing "dotnet bin\MyApp.dll", ie. when the host is in another path than the executable in use.
// This is probably because DXIL is a native dependency for DXC, but the way Windows loads these
// libraries doesn't take into account the .NET concepts of "app directory": neither the current "bin"
// directory nor the "process directory", which is "C:\Program Files\dotnet", actually contain the
// native library we need, hence the runtime crash. Manually loading the library this way solves this.
if (NativeLibrary.TryLoad("dxil", assembly, searchPath, out _) &&
NativeLibrary.TryLoad("dxcompiler", assembly, searchPath, out IntPtr handle))
{
return handle;
}
}

return IntPtr.Zero;
}
}
Loading