yum-mirror/slang

Making it easier to work with shaders

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

Jay KwakFix compiler warning with clang 18.1.8 on windows (#6843)04db5a956

master
16.6 KiB555 linesraw
1// slang-glslang-compiler.cpp
2#include "slang-glslang-compiler.h"
3
4#include "../core/slang-blob.h"
5#include "../core/slang-char-util.h"
6#include "../core/slang-common.h"
7#include "../core/slang-io.h"
8#include "../core/slang-semantic-version.h"
9#include "../core/slang-shared-library.h"
10#include "../core/slang-string-slice-pool.h"
11#include "../core/slang-string-util.h"
12#include "slang-artifact-associated-impl.h"
13#include "slang-artifact-desc-util.h"
14#include "slang-com-helper.h"
15#include "slang-include-system.h"
16#include "slang-source-loc.h"
17
18// Enable calling through to `glslang` on
19// all platforms.
20#ifndef SLANG_ENABLE_GLSLANG_SUPPORT
21#define SLANG_ENABLE_GLSLANG_SUPPORT 1
22#endif
23
24#if SLANG_ENABLE_GLSLANG_SUPPORT
25#include "../slang-glslang/slang-glslang.h"
26#endif
27
28namespace Slang
29{
30
31#if SLANG_ENABLE_GLSLANG_SUPPORT
32
33class GlslangDownstreamCompiler : public DownstreamCompilerBase
34{
35public:
36    typedef DownstreamCompilerBase Super;
37
38    // IDownstreamCompiler
39    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
40    compile(const CompileOptions& options, IArtifact** outResult) SLANG_OVERRIDE;
41    virtual SLANG_NO_THROW bool SLANG_MCALL
42    canConvert(const ArtifactDesc& from, const ArtifactDesc& to) SLANG_OVERRIDE;
43    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
44    convert(IArtifact* from, const ArtifactDesc& to, IArtifact** outArtifact) SLANG_OVERRIDE;
45    virtual SLANG_NO_THROW bool SLANG_MCALL isFileBased() SLANG_OVERRIDE { return false; }
46    virtual SLANG_NO_THROW SlangResult SLANG_MCALL getVersionString(slang::IBlob** outVersionString)
47        SLANG_OVERRIDE;
48    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
49    validate(const uint32_t* contents, int contentsSize) SLANG_OVERRIDE;
50    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
51    disassemble(const uint32_t* contents, int contentsSize) SLANG_OVERRIDE;
52    virtual SLANG_NO_THROW SlangResult SLANG_MCALL disassembleWithResult(
53        const uint32_t* contents,
54        int contentsSize,
55        String& outString) SLANG_OVERRIDE;
56    virtual SLANG_NO_THROW int SLANG_MCALL link(
57        const uint32_t** modules,
58        const uint32_t* moduleSizes,
59        const uint32_t moduleCount,
60        IArtifact** outArtifact) SLANG_OVERRIDE;
61
62    /// Must be called before use
63    SlangResult init(ISlangSharedLibrary* library);
64
65    GlslangDownstreamCompiler(SlangPassThrough compilerType)
66        : m_compilerType(compilerType)
67    {
68    }
69
70protected:
71    SlangResult _invoke(glslang_CompileRequest_1_2& request);
72
73    glslang_CompileFunc_1_0 m_compile_1_0 = nullptr;
74    glslang_CompileFunc_1_1 m_compile_1_1 = nullptr;
75    glslang_CompileFunc_1_2 m_compile_1_2 = nullptr;
76    glslang_ValidateSPIRVFunc m_validate = nullptr;
77    glslang_DisassembleSPIRVFunc m_disassemble = nullptr;
78    glslang_DisassembleSPIRVWithResultFunc m_disassembleWithResult = nullptr;
79    glslang_LinkSPIRVFunc m_link = nullptr;
80
81    ComPtr<ISlangSharedLibrary> m_sharedLibrary;
82
83    SlangPassThrough m_compilerType;
84};
85
86SlangResult GlslangDownstreamCompiler::init(ISlangSharedLibrary* library)
87{
88    m_compile_1_0 = (glslang_CompileFunc_1_0)library->findFuncByName("glslang_compile");
89    m_compile_1_1 = (glslang_CompileFunc_1_1)library->findFuncByName("glslang_compile_1_1");
90    m_compile_1_2 = (glslang_CompileFunc_1_2)library->findFuncByName("glslang_compile_1_2");
91    m_validate = (glslang_ValidateSPIRVFunc)library->findFuncByName("glslang_validateSPIRV");
92    m_disassemble =
93        (glslang_DisassembleSPIRVFunc)library->findFuncByName("glslang_disassembleSPIRV");
94    m_disassembleWithResult = (glslang_DisassembleSPIRVWithResultFunc)library->findFuncByName(
95        "glslang_disassembleSPIRVWithResult");
96    m_link = (glslang_LinkSPIRVFunc)library->findFuncByName("glslang_linkSPIRV");
97
98    if (m_compile_1_0 == nullptr && m_compile_1_1 == nullptr && m_compile_1_2 == nullptr)
99    {
100        return SLANG_FAIL;
101    }
102
103    m_sharedLibrary = library;
104
105    // It's not clear how to query for a version, but we can get a version number from the header
106    m_desc = Desc(m_compilerType);
107
108    Slang::String filename;
109    if (m_compile_1_2)
110    {
111        filename = Slang::SharedLibraryUtils::getSharedLibraryFileName((void*)m_compile_1_2);
112    }
113    else if (m_compile_1_1)
114    {
115        filename = Slang::SharedLibraryUtils::getSharedLibraryFileName((void*)m_compile_1_1);
116    }
117    else if (m_compile_1_0)
118    {
119        filename = Slang::SharedLibraryUtils::getSharedLibraryFileName((void*)m_compile_1_0);
120    }
121    else
122    {
123        return SLANG_FAIL;
124    }
125
126    return SLANG_OK;
127}
128
129SlangResult GlslangDownstreamCompiler::_invoke(glslang_CompileRequest_1_2& request)
130{
131    int err = 1;
132    if (m_compile_1_2)
133    {
134        err = m_compile_1_2(&request);
135    }
136    else if (m_compile_1_1)
137    {
138        glslang_CompileRequest_1_1 request_1_1;
139        memcpy(&request_1_1, &request, sizeof(request_1_1));
140        request_1_1.sizeInBytes = sizeof(request_1_1);
141        err = m_compile_1_1(&request_1_1);
142    }
143    else if (m_compile_1_0)
144    {
145        glslang_CompileRequest_1_1 request_1_1;
146        memcpy(&request_1_1, &request, sizeof(request_1_1));
147        request_1_1.sizeInBytes = sizeof(request_1_1);
148        glslang_CompileRequest_1_0 request_1_0;
149        request_1_0.set(request_1_1);
150        err = m_compile_1_0(&request_1_0);
151    }
152
153    return err ? SLANG_FAIL : SLANG_OK;
154}
155
156static SlangResult _parseDiagnosticLine(
157    SliceAllocator& allocator,
158    const UnownedStringSlice& line,
159    List<UnownedStringSlice>& lineSlices,
160    ArtifactDiagnostic& outDiagnostic)
161{
162    /* ERROR: tests/diagnostics/syntax-error-intrinsic.slang:13: '@' : unexpected token */
163
164    if (lineSlices.getCount() < 4)
165    {
166        return SLANG_FAIL;
167    }
168    {
169        const UnownedStringSlice severitySlice = lineSlices[0].trim();
170
171        outDiagnostic.severity = ArtifactDiagnostic::Severity::Error;
172        if (severitySlice.caseInsensitiveEquals(UnownedStringSlice::fromLiteral("warning")))
173        {
174            outDiagnostic.severity = ArtifactDiagnostic::Severity::Warning;
175        }
176    }
177
178    outDiagnostic.filePath = allocator.allocate(lineSlices[1]);
179
180    SLANG_RETURN_ON_FAIL(StringUtil::parseInt(lineSlices[2], outDiagnostic.location.line));
181    outDiagnostic.text = allocator.allocate(lineSlices[3].begin(), line.end());
182    return SLANG_OK;
183}
184
185SlangResult GlslangDownstreamCompiler::compile(
186    const CompileOptions& inOptions,
187    IArtifact** outArtifact)
188{
189    if (!isVersionCompatible(inOptions))
190    {
191        // Not possible to compile with this version of the interface.
192        return SLANG_E_NOT_IMPLEMENTED;
193    }
194
195    CompileOptions options = getCompatibleVersion(&inOptions);
196
197    // This compiler can only handle a single artifact
198    if (options.sourceArtifacts.count != 1)
199    {
200        return SLANG_FAIL;
201    }
202
203    IArtifact* sourceArtifact = options.sourceArtifacts[0];
204
205    if (options.targetType != SLANG_SPIRV)
206    {
207        SLANG_ASSERT(!"Can only compile to SPIR-V");
208        return SLANG_FAIL;
209    }
210
211    StringBuilder diagnosticOutput;
212    auto diagnosticOutputFunc = [](void const* data, size_t size, void* userData)
213    { (*(StringBuilder*)userData).append((char const*)data, (char const*)data + size); };
214    List<uint8_t> spirv;
215    auto outputFunc = [](void const* data, size_t size, void* userData)
216    { ((List<uint8_t>*)userData)->addRange((uint8_t*)data, size); };
217
218    ComPtr<ISlangBlob> sourceBlob;
219    SLANG_RETURN_ON_FAIL(sourceArtifact->loadBlob(ArtifactKeep::Yes, sourceBlob.writeRef()));
220
221    String sourcePath = ArtifactUtil::findPath(sourceArtifact);
222
223    glslang_CompileRequest_1_2 request;
224    memset(&request, 0, sizeof(request));
225    request.sizeInBytes = sizeof(request);
226
227    switch (options.sourceLanguage)
228    {
229    case SLANG_SOURCE_LANGUAGE_GLSL:
230        request.action = GLSLANG_ACTION_COMPILE_GLSL_TO_SPIRV;
231        break;
232    case SLANG_SOURCE_LANGUAGE_SPIRV:
233        request.action = GLSLANG_ACTION_OPTIMIZE_SPIRV;
234        break;
235    default:
236        SLANG_ASSERT(!"Can only handle GLSL or SPIR-V as input.");
237        return SLANG_FAIL;
238    }
239
240    request.sourcePath = sourcePath.getBuffer();
241
242    request.slangStage = options.stage;
243
244    const char* inputBegin = (const char*)sourceBlob->getBufferPointer();
245    request.inputBegin = inputBegin;
246    request.inputEnd = inputBegin + sourceBlob->getBufferSize();
247
248    // Find the SPIR-V version if set
249    SemanticVersion spirvVersion;
250    for (const auto& capabilityVersion : options.requiredCapabilityVersions)
251    {
252        if (capabilityVersion.kind == DownstreamCompileOptions::CapabilityVersion::Kind::SPIRV)
253        {
254            if (capabilityVersion.version > spirvVersion)
255            {
256                spirvVersion = capabilityVersion.version;
257            }
258        }
259    }
260
261    request.spirvVersion.major = spirvVersion.m_major;
262    request.spirvVersion.minor = spirvVersion.m_minor;
263    request.spirvVersion.patch = spirvVersion.m_patch;
264
265    request.outputFunc = outputFunc;
266    request.outputUserData = &spirv;
267
268    request.diagnosticFunc = diagnosticOutputFunc;
269    request.diagnosticUserData = &diagnosticOutput;
270
271    request.optimizationLevel = (unsigned)options.optimizationLevel;
272    request.debugInfoType = (unsigned)options.debugInfoType;
273
274    request.entryPointName = options.entryPointName.begin();
275
276    const SlangResult invokeResult = _invoke(request);
277
278    auto artifact = ArtifactUtil::createArtifactForCompileTarget(options.targetType);
279
280    auto diagnostics = ArtifactDiagnostics::create();
281
282    // Set the diagnostics result
283    diagnostics->setResult(invokeResult);
284
285    ArtifactUtil::addAssociated(artifact, diagnostics);
286
287    if (SLANG_FAILED(invokeResult))
288    {
289        diagnostics->setRaw(SliceUtil::asCharSlice(diagnosticOutput));
290
291        SliceAllocator allocator;
292
293        SlangResult diagnosticParseRes = ArtifactDiagnosticUtil::parseColonDelimitedDiagnostics(
294            allocator,
295            diagnosticOutput.getUnownedSlice(),
296            1,
297            _parseDiagnosticLine,
298            diagnostics);
299        SLANG_UNUSED(diagnosticParseRes);
300
301        diagnostics->requireErrorDiagnostic();
302    }
303    else
304    {
305        artifact->addRepresentationUnknown(ListBlob::moveCreate(spirv));
306    }
307
308    *outArtifact = artifact.detach();
309    return SLANG_OK;
310}
311
312SlangResult GlslangDownstreamCompiler::validate(const uint32_t* contents, int contentsSize)
313{
314    if (m_validate == nullptr)
315    {
316        return SLANG_FAIL;
317    }
318
319    if (m_validate(contents, contentsSize))
320    {
321        return SLANG_OK;
322    }
323    return SLANG_FAIL;
324}
325
326SlangResult GlslangDownstreamCompiler::disassembleWithResult(
327    const uint32_t* contents,
328    int contentsSize,
329    String& outString)
330{
331    if (m_disassembleWithResult == nullptr)
332    {
333        return SLANG_FAIL;
334    }
335
336    char* resultString = nullptr;
337    if (m_disassembleWithResult(contents, contentsSize, &resultString))
338    {
339        if (resultString)
340        {
341            outString = String(resultString);
342            return SLANG_OK;
343        }
344    }
345    return SLANG_FAIL;
346}
347
348SlangResult GlslangDownstreamCompiler::disassemble(const uint32_t* contents, int contentsSize)
349{
350    if (m_disassemble == nullptr)
351    {
352        return SLANG_FAIL;
353    }
354
355    if (m_disassemble(contents, contentsSize))
356    {
357        return SLANG_OK;
358    }
359    return SLANG_FAIL;
360}
361
362SlangResult GlslangDownstreamCompiler::link(
363    const uint32_t** modules,
364    const uint32_t* moduleSizes,
365    const uint32_t moduleCount,
366    IArtifact** outArtifact)
367{
368    glslang_LinkRequest request;
369    memset(&request, 0, sizeof(request));
370
371    request.modules = modules;
372    request.moduleSizes = moduleSizes;
373    request.moduleCount = moduleCount;
374
375    if (!m_link(&request))
376    {
377        return SLANG_FAIL;
378    }
379
380    auto artifact = ArtifactUtil::createArtifactForCompileTarget(SLANG_SPIRV);
381    artifact->addRepresentationUnknown(
382        Slang::RawBlob::create(request.linkResult, request.linkResultSize * sizeof(uint32_t)));
383
384    *outArtifact = artifact.detach();
385    return SLANG_OK;
386}
387
388bool GlslangDownstreamCompiler::canConvert(const ArtifactDesc& from, const ArtifactDesc& to)
389{
390    // Can only disassemble blobs that are SPIR-V
391    return ArtifactDescUtil::isDisassembly(from, to) &&
392           ((from.payload == ArtifactPayload::SPIRV) ||
393            (from.payload == ArtifactPayload::WGSL_SPIRV));
394}
395
396SlangResult GlslangDownstreamCompiler::convert(
397    IArtifact* from,
398    const ArtifactDesc& to,
399    IArtifact** outArtifact)
400{
401    if (!canConvert(from->getDesc(), to))
402    {
403        return SLANG_FAIL;
404    }
405
406    ComPtr<ISlangBlob> blob;
407    SLANG_RETURN_ON_FAIL(from->loadBlob(ArtifactKeep::No, blob.writeRef()));
408
409    StringBuilder builder;
410
411    auto outputFunc = [](void const* data, size_t size, void* userData)
412    { (*(StringBuilder*)userData).append((char const*)data, (char const*)data + size); };
413
414    glslang_CompileRequest_1_2 request;
415    memset(&request, 0, sizeof(request));
416    request.sizeInBytes = sizeof(request);
417
418    request.action = GLSLANG_ACTION_DISSASSEMBLE_SPIRV;
419
420    request.sourcePath = nullptr;
421
422    char* blobData = (char*)blob->getBufferPointer();
423
424    request.inputBegin = blobData;
425    request.inputEnd = blobData + blob->getBufferSize();
426
427    request.outputFunc = outputFunc;
428    request.outputUserData = &builder;
429
430    SLANG_RETURN_ON_FAIL(_invoke(request));
431
432    auto disassemblyBlob = StringBlob::moveCreate(builder);
433
434    auto artifact = ArtifactUtil::createArtifact(to);
435    artifact->addRepresentationUnknown(disassemblyBlob);
436
437    *outArtifact = artifact.detach();
438
439    return SLANG_OK;
440}
441
442SlangResult GlslangDownstreamCompiler::getVersionString(slang::IBlob** outVersionString)
443{
444    uint64_t timestamp;
445    if (m_compile_1_1)
446    {
447        timestamp = SharedLibraryUtils::getSharedLibraryTimestamp((void*)m_compile_1_1);
448    }
449    else if (m_compile_1_0)
450    {
451        timestamp = SharedLibraryUtils::getSharedLibraryTimestamp((void*)m_compile_1_0);
452    }
453    else
454    {
455        return SLANG_FAIL;
456    }
457
458    auto timestampString = String(timestamp);
459    ComPtr<ISlangBlob> version = StringBlob::create(timestampString.getBuffer());
460    *outVersionString = version.detach();
461    return SLANG_OK;
462}
463
464static SlangResult locateGlslangSpirvDownstreamCompiler(
465    const String& path,
466    ISlangSharedLibraryLoader* loader,
467    DownstreamCompilerSet* set,
468    SlangPassThrough compilerType)
469{
470    ComPtr<ISlangSharedLibrary> library;
471
472#if SLANG_UNIX_FAMILY
473    // On unix systems we need to ensure pthread is loaded first.
474    // TODO(JS):
475    // There is an argument that this should be performed through the loader....
476    // NOTE! We don't currently load through a dependent library, as it is *assumed* something as
477    // core as 'ptheads' isn't going to be distributed with the shader compiler.
478    ComPtr<ISlangSharedLibrary> pthreadLibrary;
479    DefaultSharedLibraryLoader::load(loader, path, "pthread", pthreadLibrary.writeRef());
480    if (!pthreadLibrary.get())
481    {
482        DefaultSharedLibraryLoader::load(
483            loader,
484            path,
485            "libpthread.so.0",
486            pthreadLibrary.writeRef());
487    }
488
489#endif
490
491    SLANG_RETURN_ON_FAIL(
492        DownstreamCompilerUtil::loadSharedLibrary(path, loader, nullptr, "slang-glslang", library));
493
494    SLANG_ASSERT(library);
495    if (!library)
496    {
497        return SLANG_FAIL;
498    }
499
500    auto compiler = new GlslangDownstreamCompiler(compilerType);
501    ComPtr<IDownstreamCompiler> compilerIntf(compiler);
502    SLANG_RETURN_ON_FAIL(compiler->init(library));
503
504    set->addCompiler(compilerIntf);
505    return SLANG_OK;
506}
507
508SlangResult GlslangDownstreamCompilerUtil::locateCompilers(
509    const String& path,
510    ISlangSharedLibraryLoader* loader,
511    DownstreamCompilerSet* set)
512{
513    return locateGlslangSpirvDownstreamCompiler(path, loader, set, SLANG_PASS_THROUGH_GLSLANG);
514}
515
516SlangResult SpirvOptDownstreamCompilerUtil::locateCompilers(
517    const String& path,
518    ISlangSharedLibraryLoader* loader,
519    DownstreamCompilerSet* set)
520{
521    return locateGlslangSpirvDownstreamCompiler(path, loader, set, SLANG_PASS_THROUGH_SPIRV_OPT);
522}
523
524SlangResult SpirvDisDownstreamCompilerUtil::locateCompilers(
525    const String& path,
526    ISlangSharedLibraryLoader* loader,
527    DownstreamCompilerSet* set)
528{
529    return locateGlslangSpirvDownstreamCompiler(path, loader, set, SLANG_PASS_THROUGH_SPIRV_DIS);
530}
531
532SlangResult SpirvLinkDownstreamCompilerUtil::locateCompilers(
533    const String& path,
534    ISlangSharedLibraryLoader* loader,
535    DownstreamCompilerSet* set)
536{
537    return locateGlslangSpirvDownstreamCompiler(path, loader, set, SLANG_PASS_THROUGH_SPIRV_LINK);
538}
539
540#else // SLANG_ENABLE_GLSLANG_SUPPORT
541
542/* static */ SlangResult GlslangDownstreamCompilerUtil::locateCompilers(
543    const String& path,
544    ISlangSharedLibraryLoader* loader,
545    DownstreamCompilerSet* set)
546{
547    SLANG_UNUSED(path);
548    SLANG_UNUSED(loader);
549    SLANG_UNUSED(set);
550    return SLANG_E_NOT_AVAILABLE;
551}
552
553#endif // SLANG_ENABLE_GLSLANG_SUPPORT
554
555} // namespace Slang