yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Ellie HermaszewskaMove switch statement bodies to their own lines (#5493)b118451e3

master
12.3 KiB444 linesraw
1// slang-fxc-compiler.cpp
2#include "slang-fxc-compiler.h"
3
4#if SLANG_ENABLE_DXBC_SUPPORT
5
6#include "../core/slang-blob.h"
7#include "../core/slang-char-util.h"
8#include "../core/slang-common.h"
9#include "../core/slang-io.h"
10#include "../core/slang-semantic-version.h"
11#include "../core/slang-shared-library.h"
12#include "../core/slang-string-slice-pool.h"
13#include "../core/slang-string-util.h"
14#include "slang-artifact-associated-impl.h"
15#include "slang-artifact-desc-util.h"
16#include "slang-artifact-diagnostic-util.h"
17#include "slang-com-helper.h"
18#include "slang-include-system.h"
19#include "slang-source-loc.h"
20
21// Enable calling through to `fxc` or `dxc` to
22// generate code on Windows.
23#ifdef _WIN32
24#include <d3dcompiler.h>
25#include <windows.h>
26#endif
27
28// Some of the `D3DCOMPILE_*` constants aren't available in all
29// versions of `d3dcompiler.h`, so we define them here just in case
30#ifndef D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES
31#define D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES (1 << 20)
32#endif
33
34#ifndef D3DCOMPILE_ALL_RESOURCES_BOUND
35#define D3DCOMPILE_ALL_RESOURCES_BOUND (1 << 21)
36#endif
37
38#endif // SLANG_ENABLE_DXBC_SUPPORT
39
40namespace Slang
41{
42
43#if SLANG_ENABLE_DXBC_SUPPORT
44
45static UnownedStringSlice _getSlice(ID3DBlob* blob)
46{
47    return StringUtil::getSlice((ISlangBlob*)blob);
48}
49
50struct FxcIncludeHandler : ID3DInclude
51{
52
53    STDMETHOD(Open)
54    (D3D_INCLUDE_TYPE includeType,
55     LPCSTR fileName,
56     LPCVOID parentData,
57     LPCVOID* outData,
58     UINT* outSize) override
59    {
60        SLANG_UNUSED(includeType);
61        // NOTE! The pParentData means the *text* of any previous include.
62        // In order to work out what *path* that came from, we need to seach which source file it
63        // came from, and use it's path
64
65        // Assume the root pathInfo initially
66        PathInfo includedFromPathInfo = m_rootPathInfo;
67
68        // Lets try and find the parent source if there is any
69        if (parentData)
70        {
71            SourceFile* foundSourceFile =
72                m_system.getSourceManager()->findSourceFileByContentRecursively(
73                    (const char*)parentData);
74            if (foundSourceFile)
75            {
76                includedFromPathInfo = foundSourceFile->getPathInfo();
77            }
78        }
79
80        String path(fileName);
81        PathInfo pathInfo;
82        ComPtr<ISlangBlob> blob;
83
84        SLANG_RETURN_ON_FAIL(
85            m_system.findAndLoadFile(path, includedFromPathInfo.foundPath, pathInfo, blob));
86
87        // Return the data
88        *outData = blob->getBufferPointer();
89        *outSize = (UINT)blob->getBufferSize();
90
91        return S_OK;
92    }
93
94    STDMETHOD(Close)(LPCVOID pData) override
95    {
96        SLANG_UNUSED(pData);
97        return S_OK;
98    }
99    FxcIncludeHandler(
100        SearchDirectoryList* searchDirectories,
101        ISlangFileSystemExt* fileSystemExt,
102        SourceManager* sourceManager)
103        : m_system(searchDirectories, fileSystemExt, sourceManager)
104    {
105    }
106
107    PathInfo m_rootPathInfo;
108    IncludeSystem m_system;
109};
110
111class FXCDownstreamCompiler : public DownstreamCompilerBase
112{
113public:
114    typedef DownstreamCompilerBase Super;
115
116    // IDownstreamCompiler
117    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
118    compile(const CompileOptions& options, IArtifact** outArtifact) SLANG_OVERRIDE;
119    virtual SLANG_NO_THROW bool SLANG_MCALL
120    canConvert(const ArtifactDesc& from, const ArtifactDesc& to) SLANG_OVERRIDE;
121    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
122    convert(IArtifact* from, const ArtifactDesc& to, IArtifact** outArtifact) SLANG_OVERRIDE;
123    virtual SLANG_NO_THROW bool SLANG_MCALL isFileBased() SLANG_OVERRIDE { return false; }
124
125    /// Must be called before use
126    SlangResult init(ISlangSharedLibrary* library);
127
128    FXCDownstreamCompiler() {}
129
130protected:
131    pD3DCompile m_compile = nullptr;
132    pD3DDisassemble m_disassemble = nullptr;
133
134    ComPtr<ISlangSharedLibrary> m_sharedLibrary;
135};
136
137SlangResult FXCDownstreamCompiler::init(ISlangSharedLibrary* library)
138{
139    m_compile = (pD3DCompile)library->findFuncByName("D3DCompile");
140    m_disassemble = (pD3DDisassemble)library->findFuncByName("D3DDisassemble");
141
142    if (!m_compile || !m_disassemble)
143    {
144        return SLANG_FAIL;
145    }
146
147    m_sharedLibrary = library;
148
149    // It's not clear how to query for a version, but we can get a version number from the header
150    m_desc = Desc(SLANG_PASS_THROUGH_FXC, D3D_COMPILER_VERSION);
151
152    return SLANG_OK;
153}
154
155static SlangResult _parseDiagnosticLine(
156    SliceAllocator& allocator,
157    const UnownedStringSlice& line,
158    List<UnownedStringSlice>& lineSlices,
159    ArtifactDiagnostic& outDiagnostic)
160{
161    /* tests/diagnostics/syntax-error-intrinsic.slang(14,2): error X3000: syntax error: unexpected
162     * token '@' */
163    if (lineSlices.getCount() < 3)
164    {
165        return SLANG_FAIL;
166    }
167
168    SLANG_RETURN_ON_FAIL(
169        ArtifactDiagnosticUtil::splitPathLocation(allocator, lineSlices[0], outDiagnostic));
170
171    {
172        const UnownedStringSlice severityAndCodeSlice = lineSlices[1].trim();
173        const UnownedStringSlice severitySlice =
174            StringUtil::getAtInSplit(severityAndCodeSlice, ' ', 0);
175
176        outDiagnostic.code =
177            allocator.allocate(StringUtil::getAtInSplit(severityAndCodeSlice, ' ', 1));
178
179        outDiagnostic.severity = ArtifactDiagnostic::Severity::Error;
180        if (severitySlice == "warning")
181        {
182            outDiagnostic.severity = ArtifactDiagnostic::Severity::Warning;
183        }
184    }
185
186    outDiagnostic.text = allocator.allocate(lineSlices[2].begin(), line.end());
187    return SLANG_OK;
188}
189
190SlangResult FXCDownstreamCompiler::compile(const CompileOptions& inOptions, IArtifact** outArtifact)
191{
192    if (!isVersionCompatible(inOptions))
193    {
194        // Not possible to compile with this version of the interface.
195        return SLANG_E_NOT_IMPLEMENTED;
196    }
197
198    CompileOptions options = getCompatibleVersion(&inOptions);
199
200    // This compiler can only deal with a single source artifact
201    if (options.sourceArtifacts.count != 1)
202    {
203        return SLANG_FAIL;
204    }
205
206    IArtifact* sourceArtifact = options.sourceArtifacts[0];
207
208    if (options.sourceLanguage != SLANG_SOURCE_LANGUAGE_HLSL || options.targetType != SLANG_DXBC)
209    {
210        SLANG_ASSERT(!"Can only compile HLSL to DXBC");
211        return SLANG_FAIL;
212    }
213
214    // If we have been invoked in a pass-through mode, then we need to make sure
215    // that the downstream compiler sees whatever options were passed to Slang
216    // via the command line or API.
217    //
218    // TODO: more pieces of information should be added here as needed.
219    //
220
221    SearchDirectoryList searchDirectories;
222    for (const auto& includePath : options.includePaths)
223    {
224        searchDirectories.searchDirectories.add(asString(includePath));
225    }
226
227    const auto sourcePath = ArtifactUtil::findPath(sourceArtifact);
228
229    // Use the default fileSystemExt is not set
230    ID3DInclude* includeHandler = nullptr;
231
232    FxcIncludeHandler fxcIncludeHandlerStorage(
233        &searchDirectories,
234        options.fileSystemExt,
235        options.sourceManager);
236    if (options.fileSystemExt)
237    {
238
239        if (sourcePath.getLength() > 0)
240        {
241            fxcIncludeHandlerStorage.m_rootPathInfo = PathInfo::makePath(sourcePath);
242        }
243        includeHandler = &fxcIncludeHandlerStorage;
244    }
245
246    List<D3D_SHADER_MACRO> dxMacrosStorage;
247    D3D_SHADER_MACRO const* dxMacros = nullptr;
248
249    if (options.defines.count > 0)
250    {
251        for (const auto& define : options.defines)
252        {
253            D3D_SHADER_MACRO dxMacro;
254            dxMacro.Name = define.nameWithSig;
255            dxMacro.Definition = define.value;
256            dxMacrosStorage.add(dxMacro);
257        }
258        D3D_SHADER_MACRO nullTerminator = {0, 0};
259        dxMacrosStorage.add(nullTerminator);
260
261        dxMacros = dxMacrosStorage.getBuffer();
262    }
263
264    DWORD flags = 0;
265
266    switch (options.floatingPointMode)
267    {
268    default:
269        break;
270
271    case FloatingPointMode::Precise:
272        flags |= D3DCOMPILE_IEEE_STRICTNESS;
273        break;
274    }
275
276    flags |= D3DCOMPILE_ENABLE_STRICTNESS;
277    flags |= D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES;
278
279    switch (options.optimizationLevel)
280    {
281    default:
282        break;
283
284    case OptimizationLevel::None:
285        flags |= D3DCOMPILE_OPTIMIZATION_LEVEL0;
286        break;
287    case OptimizationLevel::Default:
288        flags |= D3DCOMPILE_OPTIMIZATION_LEVEL1;
289        break;
290    case OptimizationLevel::High:
291        flags |= D3DCOMPILE_OPTIMIZATION_LEVEL2;
292        break;
293    case OptimizationLevel::Maximal:
294        flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
295        break;
296    }
297
298    switch (options.debugInfoType)
299    {
300    case DebugInfoType::None:
301        break;
302
303    default:
304        flags |= D3DCOMPILE_DEBUG;
305        break;
306    }
307
308    ComPtr<ISlangBlob> sourceBlob;
309    SLANG_RETURN_ON_FAIL(sourceArtifact->loadBlob(ArtifactKeep::Yes, sourceBlob.writeRef()));
310
311    ComPtr<ID3DBlob> codeBlob;
312    ComPtr<ID3DBlob> diagnosticsBlob;
313    HRESULT hr = m_compile(
314        sourceBlob->getBufferPointer(),
315        sourceBlob->getBufferSize(),
316        String(sourcePath).getBuffer(),
317        dxMacros,
318        includeHandler,
319        options.entryPointName,
320        options.profileName,
321        flags,
322        0, // unused: effect flags
323        codeBlob.writeRef(),
324        diagnosticsBlob.writeRef());
325
326    auto diagnostics = ArtifactDiagnostics::create();
327
328    // HRESULT is compatible with SlangResult
329    diagnostics->setResult(hr);
330
331    SliceAllocator allocator;
332
333    if (diagnosticsBlob)
334    {
335        UnownedStringSlice diagnosticText = _getSlice(diagnosticsBlob);
336        diagnostics->setRaw(asCharSlice(diagnosticText));
337
338        SlangResult diagnosticParseRes = ArtifactDiagnosticUtil::parseColonDelimitedDiagnostics(
339            allocator,
340            diagnosticText,
341            0,
342            _parseDiagnosticLine,
343            diagnostics);
344        SLANG_UNUSED(diagnosticParseRes);
345        SLANG_ASSERT(SLANG_SUCCEEDED(diagnosticParseRes));
346    }
347
348    // If FXC failed, make sure we have an error in the diagnostics
349    if (FAILED(hr))
350    {
351        diagnostics->requireErrorDiagnostic();
352    }
353
354    auto artifact = ArtifactUtil::createArtifactForCompileTarget(options.targetType);
355
356    ArtifactUtil::addAssociated(artifact, diagnostics);
357
358    if (codeBlob)
359    {
360        // ID3DBlob is compatible with ISlangBlob, so just cast away...
361        artifact->addRepresentationUnknown((ISlangBlob*)codeBlob.get());
362    }
363
364    *outArtifact = artifact.detach();
365    return SLANG_OK;
366}
367
368bool FXCDownstreamCompiler::canConvert(const ArtifactDesc& from, const ArtifactDesc& to)
369{
370    // Can only disassemble blobs that are DXBC
371    return ArtifactDescUtil::isDisassembly(from, to) && from.payload == ArtifactPayload::DXBC;
372}
373
374SlangResult FXCDownstreamCompiler::convert(
375    IArtifact* from,
376    const ArtifactDesc& to,
377    IArtifact** outArtifact)
378{
379    if (!canConvert(from->getDesc(), to))
380    {
381        return SLANG_FAIL;
382    }
383
384    ComPtr<ISlangBlob> dxbcBlob;
385    SLANG_RETURN_ON_FAIL(from->loadBlob(ArtifactKeep::No, dxbcBlob.writeRef()));
386
387    ComPtr<ID3DBlob> disassemblyBlob;
388    SLANG_RETURN_ON_FAIL(m_disassemble(
389        dxbcBlob->getBufferPointer(),
390        dxbcBlob->getBufferSize(),
391        0,
392        nullptr,
393        disassemblyBlob.writeRef()));
394
395    auto artifact = ArtifactUtil::createArtifact(to);
396    // ISlangBlob is compatible with ID3DBlob
397    artifact->addRepresentationUnknown((ISlangBlob*)disassemblyBlob.get());
398
399    *outArtifact = artifact.detach();
400    return SLANG_OK;
401}
402
403/* static */ SlangResult FXCDownstreamCompilerUtil::locateCompilers(
404    const String& path,
405    ISlangSharedLibraryLoader* loader,
406    DownstreamCompilerSet* set)
407{
408    ComPtr<ISlangSharedLibrary> library;
409
410    const char* const libName = "d3dcompiler_47";
411    SLANG_RETURN_ON_FAIL(
412        DownstreamCompilerUtil::loadSharedLibrary(path, loader, nullptr, libName, library));
413
414    SLANG_ASSERT(library);
415    if (!library)
416    {
417        return SLANG_FAIL;
418    }
419
420    auto compiler = new FXCDownstreamCompiler;
421    ComPtr<IDownstreamCompiler> compilerInft(compiler);
422
423    SLANG_RETURN_ON_FAIL(compiler->init(library));
424
425    set->addCompiler(compilerInft);
426    return SLANG_OK;
427}
428
429#else // SLANG_ENABLE_DXBC_SUPPORT
430
431/* static */ SlangResult FXCDownstreamCompilerUtil::locateCompilers(
432    const String& path,
433    ISlangSharedLibraryLoader* loader,
434    DownstreamCompilerSet* set)
435{
436    SLANG_UNUSED(path);
437    SLANG_UNUSED(loader);
438    SLANG_UNUSED(set);
439    return SLANG_E_NOT_AVAILABLE;
440}
441
442#endif // else SLANG_ENABLE_DXBC_SUPPORT
443
444} // namespace Slang