• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DemoBytom / DemoEngine / 23769862947

30 Mar 2026 10:01PM UTC coverage: 30.548% (+0.007%) from 30.541%
23769862947

push

coveralls.net

DemoBytom
Drawing Mandlebrot fractal

1276 of 4177 relevant lines covered (30.55%)

0.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Demo.Engine.Platform.DirectX12/Shaders/ShaderCompiler.cs
1
// Copyright © Michał Dembski and contributors.
2
// Distributed under MIT license. See LICENSE file in the root for more information.
3

4
using System.Buffers;
5
using System.Diagnostics;
6
using System.Runtime.CompilerServices;
7
using Demo.Engine.Core.Requests;
8
using Demo.Engine.Platform.DirectX12.Shaders.Requests;
9
using Mediator;
10
using Microsoft.Extensions.Logging;
11
using SharpGen.Runtime;
12
using Vortice.Dxc;
13

14
namespace Demo.Engine.Platform.DirectX12.Shaders;
15

16
internal record struct ShaderFileInfo(
17
    string File,
18
    string Function,
19
    ShaderId ID,
20
    ShaderType ShaderType);
21

22
internal sealed class ShaderCompiler(
23
    ILogger<ShaderCompiler> logger,
24
    IMediator mediator)
25
    : IShaderAsyncCompiler,
26
      IRequestHandler<CompileShaders, bool>
27
{
28
    private readonly ShaderFileInfo[] _shaderFiles =
29
    [
30
        new ShaderFileInfo(
31
            File: Path.Combine("ShaderFiles", "FullScreenTriangle.hlsl"),
32
            Function: "FullScreenTriangleVS",
33
            ID: ShaderId.FullscreenTriangleVS,
34
            ShaderType: ShaderType.Vertex),
35
        new ShaderFileInfo(
36
            File: Path.Combine("ShaderFiles", "FillColor.hlsl"),
37
            Function: "FillColorPS",
38
            ID: ShaderId.FillColorPS,
39
            ShaderType: ShaderType.Pixel),
40
        new ShaderFileInfo(
41
            File: Path.Combine("ShaderFiles", "PostProcess.hlsl"),
42
            Function: "PostProcessPS",
43
            ID: ShaderId.PostProcessPS,
44
            ShaderType: ShaderType.Pixel),
45
        new ShaderFileInfo(
46
            File: Path.Combine("Triangle", "TrianglePS.hlsl"),
47
            Function: "main",
48
            ID: ShaderId.TrianglePS,
49
            ShaderType: ShaderType.Pixel),
50
        new ShaderFileInfo(
51
            File: Path.Combine("Triangle", "TriangleVS.hlsl"),
52
            Function: "main",
53
            ID: ShaderId.TriangleVS,
54
            ShaderType: ShaderType.Vertex),
55
    ];
56

57
    private readonly ILogger<ShaderCompiler> _logger = logger;
58
    private readonly IMediator _mediator = mediator;
59

60
    private static readonly GetShaderDirAbsolutePathRequest _getShaderDirAbsolutePathRequest = new();
61

62
    public async ValueTask<bool> Handle(
63
        CompileShaders request,
64
        CancellationToken cancellationToken)
65
        => await CompileShaders(cancellationToken);
66

67
    public async Task<bool> CompileShaders(
68
        CancellationToken cancellationToken = default)
69
    {
70
        //TODO: verify compiled shaders are up to date
71
        var compilationTasks = ArrayPool<Task<ShaderContent>>
72
            .Shared
73
            .Rent(_shaderFiles.Length);
74

75
        var completed = false;
76
        try
77
        {
78
            for (var i = 0; i < _shaderFiles.Length; ++i)
79
            {
80
                var shaderFile = _shaderFiles[i];
81
                _logger.LogCompilingShaderFile(
82
                    shaderFile.File,
83
                    shaderFile.ID,
84
                    shaderFile.ShaderType);
85

86
                var compilationTask = CompileAShader(shaderFile, cancellationToken);
87
                compilationTasks[i] = compilationTask;
88
            }
89

90
            _ = await _mediator.Send(new SaveEngineShadersRequest(
91
                Task.WhenEach(
92
                    compilationTasks
93
                        .AsSpan()[.._shaderFiles.Length])),
94
                    cancellationToken);
95

96
            completed = true;
97
        }
98
        finally
99
        {
100
            if (!completed)
101
            {
102
                _ = await Task.WhenAll(
103
                    compilationTasks
104
                        .AsSpan()[.._shaderFiles.Length])
105
                    .ConfigureAwait(false);
106
            }
107
            ArrayPool<Task<ShaderContent>>
108
                .Shared
109
                .Return(compilationTasks);
110
        }
111

112
        return true;
113
    }
114

115
    private async Task<ShaderContent> CompileAShader(
116
        ShaderFileInfo shaderFileInfo,
117
        CancellationToken cancellationToken = default)
118
    {
119
        var shaderDirAbsolutePath = await _mediator.Send(
120
            _getShaderDirAbsolutePathRequest, cancellationToken);
121

122
        var path = Path.Combine(
123
            shaderDirAbsolutePath,
124
            shaderFileInfo.File);
125

126
        using var fs = new FileStream(
127
            path, FileMode.Open, FileAccess.Read);
128
        using var sr = new StreamReader(fs);
129

130
        using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
131
            cancellationToken);
132
        linkedCts.CancelAfter(TimeSpan.FromSeconds(30));
133

134
        var shaderSource = await sr.ReadToEndAsync(linkedCts.Token);
135

136
        using var result = DxcCompiler.Compile(
137
            shaderFileInfo.ShaderType.ToShaderStage(),
138
            shaderSource,
139
            shaderFileInfo.Function,
140
            new DxcCompilerOptions
141
            {
142
                ShaderModel = DxcShaderModel.Model6_4,
143
            },
144
            additionalArguments: [
145
                // Include path for additional shader files loaded using #include
146
                $"-I{Path.Combine(shaderDirAbsolutePath, "ShaderFiles")}",
147
                ]);
148

149
        if (result.GetStatus().Failure)
150
        {
151
            var exception = new Exception(result.GetErrors());
152
            _logger.LogErrorCompilingShader(
153
                exception,
154
                shaderFileInfo.File);
155
            throw exception;
156
        }
157

158
        var memory = result.GetObjectBytecodeMemory();
159

160
        var shaderContent = new ShaderContent(
161
            shaderFileInfo.ID,
162
            memory);
163

164
        return shaderContent;
165
    }
