yum-mirror/slang

Making it easier to work with shaders

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

Harsh Aggarwal (NVIDIA)Enabling optix ci pipeline (#7311)23dcea810

master
28.9 KiB775 linesraw
1// slang-support.cpp
2
3#define _CRT_SECURE_NO_WARNINGS 1
4
5#include "slang-support.h"
6
7#include "../../source/compiler-core/slang-artifact-desc-util.h"
8#include "../../source/core/slang-file-system.h"
9#include "../../source/core/slang-string-util.h"
10#include "../../source/core/slang-test-tool-util.h"
11#include "options.h"
12
13#include <assert.h>
14#include <stdio.h>
15
16namespace renderer_test
17{
18using namespace Slang;
19
20// Entry point name to use for vertex/fragment shader
21static const char vertexEntryPointName[] = "vertexMain";
22static const char fragmentEntryPointName[] = "fragmentMain";
23static const char computeEntryPointName[] = "computeMain";
24static const char rtEntryPointName[] = "raygenMain";
25static const char taskEntryPointName[] = "taskMain";
26static const char meshEntryPointName[] = "meshMain";
27
28void ShaderCompilerUtil::Output::set(slang::IComponentType* inSlangProgram)
29{
30    slangProgram = inSlangProgram;
31    desc.slangGlobalScope = inSlangProgram;
32}
33
34void ShaderCompilerUtil::Output::reset()
35{
36    {
37        desc.slangGlobalScope = nullptr;
38    }
39
40    globalSession = nullptr;
41    m_session = nullptr;
42}
43
44static SlangResult _compileProgramImpl(
45    slang::IGlobalSession* globalSession,
46    const Options& options,
47    const ShaderCompilerUtil::Input& input,
48    const ShaderCompileRequest& request,
49    ShaderCompilerUtil::Output& out)
50{
51    out.reset();
52
53    List<const char*> args;
54    for (const auto& arg : options.downstreamArgs.getArgsByName("slang"))
55    {
56        args.add(arg.value.getBuffer());
57        // The -load-repro feature is not maintained, and not supported by the new compile API.
58        // TODO: Remove this when the feature has been deprecated.
59        SLANG_ASSERT(arg.value != "-load-repro");
60    }
61
62    slang::TargetDesc sessionTargetDesc = {};
63    slang::SessionDesc sessionDesc = {};
64    ComPtr<ISlangUnknown> sessionDescMemory;
65    // If there are additional args parse them
66    if (args.getCount())
67    {
68        const auto res = globalSession->parseCommandLineArguments(
69            int(args.getCount()),
70            args.getBuffer(),
71            &sessionDesc,
72            sessionDescMemory.writeRef());
73        // If there is a parse failure and diagnostic, output it
74        if (SLANG_FAILED(res))
75        {
76            fprintf(stderr, "error: Failed to parse command line arguments: %d\n", int(res));
77            return res;
78        }
79        // We're setting the targets ourselves, below.
80        // To simplify that, we're currently not expecting targets to be added by the command line
81        // arguments.
82        if (sessionDesc.targetCount > 0)
83        {
84            fprintf(stderr, "error: Command line arguments added targets.\n");
85            return SLANG_FAIL;
86        }
87    }
88
89    // Argument parsing may have already added options, so add those first.
90    // For module reference options there are two cases:
91    // 1. If it's a slang module, then record the path and later create an IModule from that.
92    // 2. If not, then propagate the option.
93    // The reason to propagate the option in case 2 is that there is not currently a way of
94    // representing a module for a downstream compiler in the compilation API.
95    List<slang::CompilerOptionEntry> sessionOptionEntries;
96    List<Slang::String> referencedSlangModulePaths;
97    for (int optionIndex = 0; optionIndex < sessionDesc.compilerOptionEntryCount; optionIndex++)
98    {
99        slang::CompilerOptionEntry& option = sessionDesc.compilerOptionEntries[optionIndex];
100        if (option.name == slang::CompilerOptionName::ReferenceModule)
101        {
102            SLANG_ASSERT(option.value.kind == slang::CompilerOptionValueKind::String);
103            const char* path = option.value.stringValue0;
104            auto desc = Slang::ArtifactDescUtil::getDescFromPath(Slang::UnownedStringSlice(path));
105            switch (desc.payload)
106            {
107            case Slang::ArtifactDesc::Payload::SlangIR:
108            case Slang::ArtifactDesc::Payload::Slang:
109                referencedSlangModulePaths.add(option.value.stringValue0);
110                break;
111            case Slang::ArtifactDesc::Payload::DXIL:
112                sessionOptionEntries.add(option);
113                break;
114            default:
115                {
116                    fprintf(
117                        stderr,
118                        "error: Unexpected artifact payload type: %d\n",
119                        (int)desc.payload);
120                    return SLANG_FAIL;
121                }
122            }
123        }
124        else
125        {
126            sessionOptionEntries.add(option);
127        }
128    }
129
130    List<slang::PreprocessorMacroDesc> macros;
131
132    // Define a macro so that shader code in a test can detect what language we
133    // are nominally working with.
134    char const* langDefine = nullptr;
135    switch (input.sourceLanguage)
136    {
137    case SLANG_SOURCE_LANGUAGE_GLSL:
138        macros.add({"__GLSL__", "1"});
139        break;
140
141    case SLANG_SOURCE_LANGUAGE_SLANG:
142        macros.add({"__SLANG__", "1"});
143        // fall through
144    case SLANG_SOURCE_LANGUAGE_HLSL:
145        macros.add({"__HLSL__", "1"});
146        break;
147    case SLANG_SOURCE_LANGUAGE_C:
148        macros.add({"__C__", "1"});
149        break;
150    case SLANG_SOURCE_LANGUAGE_CPP:
151        macros.add({"__CPP__", "1"});
152        break;
153    case SLANG_SOURCE_LANGUAGE_CUDA:
154        macros.add({"__CUDA__", "1"});
155        break;
156    case SLANG_SOURCE_LANGUAGE_WGSL:
157        macros.add({"__WGSL__", "1"});
158        break;
159
160    default:
161        assert(!"unexpected");
162        break;
163    }
164
165    {
166        slang::CompilerOptionEntry entry;
167        entry.name = slang::CompilerOptionName::AllowGLSL;
168        entry.value.kind = slang::CompilerOptionValueKind::Int;
169        entry.value.intValue0 = int(options.allowGLSL);
170        sessionOptionEntries.add(entry);
171    }
172
173    {
174        slang::CompilerOptionEntry entry;
175        entry.name = slang::CompilerOptionName::PassThrough;
176        entry.value.kind = slang::CompilerOptionValueKind::Int;
177        entry.value.intValue0 = int(input.passThrough);
178        sessionOptionEntries.add(entry);
179    }
180
181    {
182        slang::CompilerOptionEntry entry;
183        entry.name = slang::CompilerOptionName::LineDirectiveMode;
184        entry.value.kind = slang::CompilerOptionValueKind::Int;
185        entry.value.intValue0 = int(SlangLineDirectiveMode::SLANG_LINE_DIRECTIVE_MODE_NONE);
186        sessionOptionEntries.add(entry);
187    }
188
189    sessionTargetDesc.format = input.target;
190    if (input.profile.getLength()) // do not set profile unless requested
191        sessionTargetDesc.profile = globalSession->findProfile(input.profile.getBuffer());
192    if (options.generateSPIRVDirectly)
193        sessionTargetDesc.flags |= SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY;
194    else
195        sessionTargetDesc.flags = 0;
196
197    {
198        slang::CompilerOptionEntry entry;
199        entry.value.kind = slang::CompilerOptionValueKind::Int;
200        if (options.generateSPIRVDirectly)
201        {
202            entry.name = slang::CompilerOptionName::EmitSpirvDirectly;
203            entry.value.intValue0 = int(options.generateSPIRVDirectly);
204        }
205        else
206        {
207            entry.name = slang::CompilerOptionName::EmitSpirvViaGLSL;
208            entry.value.intValue0 = int(!options.generateSPIRVDirectly);
209        }
210        sessionOptionEntries.add(entry);
211    }
212
213    // Not expecting argument parsing to have added any targets
214    SLANG_ASSERT(sessionDesc.targetCount == 0);
215    sessionDesc.targetCount = 1;
216    sessionDesc.targets = &sessionTargetDesc;
217
218    sessionDesc.skipSPIRVValidation = options.skipSPIRVValidation;
219    if (options.generateSPIRVDirectly)
220    {
221        slang::CompilerOptionEntry entry;
222        entry.name = slang::CompilerOptionName::DebugInformation;
223        entry.value.kind = slang::CompilerOptionValueKind::Int;
224        entry.value.intValue0 =
225            int(options.disableDebugInfo ? SlangDebugInfoLevel::SLANG_DEBUG_INFO_LEVEL_NONE
226                                         : SlangDebugInfoLevel::SLANG_DEBUG_INFO_LEVEL_STANDARD);
227        sessionOptionEntries.add(entry);
228    }
229
230    for (auto& capability : options.capabilities)
231    {
232        slang::CompilerOptionEntry entry;
233        entry.name = slang::CompilerOptionName::Capability;
234        entry.value.kind = slang::CompilerOptionValueKind::String;
235        entry.value.stringValue0 = capability.getBuffer();
236        sessionOptionEntries.add(entry);
237    }
238
239    sessionDesc.compilerOptionEntryCount = sessionOptionEntries.getCount();
240    sessionDesc.compilerOptionEntries = sessionOptionEntries.getBuffer();
241
242    // Argument parsing should not have added macros.
243    SLANG_ASSERT(sessionDesc.preprocessorMacroCount == 0);
244    sessionDesc.preprocessorMacroCount = (SlangInt)macros.getCount();
245    sessionDesc.preprocessorMacros = macros.getBuffer();
246
247    ComPtr<slang::ISession> slangSession = nullptr;
248    SLANG_RETURN_ON_FAIL(globalSession->createSession(sessionDesc, slangSession.writeRef()));
249    out.m_session = slangSession;
250    out.globalSession = globalSession;
251
252    String source(request.source.dataBegin, request.source.dataEnd);
253    ComPtr<slang::IBlob> diagnostics;
254    ComPtr<slang::IModule> module(slangSession->loadModuleFromSourceString(
255        "main",
256        request.source.path,
257        source.getBuffer(),
258        diagnostics.writeRef()));
259    if (!module)
260    {
261        fprintf(
262            stderr,
263            "error: Failed to load module: %s\n",
264            diagnostics ? (char*)diagnostics->getBufferPointer() : "(no diagnostic output)");
265        return SLANG_FAIL;
266    }
267
268    // Some tests are verifying that various warnings are printed, so print any diagnostics!
269    if (diagnostics && (diagnostics->getBufferSize() > 0U))
270        StdWriters::getError().print("%s", (char*)diagnostics->getBufferPointer());
271
272    ComPtr<slang::IModule> specializedModule;
273    List<ComPtr<slang::IEntryPoint>> specializedEntryPoints;
274    List<slang::IComponentType*> componentsRawPtr;
275
276    ComPtr<ISlangFileSystem> osFileSystem =
277        ComPtr<ISlangFileSystem>(Slang::OSFileSystem::getExtSingleton());
278
279    // This list is just kept so that the modules will be freed at scope exit
280    List<ComPtr<slang::IModule>> referencedModules;
281    for (auto& path : referencedSlangModulePaths)
282    {
283        auto desc =
284            Slang::ArtifactDescUtil::getDescFromPath(Slang::UnownedStringSlice(path.getBuffer()));
285        // If it's a GPU binary, then we'll assume it's a library
286        if (ArtifactDescUtil::isGpuUsable(desc))
287        {
288            desc.kind = ArtifactKind::Library;
289        }
290        const String name = ArtifactDescUtil::getBaseNameFromPath(desc, path.getUnownedSlice());
291
292        ComPtr<slang::IBlob> codeBlob;
293        SlangResult result = osFileSystem->loadFile(path.getBuffer(), codeBlob.writeRef());
294        if (SLANG_FAILED(result))
295        {
296            fprintf(stderr, "error: Failed to read referenced module file: %s\n", path.getBuffer());
297            return SLANG_FAIL;
298        }
299
300        ComPtr<slang::IModule> module;
301        switch (desc.payload)
302        {
303        case Slang::ArtifactDesc::Payload::Slang:
304            {
305                String sourceString(
306                    (const char*)codeBlob->getBufferPointer(),
307                    (const char*)codeBlob->getBufferPointer() + codeBlob->getBufferSize());
308                module = ComPtr<slang::IModule>(slangSession->loadModuleFromSourceString(
309                    name.getBuffer(),
310                    path.getBuffer(),
311                    sourceString.getBuffer(),
312                    diagnostics.writeRef()));
313                break;
314            }
315        case Slang::ArtifactDesc::Payload::SlangIR:
316            {
317                module = ComPtr<slang::IModule>(slangSession->loadModuleFromIRBlob(
318                    name.getBuffer(),
319                    path.getBuffer(),
320                    codeBlob,
321                    diagnostics.writeRef()));
322                break;
323            }
324        default:
325            {
326                SLANG_UNREACHABLE("Unexpected artifact payload type");
327            }
328        }
329
330        if (!module)
331        {
332            fprintf(
333                stderr,
334                "error: Failed to load referenced module: %s: %s\n",
335                path.getBuffer(),
336                diagnostics ? (char*)diagnostics->getBufferPointer() : "(no diagnostic output)");
337            return SLANG_FAIL;
338        }
339        referencedModules.add(module);
340        componentsRawPtr.add(module.get());
341    }
342
343    int globalSpecializationArgCount = int(request.globalSpecializationArgs.getCount());
344    int moduleSpecializationArgCount = module->getSpecializationParamCount();
345    if (globalSpecializationArgCount != moduleSpecializationArgCount)
346    {
347        fprintf(
348            stderr,
349            "error: The specialization argument count of the request (%d) does not match that of "
350            "the module (%d)!\n",
351            globalSpecializationArgCount,
352            moduleSpecializationArgCount);
353        return SLANG_FAIL;
354    }
355    List<slang::SpecializationArg> moduleSpecializationArgs;
356    for (int ii = 0; ii < globalSpecializationArgCount; ++ii)
357    {
358        String specializedTypeName = request.globalSpecializationArgs[ii].getBuffer();
359        slang::TypeReflection* typeReflection =
360            module->getLayout()->findTypeByName(specializedTypeName.getBuffer());
361        moduleSpecializationArgs.add(slang::SpecializationArg::fromType(typeReflection));
362    }
363
364    {
365        ComPtr<slang::IBlob> diagnostics;
366        auto res = module->specialize(
367            moduleSpecializationArgs.getBuffer(),
368            moduleSpecializationArgs.getCount(),
369            (slang::IComponentType**)specializedModule.writeRef(),
370            diagnostics.writeRef());
371        if (SLANG_FAILED(res))
372        {
373            fprintf(
374                stderr,
375                "error: Failed to specialize module: %s\n",
376                diagnostics ? (char*)diagnostics->getBufferPointer() : "(no diagnostic output)");
377            return res;
378        }
379    }
380
381    Index explicitEntryPointCount = request.entryPoints.getCount();
382    for (Index ee = 0; ee < explicitEntryPointCount; ++ee)
383    {
384        if (options.dontAddDefaultEntryPoints)
385        {
386            // If default entry points are not to be added, then
387            // the `request.entryPoints` array should have been
388            // left empty.
389            //
390            SLANG_ASSERT(false);
391        }
392
393        auto& entryPointInfo = request.entryPoints[ee];
394
395        ComPtr<slang::IEntryPoint> entryPoint;
396        ComPtr<slang::IBlob> diagnostics;
397        auto res = module->findAndCheckEntryPoint(
398            entryPointInfo.name,
399            entryPointInfo.slangStage,
400            entryPoint.writeRef(),
401            diagnostics.writeRef());
402        if (SLANG_FAILED(res))
403        {
404            fprintf(
405                stderr,
406                "error: Failed to find entry point '%s': %s\n",
407                entryPointInfo.name,
408                diagnostics ? (char*)diagnostics->getBufferPointer() : "(no diagnostic output)");
409            return res;
410        }
411
412        const int entryPointSpecializationArgCount =
413            int(request.entryPointSpecializationArgs.getCount());
414        if (entryPointSpecializationArgCount != entryPoint->getSpecializationParamCount())
415        {
416            fprintf(
417                stderr,
418                "error: %s\n",
419                "The specialization argument count of the requested entry point does not match "
420                "that of the entry point!");
421            return SLANG_FAIL;
422        }
423
424        List<slang::SpecializationArg> entryPointSpecializationArgs;
425        for (int ii = 0; ii < entryPointSpecializationArgCount; ++ii)
426        {
427            String specializedTypeName = request.entryPointSpecializationArgs[ii].getBuffer();
428            slang::TypeReflection* typeReflection =
429                module->getLayout()->findTypeByName(specializedTypeName.getBuffer());
430            entryPointSpecializationArgs.add(slang::SpecializationArg::fromType(typeReflection));
431        }
432
433        ComPtr<slang::IEntryPoint> specializedEntryPoint;
434        {
435            ComPtr<slang::IBlob> diagnostics;
436            auto res = entryPoint->specialize(
437                entryPointSpecializationArgs.getBuffer(),
438                entryPointSpecializationArgs.getCount(),
439                (slang::IComponentType**)specializedEntryPoint.writeRef(),
440                diagnostics.writeRef());
441            if (SLANG_FAILED(res))
442            {
443                fprintf(
444                    stderr,
445                    "error: Failed to specialize entry point: %s\n",
446                    diagnostics ? (char*)diagnostics->getBufferPointer()
447                                : "(no diagnostic output)");
448                return res;
449            }
450        }
451        specializedEntryPoints.add(specializedEntryPoint);
452    }
453
454    // If no explicit entry points were provided, check if the module has any
455    // defined entry points (e.g., functions marked with [shader(...)] attributes)
456    if (explicitEntryPointCount == 0 && !options.dontAddDefaultEntryPoints)
457    {
458        SlangInt32 definedEntryPointCount = module->getDefinedEntryPointCount();
459        for (SlangInt32 ee = 0; ee < definedEntryPointCount; ++ee)
460        {
461            ComPtr<slang::IEntryPoint> entryPoint;
462            SLANG_RETURN_ON_FAIL(module->getDefinedEntryPoint(ee, entryPoint.writeRef()));
463
464            // For now, we'll assume no specialization is needed for discovered entry points
465            // If specialization is needed, this would need to be updated
466            specializedEntryPoints.add(entryPoint);
467        }
468    }
469
470    if (input.passThrough == SLANG_PASS_THROUGH_NONE)
471    {
472        componentsRawPtr.add(specializedModule);
473        for (auto& specializedEntryPoint : specializedEntryPoints)
474            componentsRawPtr.add(specializedEntryPoint);
475    }
476
477    // This list just makes sure that the components get released
478    List<ComPtr<slang::ITypeConformance>> typeConformanceComponents;
479    if (request.typeConformances.getCount())
480    {
481        auto reflection = module->getLayout();
482        for (auto& conformance : request.typeConformances)
483        {
484            ComPtr<ISlangBlob> outDiagnostic;
485            auto derivedType = reflection->findTypeByName(conformance.derivedTypeName.getBuffer());
486            auto baseType = reflection->findTypeByName(conformance.baseTypeName.getBuffer());
487            ComPtr<slang::ITypeConformance> conformanceComponentType;
488            SlangResult res = slangSession->createTypeConformanceComponentType(
489                derivedType,
490                baseType,
491                conformanceComponentType.writeRef(),
492                conformance.idOverride,
493                outDiagnostic.writeRef());
494            if (SLANG_FAILED(res))
495            {
496                fprintf(
497                    stderr,
498                    "error: Failed to handle type conformances: %s\n",
499                    outDiagnostic ? (char*)outDiagnostic->getBufferPointer()
500                                  : "(no diagnostic output)");
501                return res;
502            }
503            typeConformanceComponents.add(conformanceComponentType);
504            componentsRawPtr.add(conformanceComponentType);
505        }
506    }
507
508    ComPtr<slang::IComponentType> linkedSlangProgram;
509    if (componentsRawPtr.getCount() > 0)
510    {
511        ComPtr<slang::IComponentType> composite;
512        ComPtr<ISlangBlob> outDiagnostic;
513        SlangResult res = slangSession->createCompositeComponentType(
514            componentsRawPtr.getBuffer(),
515            componentsRawPtr.getCount(),
516            composite.writeRef(),
517            outDiagnostic.writeRef());
518        if (SLANG_FAILED(res))
519        {
520            fprintf(
521                stderr,
522                "error: Failed to create composite: %s\n",
523                outDiagnostic ? (char*)outDiagnostic->getBufferPointer()
524                              : "(no diagnostic output)");
525            return res;
526        }
527        res = composite->link(linkedSlangProgram.writeRef(), outDiagnostic.writeRef());
528        if (SLANG_FAILED(res))
529        {
530            fprintf(
531                stderr,
532                "error: Failed to link program: %s\n",
533                outDiagnostic ? (char*)outDiagnostic->getBufferPointer()
534                              : "(no diagnostic output)");
535        }
536    }
537
538    out.set(linkedSlangProgram);
539    return SLANG_OK;
540}
541
542static SlangResult compileProgram(
543    slang::IGlobalSession* globalSession,
544    const Options& options,
545    const ShaderCompilerUtil::Input& input,
546    const ShaderCompileRequest& request,
547    ShaderCompilerUtil::Output& out)
548{
549    if (input.passThrough == SLANG_PASS_THROUGH_NONE)
550    {
551        return _compileProgramImpl(globalSession, options, input, request, out);
552    }
553    else
554    {
555        bool canUseSlangForPrecompile = false;
556        switch (input.passThrough)
557        {
558        case SLANG_PASS_THROUGH_DXC:
559        case SLANG_PASS_THROUGH_FXC:
560            canUseSlangForPrecompile = true;
561            break;
562        default:
563            break;
564        }
565        // If we are doing a HLSL pass-through compilation, then we can't rely
566        // on the downstream compiler for the reflection information that
567        // will drive all of our parameter binding. As such, we will first
568        // compile with Slang to get reflection information, and then
569        // compile in another pass using the desired downstream compiler
570        // so that we can get the refleciton information we need.
571        //
572        ShaderCompilerUtil::Output slangOutput;
573        if (canUseSlangForPrecompile)
574        {
575            ShaderCompilerUtil::Input slangInput = input;
576            slangInput.sourceLanguage = SLANG_SOURCE_LANGUAGE_SLANG;
577            slangInput.passThrough = SLANG_PASS_THROUGH_NONE;
578            // TODO: we want to pass along a flag to skip codegen...
579
580
581            SLANG_RETURN_ON_FAIL(
582                _compileProgramImpl(globalSession, options, slangInput, request, slangOutput));
583        }
584
585        // Now we have what we need to be able to do the downstream compile better.
586        //
587        // TODO: We should be able to use the output from the Slang compilation
588        // to fill in the actual entry points to be used for this compilation,
589        // so that discovery of entry points via `[shader(...)]` attributes will work.
590        //
591        SLANG_RETURN_ON_FAIL(_compileProgramImpl(globalSession, options, input, request, out));
592
593        out.m_session = slangOutput.m_session;
594        // slangOutput.desc.slangGlobalScope and slangOutput.slangProgram are the same object,
595        // but the latter is a ComPtr while the former isn't. Therefore we need to detach so
596        // that the object doesn't get destroyed.
597        SLANG_ASSERT(slangOutput.desc.slangGlobalScope == slangOutput.slangProgram.get());
598        out.desc.slangGlobalScope = slangOutput.slangProgram.detach();
599        slangOutput.m_session = nullptr;
600        return SLANG_OK;
601    }
602}
603
604// Helper for compileWithLayout
605/* static */ SlangResult readSource(const String& inSourcePath, List<char>& outSourceText)
606{
607    // Read in the source code
608    FILE* sourceFile = fopen(inSourcePath.getBuffer(), "rb");
609    if (!sourceFile)
610    {
611        fprintf(stderr, "error: failed to open '%s' for reading\n", inSourcePath.getBuffer());
612        return SLANG_FAIL;
613    }
614    fseek(sourceFile, 0, SEEK_END);
615    size_t sourceSize = ftell(sourceFile);
616    fseek(sourceFile, 0, SEEK_SET);
617
618    outSourceText.setCount(sourceSize + 1);
619    if (fread(outSourceText.getBuffer(), sourceSize, 1, sourceFile) != 1)
620    {
621        fprintf(stderr, "error: failed to read from '%s'\n", inSourcePath.getBuffer());
622        return SLANG_FAIL;
623    }
624    fclose(sourceFile);
625    outSourceText[sourceSize] = 0;
626
627    return SLANG_OK;
628}
629
630/* static */ SlangResult ShaderCompilerUtil::compileWithLayout(
631    slang::IGlobalSession* globalSession,
632    const Options& options,
633    const Input& input,
634    ShaderCompilerUtil::OutputAndLayout& output)
635{
636    String sourcePath = options.sourcePath;
637    auto shaderType = options.shaderType;
638
639    List<char> sourceText;
640    SLANG_RETURN_ON_FAIL(readSource(sourcePath, sourceText));
641
642    if (input.sourceLanguage == SLANG_SOURCE_LANGUAGE_CPP ||
643        input.sourceLanguage == SLANG_SOURCE_LANGUAGE_C)
644    {
645        // Add an include of the prelude
646        ComPtr<ISlangBlob> prelude;
647        globalSession->getLanguagePrelude(input.sourceLanguage, prelude.writeRef());
648
649        String preludeString = StringUtil::getString(prelude);
650
651        // Add the prelude
652        StringBuilder builder;
653        builder << preludeString << "\n";
654        builder << UnownedStringSlice(sourceText.getBuffer(), sourceText.getCount());
655
656        sourceText.setCount(builder.getLength());
657        memcpy(sourceText.getBuffer(), builder.getBuffer(), builder.getLength());
658    }
659
660    output.sourcePath = sourcePath;
661
662    auto& layout = output.layout;
663
664    // Default the amount of renderTargets based on shader type
665    switch (shaderType)
666    {
667    default:
668        layout.numRenderTargets = 1;
669        break;
670
671    case Options::ShaderProgramType::Compute:
672    case Options::ShaderProgramType::RayTracing:
673        layout.numRenderTargets = 0;
674        break;
675    }
676
677    // Deterministic random generator
678    RefPtr<RandomGenerator> rand = RandomGenerator::create(0x34234);
679
680    // Parse the layout
681    layout.parse(rand, sourceText.getBuffer());
682
683    // Setup SourceInfo
684    ShaderCompileRequest::SourceInfo sourceInfo;
685    sourceInfo.path = sourcePath.getBuffer();
686    sourceInfo.dataBegin = sourceText.getBuffer();
687    // Subtract 1 because it's zero terminated
688    sourceInfo.dataEnd = sourceText.getBuffer() + sourceText.getCount() - 1;
689
690    ShaderCompileRequest compileRequest;
691
692    compileRequest.source = sourceInfo;
693
694    // Now we will add the "default" entry point names/stages that
695    // are appropriate to the pipeline type being targetted, *unless*
696    // the options specify that we should leave out the default
697    // entry points and instead rely on the Slang compiler's built-in
698    // mechanisms for discovering entry points (e.g., `[shader(...)]`
699    // attributes).
700    //
701    if (!options.dontAddDefaultEntryPoints)
702    {
703        switch (shaderType)
704        {
705        case Options::ShaderProgramType::Graphics:
706        case Options::ShaderProgramType::GraphicsCompute:
707            {
708                ShaderCompileRequest::EntryPoint vertexEntryPoint;
709                vertexEntryPoint.name = vertexEntryPointName;
710                vertexEntryPoint.slangStage = SLANG_STAGE_VERTEX;
711                compileRequest.entryPoints.add(vertexEntryPoint);
712
713                ShaderCompileRequest::EntryPoint fragmentEntryPoint;
714                fragmentEntryPoint.name = fragmentEntryPointName;
715                fragmentEntryPoint.slangStage = SLANG_STAGE_FRAGMENT;
716                compileRequest.entryPoints.add(fragmentEntryPoint);
717            }
718            break;
719        case Options::ShaderProgramType::GraphicsTaskMeshCompute:
720            {
721                ShaderCompileRequest::EntryPoint taskEntryPoint;
722                taskEntryPoint.name = taskEntryPointName;
723                taskEntryPoint.slangStage = SLANG_STAGE_AMPLIFICATION;
724                compileRequest.entryPoints.add(taskEntryPoint);
725            }
726            [[fallthrough]];
727        case Options::ShaderProgramType::GraphicsMeshCompute:
728            {
729                ShaderCompileRequest::EntryPoint meshEntryPoint;
730                meshEntryPoint.name = meshEntryPointName;
731                meshEntryPoint.slangStage = SLANG_STAGE_MESH;
732                compileRequest.entryPoints.add(meshEntryPoint);
733
734                ShaderCompileRequest::EntryPoint fragmentEntryPoint;
735                fragmentEntryPoint.name = fragmentEntryPointName;
736                fragmentEntryPoint.slangStage = SLANG_STAGE_FRAGMENT;
737                compileRequest.entryPoints.add(fragmentEntryPoint);
738            }
739            break;
740        case Options::ShaderProgramType::RayTracing:
741            {
742                // Note: Current GPU ray tracing pipelines allow for an
743                // almost arbitrary mix of entry points for different stages
744                // to be used together (e.g., a single "program" might
745                // have multiple any-hit shaders, multiple miss shaders, etc.)
746                //
747                // Rather than try to define a fixed set of entry point
748                // names and stages that the testing will support, we will
749                // instead rely on `[shader(...)]` annotations to tell us
750                // what entry points are present in the input code.
751            }
752            break;
753        default:
754            {
755                ShaderCompileRequest::EntryPoint computeEntryPoint;
756                computeEntryPoint.name = computeEntryPointName;
757                computeEntryPoint.slangStage = SLANG_STAGE_COMPUTE;
758                compileRequest.entryPoints.add(computeEntryPoint);
759            }
760        }
761    }
762    compileRequest.globalSpecializationArgs = layout.globalSpecializationArgs;
763    compileRequest.entryPointSpecializationArgs = layout.entryPointSpecializationArgs;
764    for (auto conformance : layout.typeConformances)
765    {
766        ShaderCompileRequest::TypeConformance c;
767        c.derivedTypeName = conformance.derivedTypeName;
768        c.baseTypeName = conformance.baseTypeName;
769        c.idOverride = conformance.idOverride;
770        compileRequest.typeConformances.add(c);
771    }
772    return compileProgram(globalSession, options, input, compileRequest, output.output);
773}
774
775} // namespace renderer_test