166

167
    private static async IAsyncEnumerable<T> AwaitEach<T>(
168
        IAsyncEnumerable<Task<T>> input,
169
        [EnumeratorCancellation] CancellationToken cancellationToken = default)
170
    {
171
        await foreach (var task in input
172
            .WithCancellation(cancellationToken))
173
        {
174
            yield return await task;
175
        }
176
    }
177

178
    private sealed class ShaderIncludeHandler
179
        : CallbackBase,
180
          IDxcIncludeHandler
181
    {
182
        public Result LoadSource(
183
            string filename,
184
            out IDxcBlob includeSource)
185
            => throw new NotImplementedException();
×
186
    }
187
}
188

189
file static class SomeExtensions
190
{
191
    extension(ShaderType shaderType)
192
    {
193
        public DxcShaderStage ToShaderStage() => shaderType switch
194
        {
195
            ShaderType.Vertex => DxcShaderStage.Vertex,
196
            ShaderType.Hull => DxcShaderStage.Hull,
197
            ShaderType.Domain => DxcShaderStage.Domain,
198
            ShaderType.Geometry => DxcShaderStage.Geometry,
199
            ShaderType.Pixel => DxcShaderStage.Pixel,
200
            ShaderType.Compute => DxcShaderStage.Compute,
201
            ShaderType.Amplification => DxcShaderStage.Amplification,
202
            ShaderType.Mesh => DxcShaderStage.Mesh,
203
            _ => throw new UnreachableException(),
204
        };
205
    }
206
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc