xref: /aosp_15_r20/external/skia/tools/skslc/Main.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 #include "include/core/SkStream.h"
8 #include "src/base/SkStringView.h"
9 #include "src/core/SkCpu.h"
10 #include "src/core/SkOpts.h"
11 #include "src/sksl/SkSLCompiler.h"
12 #include "src/sksl/SkSLFileOutputStream.h"
13 #include "src/sksl/SkSLProgramSettings.h"
14 #include "src/sksl/SkSLStringStream.h"
15 #include "src/sksl/SkSLUtil.h"
16 #include "src/sksl/codegen/SkSLCodeGenTypes.h"
17 #include "src/sksl/codegen/SkSLGLSLCodeGenerator.h"
18 #include "src/sksl/codegen/SkSLHLSLCodeGenerator.h"
19 #include "src/sksl/codegen/SkSLMetalCodeGenerator.h"
20 #include "src/sksl/codegen/SkSLPipelineStageCodeGenerator.h"
21 #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h"
22 #include "src/sksl/codegen/SkSLRasterPipelineCodeGenerator.h"
23 #include "src/sksl/codegen/SkSLSPIRVCodeGenerator.h"
24 #include "src/sksl/codegen/SkSLSPIRVValidator.h"
25 #include "src/sksl/codegen/SkSLWGSLCodeGenerator.h"
26 #include "src/sksl/codegen/SkSLWGSLValidator.h"
27 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
28 #include "src/sksl/ir/SkSLProgram.h"
29 #include "src/sksl/ir/SkSLVarDeclarations.h"
30 #include "src/sksl/tracing/SkSLDebugTracePriv.h"
31 #include "src/utils/SkShaderUtils.h"
32 #include "tools/skslc/ProcessWorklist.h"
33 
34 #include "spirv-tools/libspirv.hpp"
35 
36 #include <fstream>
37 #include <limits.h>
38 #include <optional>
39 #include <stdarg.h>
40 #include <stdio.h>
41 
42 #if defined(SK_COMPILE_WITH_GN)
43 namespace SkOpts {
44     size_t raster_pipeline_highp_stride = 1;
45 }
46 
SkDebugf(const char format[],...)47 void SkDebugf(const char format[], ...) {
48     va_list args;
49     va_start(args, format);
50     vfprintf(stderr, format, args);
51     va_end(args);
52 }
53 #endif
54 
as_SkWStream(SkSL::OutputStream & s)55 static std::unique_ptr<SkWStream> as_SkWStream(SkSL::OutputStream& s) {
56     struct Adapter : public SkWStream {
57     public:
58         Adapter(SkSL::OutputStream& out) : fOut(out), fBytesWritten(0) {}
59 
60         bool write(const void* buffer, size_t size) override {
61             fOut.write(buffer, size);
62             fBytesWritten += size;
63             return true;
64         }
65         void flush() override {}
66         size_t bytesWritten() const override { return fBytesWritten; }
67 
68     private:
69         SkSL::OutputStream& fOut;
70         size_t fBytesWritten;
71     };
72 
73     return std::make_unique<Adapter>(s);
74 }
75 
consume_suffix(std::string * str,const char suffix[])76 static bool consume_suffix(std::string* str, const char suffix[]) {
77     if (!skstd::ends_with(*str, suffix)) {
78         return false;
79     }
80     str->resize(str->length() - strlen(suffix));
81     return true;
82 }
83 
84 class ShaderCapsTestFactory : public SkSL::ShaderCapsFactory {
85 public:
AddAndTrueToLoopCondition()86     static const SkSL::ShaderCaps* AddAndTrueToLoopCondition() {
87         static const SkSL::ShaderCaps* sCaps = []{
88             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
89             caps->fVersionDeclString = "#version 400";
90             caps->fAddAndTrueToLoopCondition = true;
91             return caps.release();
92         }();
93         return sCaps;
94     }
95 
CannotUseFractForNegativeValues()96     static const SkSL::ShaderCaps* CannotUseFractForNegativeValues() {
97         static const SkSL::ShaderCaps* sCaps = [] {
98             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
99             caps->fVersionDeclString = "#version 400";
100             caps->fCanUseFractForNegativeValues = false;
101             return caps.release();
102         }();
103         return sCaps;
104     }
105 
CannotUseFragCoord()106     static const SkSL::ShaderCaps* CannotUseFragCoord() {
107         static const SkSL::ShaderCaps* sCaps = [] {
108             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
109             caps->fVersionDeclString = "#version 400";
110             caps->fCanUseFragCoord = false;
111             return caps.release();
112         }();
113         return sCaps;
114     }
115 
CannotUseMinAndAbsTogether()116     static const SkSL::ShaderCaps* CannotUseMinAndAbsTogether() {
117         static const SkSL::ShaderCaps* sCaps = [] {
118             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
119             caps->fVersionDeclString = "#version 400";
120             caps->fCanUseMinAndAbsTogether = false;
121             return caps.release();
122         }();
123         return sCaps;
124     }
125 
CannotUseVoidInSequenceExpressions()126     static const SkSL::ShaderCaps* CannotUseVoidInSequenceExpressions() {
127         static const SkSL::ShaderCaps* sCaps = [] {
128             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
129             caps->fCanUseVoidInSequenceExpressions = false;
130             return caps.release();
131         }();
132         return sCaps;
133     }
134 
135 
DualSourceBlending()136     static const SkSL::ShaderCaps* DualSourceBlending() {
137         static const SkSL::ShaderCaps* sCaps = [] {
138             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
139             caps->fDualSourceBlendingSupport = true;
140             return caps.release();
141         }();
142         return sCaps;
143     }
144 
EmulateAbsIntFunction()145     static const SkSL::ShaderCaps* EmulateAbsIntFunction() {
146         static const SkSL::ShaderCaps* sCaps = [] {
147             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
148             caps->fVersionDeclString = "#version 400";
149             caps->fEmulateAbsIntFunction = true;
150             return caps.release();
151         }();
152         return sCaps;
153     }
154 
FramebufferFetchSupport()155     static const SkSL::ShaderCaps* FramebufferFetchSupport() {
156         static const SkSL::ShaderCaps* sCaps = [] {
157             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
158             caps->fFBFetchSupport = true;
159             caps->fFBFetchColorName = "FramebufferFragColor";  // a nice, backend-neutral name
160             return caps.release();
161         }();
162         return sCaps;
163     }
164 
MustDeclareFragmentFrontFacing()165     static const SkSL::ShaderCaps* MustDeclareFragmentFrontFacing() {
166         static const SkSL::ShaderCaps* sCaps = [] {
167             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
168             caps->fMustDeclareFragmentFrontFacing = true;
169             return caps.release();
170         }();
171         return sCaps;
172     }
173 
MustForceNegatedAtanParamToFloat()174     static const SkSL::ShaderCaps* MustForceNegatedAtanParamToFloat() {
175         static const SkSL::ShaderCaps* sCaps = [] {
176             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
177             caps->fVersionDeclString = "#version 400";
178             caps->fMustForceNegatedAtanParamToFloat = true;
179             return caps.release();
180         }();
181         return sCaps;
182     }
183 
MustForceNegatedLdexpParamToMultiply()184     static const SkSL::ShaderCaps* MustForceNegatedLdexpParamToMultiply() {
185         static const SkSL::ShaderCaps* sCaps = [] {
186             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
187             caps->fVersionDeclString = "#version 400";
188             caps->fMustForceNegatedLdexpParamToMultiply = true;
189             return caps.release();
190         }();
191         return sCaps;
192     }
193 
MustGuardDivisionEvenAfterExplicitZeroCheck()194     static const SkSL::ShaderCaps* MustGuardDivisionEvenAfterExplicitZeroCheck() {
195         static const SkSL::ShaderCaps* sCaps = [] {
196             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
197             caps->fMustGuardDivisionEvenAfterExplicitZeroCheck = true;
198             return caps.release();
199         }();
200         return sCaps;
201     }
202 
NoBuiltinDeterminantSupport()203     static const SkSL::ShaderCaps* NoBuiltinDeterminantSupport() {
204         static const SkSL::ShaderCaps* sCaps = [] {
205             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
206             caps->fVersionDeclString = "#version 400";
207             caps->fBuiltinDeterminantSupport = false;
208             return caps.release();
209         }();
210         return sCaps;
211     }
212 
NoBuiltinFMASupport()213     static const SkSL::ShaderCaps* NoBuiltinFMASupport() {
214         static const SkSL::ShaderCaps* sCaps = [] {
215             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
216             caps->fVersionDeclString = "#version 400";
217             caps->fBuiltinFMASupport = false;
218             return caps.release();
219         }();
220         return sCaps;
221     }
222 
NoExternalTextureSupport()223     static const SkSL::ShaderCaps* NoExternalTextureSupport() {
224         static const SkSL::ShaderCaps* sCaps = [] {
225             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
226             caps->fVersionDeclString = "#version 400";
227             caps->fExternalTextureSupport = false;
228             return caps.release();
229         }();
230         return sCaps;
231     }
232 
RemovePowWithConstantExponent()233     static const SkSL::ShaderCaps* RemovePowWithConstantExponent() {
234         static const SkSL::ShaderCaps* sCaps = [] {
235             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
236             caps->fVersionDeclString = "#version 400";
237             caps->fRemovePowWithConstantExponent = true;
238             return caps.release();
239         }();
240         return sCaps;
241     }
242 
RewriteDoWhileLoops()243     static const SkSL::ShaderCaps* RewriteDoWhileLoops() {
244         static const SkSL::ShaderCaps* sCaps = [] {
245             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
246             caps->fVersionDeclString = "#version 400";
247             caps->fRewriteDoWhileLoops = true;
248             return caps.release();
249         }();
250         return sCaps;
251     }
252 
RewriteMatrixComparisons()253     static const SkSL::ShaderCaps* RewriteMatrixComparisons() {
254         static const SkSL::ShaderCaps* sCaps = [] {
255             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
256             caps->fRewriteMatrixComparisons = true;
257             caps->fUsesPrecisionModifiers = true;
258             return caps.release();
259         }();
260         return sCaps;
261     }
262 
RewriteMatrixVectorMultiply()263     static const SkSL::ShaderCaps* RewriteMatrixVectorMultiply() {
264         static const SkSL::ShaderCaps* sCaps = [] {
265             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
266             caps->fVersionDeclString = "#version 400";
267             caps->fRewriteMatrixVectorMultiply = true;
268             return caps.release();
269         }();
270         return sCaps;
271     }
272 
RewriteSwitchStatements()273     static const SkSL::ShaderCaps* RewriteSwitchStatements() {
274         static const SkSL::ShaderCaps* sCaps = [] {
275             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
276             caps->fVersionDeclString = "#version 400";
277             caps->fRewriteSwitchStatements = true;
278             return caps.release();
279         }();
280         return sCaps;
281     }
282 
SampleMaskSupport()283     static const SkSL::ShaderCaps* SampleMaskSupport() {
284         static const SkSL::ShaderCaps* sCaps = [] {
285             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
286             caps->fVersionDeclString = "#version 400";
287             caps->fShaderDerivativeSupport = true;
288             caps->fSampleMaskSupport = true;
289             return caps.release();
290         }();
291         return sCaps;
292     }
293 
ShaderDerivativeExtensionString()294     static const SkSL::ShaderCaps* ShaderDerivativeExtensionString() {
295         static const SkSL::ShaderCaps* sCaps = [] {
296             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
297             caps->fVersionDeclString = "#version 400";
298             caps->fShaderDerivativeSupport = true;
299             caps->fShaderDerivativeExtensionString = "GL_OES_standard_derivatives";
300             caps->fUsesPrecisionModifiers = true;
301             return caps.release();
302         }();
303         return sCaps;
304     }
305 
UnfoldShortCircuitAsTernary()306     static const SkSL::ShaderCaps* UnfoldShortCircuitAsTernary() {
307         static const SkSL::ShaderCaps* sCaps = [] {
308             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
309             caps->fVersionDeclString = "#version 400";
310             caps->fUnfoldShortCircuitAsTernary = true;
311             return caps.release();
312         }();
313         return sCaps;
314     }
315 
UsesPrecisionModifiers()316     static const SkSL::ShaderCaps* UsesPrecisionModifiers() {
317         static const SkSL::ShaderCaps* sCaps = [] {
318             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
319             caps->fVersionDeclString = "#version 400";
320             caps->fUsesPrecisionModifiers = true;
321             return caps.release();
322         }();
323         return sCaps;
324     }
325 
Version110()326     static const SkSL::ShaderCaps* Version110() {
327         static const SkSL::ShaderCaps* sCaps = [] {
328             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
329             caps->fVersionDeclString = "#version 110";
330             caps->fGLSLGeneration = SkSL::GLSLGeneration::k110;
331             return caps.release();
332         }();
333         return sCaps;
334     }
335 
Version450Core()336     static const SkSL::ShaderCaps* Version450Core() {
337         static const SkSL::ShaderCaps* sCaps = [] {
338             std::unique_ptr<SkSL::ShaderCaps> caps = MakeShaderCaps();
339             caps->fVersionDeclString = "#version 450 core";
340             return caps.release();
341         }();
342         return sCaps;
343     }
344 };
345 
346 // Given a string containing an SkSL program, searches for a #pragma settings comment, like so:
347 //    /*#pragma settings Default Sharpen*/
348 // The passed-in Settings object will be updated accordingly. Any number of options can be provided.
detect_shader_settings(const std::string & text,SkSL::ProgramSettings * settings,const SkSL::ShaderCaps ** caps,std::unique_ptr<SkSL::DebugTracePriv> * debugTrace)349 static bool detect_shader_settings(const std::string& text,
350                                    SkSL::ProgramSettings* settings,
351                                    const SkSL::ShaderCaps** caps,
352                                    std::unique_ptr<SkSL::DebugTracePriv>* debugTrace) {
353     using Factory = ShaderCapsTestFactory;
354 
355     // Find a matching comment and isolate the name portion.
356     static constexpr char kPragmaSettings[] = "/*#pragma settings ";
357     const char* settingsPtr = strstr(text.c_str(), kPragmaSettings);
358     if (settingsPtr != nullptr) {
359         // Subtract one here in order to preserve the leading space, which is necessary to allow
360         // consumeSuffix to find the first item.
361         settingsPtr += strlen(kPragmaSettings) - 1;
362 
363         const char* settingsEnd = strstr(settingsPtr, "*/");
364         if (settingsEnd != nullptr) {
365             std::string settingsText{settingsPtr, size_t(settingsEnd - settingsPtr)};
366 
367             // Apply settings as requested. Since they can come in any order, repeat until we've
368             // consumed them all.
369             for (;;) {
370                 const size_t startingLength = settingsText.length();
371 
372                 if (consume_suffix(&settingsText, " AddAndTrueToLoopCondition")) {
373                     *caps = Factory::AddAndTrueToLoopCondition();
374                 }
375                 if (consume_suffix(&settingsText, " CannotUseFractForNegativeValues")) {
376                     *caps = Factory::CannotUseFractForNegativeValues();
377                 }
378                 if (consume_suffix(&settingsText, " CannotUseFragCoord")) {
379                     *caps = Factory::CannotUseFragCoord();
380                 }
381                 if (consume_suffix(&settingsText, " CannotUseMinAndAbsTogether")) {
382                     *caps = Factory::CannotUseMinAndAbsTogether();
383                 }
384                 if (consume_suffix(&settingsText, " CannotUseVoidInSequenceExpressions")) {
385                     *caps = Factory::CannotUseVoidInSequenceExpressions();
386                 }
387                 if (consume_suffix(&settingsText, " DualSourceBlending")) {
388                     *caps = Factory::DualSourceBlending();
389                 }
390                 if (consume_suffix(&settingsText, " Default")) {
391                     *caps = Factory::Default();
392                 }
393                 if (consume_suffix(&settingsText, " EmulateAbsIntFunction")) {
394                     *caps = Factory::EmulateAbsIntFunction();
395                 }
396                 if (consume_suffix(&settingsText, " FramebufferFetchSupport")) {
397                     *caps = Factory::FramebufferFetchSupport();
398                 }
399                 if (consume_suffix(&settingsText, " MustGuardDivisionEvenAfterExplicitZeroCheck")) {
400                     *caps = Factory::MustGuardDivisionEvenAfterExplicitZeroCheck();
401                 }
402                 if (consume_suffix(&settingsText, " MustDeclareFragmentFrontFacing")) {
403                     *caps = Factory::MustDeclareFragmentFrontFacing();
404                 }
405                 if (consume_suffix(&settingsText, " MustForceNegatedAtanParamToFloat")) {
406                     *caps = Factory::MustForceNegatedAtanParamToFloat();
407                 }
408                 if (consume_suffix(&settingsText, " MustForceNegatedLdexpParamToMultiply")) {
409                     *caps = Factory::MustForceNegatedLdexpParamToMultiply();
410                 }
411                 if (consume_suffix(&settingsText, " NoBuiltinDeterminantSupport")) {
412                     *caps = Factory::NoBuiltinDeterminantSupport();
413                 }
414                 if (consume_suffix(&settingsText, " NoBuiltinFMASupport")) {
415                     *caps = Factory::NoBuiltinFMASupport();
416                 }
417                 if (consume_suffix(&settingsText, " NoExternalTextureSupport")) {
418                     *caps = Factory::NoExternalTextureSupport();
419                 }
420                 if (consume_suffix(&settingsText, " RemovePowWithConstantExponent")) {
421                     *caps = Factory::RemovePowWithConstantExponent();
422                 }
423                 if (consume_suffix(&settingsText, " RewriteDoWhileLoops")) {
424                     *caps = Factory::RewriteDoWhileLoops();
425                 }
426                 if (consume_suffix(&settingsText, " RewriteSwitchStatements")) {
427                     *caps = Factory::RewriteSwitchStatements();
428                 }
429                 if (consume_suffix(&settingsText, " RewriteMatrixVectorMultiply")) {
430                     *caps = Factory::RewriteMatrixVectorMultiply();
431                 }
432                 if (consume_suffix(&settingsText, " RewriteMatrixComparisons")) {
433                     *caps = Factory::RewriteMatrixComparisons();
434                 }
435                 if (consume_suffix(&settingsText, " ShaderDerivativeExtensionString")) {
436                     *caps = Factory::ShaderDerivativeExtensionString();
437                 }
438                 if (consume_suffix(&settingsText, " UnfoldShortCircuitAsTernary")) {
439                     *caps = Factory::UnfoldShortCircuitAsTernary();
440                 }
441                 if (consume_suffix(&settingsText, " UsesPrecisionModifiers")) {
442                     *caps = Factory::UsesPrecisionModifiers();
443                 }
444                 if (consume_suffix(&settingsText, " Version110")) {
445                     *caps = Factory::Version110();
446                 }
447                 if (consume_suffix(&settingsText, " Version450Core")) {
448                     *caps = Factory::Version450Core();
449                 }
450                 if (consume_suffix(&settingsText, " AllowNarrowingConversions")) {
451                     settings->fAllowNarrowingConversions = true;
452                 }
453                 if (consume_suffix(&settingsText, " ForceHighPrecision")) {
454                     settings->fForceHighPrecision = true;
455                 }
456                 if (consume_suffix(&settingsText, " NoInline")) {
457                     settings->fInlineThreshold = 0;
458                 }
459                 if (consume_suffix(&settingsText, " NoOptimize")) {
460                     settings->fOptimize = false;
461                     settings->fInlineThreshold = 0;
462                 }
463                 if (consume_suffix(&settingsText, " NoRTFlip")) {
464                     settings->fForceNoRTFlip = true;
465                 }
466                 if (consume_suffix(&settingsText, " InlineThresholdMax")) {
467                     settings->fInlineThreshold = INT_MAX;
468                 }
469                 if (consume_suffix(&settingsText, " Sharpen")) {
470                     settings->fSharpenTextures = true;
471                 }
472                 if (consume_suffix(&settingsText, " DebugTrace")) {
473                     settings->fOptimize = false;
474                     *debugTrace = std::make_unique<SkSL::DebugTracePriv>();
475                 }
476 
477                 if (settingsText.empty()) {
478                     break;
479                 }
480                 if (settingsText.length() == startingLength) {
481                     printf("Unrecognized #pragma settings: %s\n", settingsText.c_str());
482                     return false;
483                 }
484             }
485         }
486     }
487 
488     return true;
489 }
490 
491 /**
492  * Displays a usage banner; used when the command line arguments don't make sense.
493  */
show_usage()494 static void show_usage() {
495     printf("usage: skslc <input> <output> <flags>\n"
496            "       skslc <worklist>\n"
497            "\n"
498            "Allowed flags:\n"
499            "--settings:   honor embedded /*#pragma settings*/ comments.\n"
500            "--nosettings: ignore /*#pragma settings*/ comments\n");
501 }
502 
set_flag(std::optional<bool> * flag,const char * name,bool value)503 static bool set_flag(std::optional<bool>* flag, const char* name, bool value) {
504     if (flag->has_value()) {
505         printf("%s flag was specified multiple times\n", name);
506         return false;
507     }
508     *flag = value;
509     return true;
510 }
511 
512 /**
513  * Handle a single input.
514  */
process_command(SkSpan<std::string> args)515 static ResultCode process_command(SkSpan<std::string> args) {
516     std::optional<bool> honorSettings;
517     std::vector<std::string> paths;
518     for (size_t i = 1; i < args.size(); ++i) {
519         const std::string& arg = args[i];
520         if (arg == "--settings") {
521             if (!set_flag(&honorSettings, "settings", true)) {
522                 return ResultCode::kInputError;
523             }
524         } else if (arg == "--nosettings") {
525             if (!set_flag(&honorSettings, "settings", false)) {
526                 return ResultCode::kInputError;
527             }
528         } else if (!skstd::starts_with(arg, "--")) {
529             paths.push_back(arg);
530         } else {
531             show_usage();
532             return ResultCode::kInputError;
533         }
534     }
535     if (paths.size() != 2) {
536         show_usage();
537         return ResultCode::kInputError;
538     }
539 
540     if (!honorSettings.has_value()) {
541         honorSettings = true;
542     }
543 
544     const std::string& inputPath = paths[0];
545     const std::string& outputPath = paths[1];
546     SkSL::ProgramKind kind;
547     if (skstd::ends_with(inputPath, ".vert")) {
548         kind = SkSL::ProgramKind::kVertex;
549     } else if (skstd::ends_with(inputPath, ".frag") || skstd::ends_with(inputPath, ".sksl")) {
550         kind = SkSL::ProgramKind::kFragment;
551     } else if (skstd::ends_with(inputPath, ".mvert")) {
552         kind = SkSL::ProgramKind::kMeshVertex;
553     } else if (skstd::ends_with(inputPath, ".mfrag")) {
554         kind = SkSL::ProgramKind::kMeshFragment;
555     } else if (skstd::ends_with(inputPath, ".compute")) {
556         kind = SkSL::ProgramKind::kCompute;
557     } else if (skstd::ends_with(inputPath, ".rtb")) {
558         kind = SkSL::ProgramKind::kRuntimeBlender;
559     } else if (skstd::ends_with(inputPath, ".rtcf")) {
560         kind = SkSL::ProgramKind::kRuntimeColorFilter;
561     } else if (skstd::ends_with(inputPath, ".rts")) {
562         kind = SkSL::ProgramKind::kRuntimeShader;
563     } else if (skstd::ends_with(inputPath, ".privrts")) {
564         kind = SkSL::ProgramKind::kPrivateRuntimeShader;
565     } else {
566         printf("input filename must end in '.vert', '.frag', '.mvert', '.mfrag', '.compute', "
567                "'.rtb', '.rtcf', '.rts', '.privrts', or '.sksl'\n");
568         return ResultCode::kInputError;
569     }
570 
571     std::ifstream in(inputPath);
572     std::string text((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
573     if (in.rdstate()) {
574         printf("error reading '%s'\n", inputPath.c_str());
575         return ResultCode::kInputError;
576     }
577 
578     SkSL::ProgramSettings settings;
579     const SkSL::ShaderCaps* caps = SkSL::ShaderCapsFactory::Standalone();
580     std::unique_ptr<SkSL::DebugTracePriv> debugTrace;
581     if (*honorSettings) {
582         if (!detect_shader_settings(text, &settings, &caps, &debugTrace)) {
583             return ResultCode::kInputError;
584         }
585     }
586 
587     // This tells the compiler where the rt-flip uniform will live should it be required. For
588     // testing purposes we don't care where that is, but the compiler will report an error if we
589     // leave them at their default invalid values, or if the offset overlaps another uniform.
590     settings.fRTFlipOffset  = 16384;
591     settings.fRTFlipSet     = 0;
592     settings.fRTFlipBinding = 0;
593 
594     auto emitCompileError = [&](const char* errorText) {
595         // Overwrite the compiler output, if any, with an error message.
596         SkSL::FileOutputStream errorStream(outputPath.c_str());
597         errorStream.writeText("### Compilation failed:\n\n");
598         errorStream.writeText(errorText);
599         errorStream.close();
600         // Also emit the error directly to stdout.
601         puts(errorText);
602     };
603 
604     auto compileProgram = [&](const auto& writeFn) -> ResultCode {
605         SkSL::FileOutputStream out(outputPath.c_str());
606         SkSL::Compiler compiler;
607         if (!out.isValid()) {
608             printf("error writing '%s'\n", outputPath.c_str());
609             return ResultCode::kOutputError;
610         }
611         std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, text, settings);
612         if (!program || !writeFn(compiler, caps, *program, out)) {
613             out.close();
614             emitCompileError(compiler.errorText().c_str());
615             return ResultCode::kCompileError;
616         }
617         if (!out.close()) {
618             printf("error writing '%s'\n", outputPath.c_str());
619             return ResultCode::kOutputError;
620         }
621         return ResultCode::kSuccess;
622     };
623 
624     auto compileProgramAsRuntimeShader = [&](const auto& writeFn) -> ResultCode {
625         if (kind == SkSL::ProgramKind::kVertex) {
626             emitCompileError("Runtime shaders do not support vertex programs\n");
627             return ResultCode::kCompileError;
628         }
629         if (kind == SkSL::ProgramKind::kFragment) {
630             // Handle .sksl and .frag programs as runtime shaders.
631             kind = SkSL::ProgramKind::kPrivateRuntimeShader;
632         }
633         return compileProgram(writeFn);
634     };
635 
636     if (skstd::ends_with(outputPath, ".spirv")) {
637         return compileProgram([](SkSL::Compiler& compiler,
638                                  const SkSL::ShaderCaps* shaderCaps,
639                                  SkSL::Program& program,
640                                  SkSL::OutputStream& out) {
641             return SkSL::ToSPIRV(program, shaderCaps, out, SkSL::ValidateSPIRVAndDissassemble);
642         });
643     } else if (skstd::ends_with(outputPath, ".asm.frag") ||
644                skstd::ends_with(outputPath, ".asm.vert") ||
645                skstd::ends_with(outputPath, ".asm.comp")) {
646         return compileProgram(
647                 [](SkSL::Compiler& compiler,
648                    const SkSL::ShaderCaps* shaderCaps,
649                    SkSL::Program& program,
650                    SkSL::OutputStream& out) {
651                     // Compile program to SPIR-V assembly in a string-stream.
652                     SkSL::StringStream assembly;
653                     if (!SkSL::ToSPIRV(program,
654                                        shaderCaps,
655                                        assembly,
656                                        SkSL::ValidateSPIRVAndDissassemble)) {
657                         return false;
658                     }
659                     // Convert the string-stream to a SPIR-V disassembly.
660                     spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
661                     const std::string& spirv(assembly.str());
662                     std::string disassembly;
663                     uint32_t options = spvtools::SpirvTools::kDefaultDisassembleOption;
664                     options |= SPV_BINARY_TO_TEXT_OPTION_INDENT;
665                     if (!tools.Disassemble((const uint32_t*)spirv.data(),
666                                            spirv.size() / 4,
667                                            &disassembly,
668                                            options)) {
669                         return false;
670                     }
671                     // Finally, write the disassembly to our output stream.
672                     out.write(disassembly.data(), disassembly.size());
673                     return true;
674                 });
675     } else if (skstd::ends_with(outputPath, ".glsl")) {
676         return compileProgram([](SkSL::Compiler& compiler,
677                                  const SkSL::ShaderCaps* shaderCaps,
678                                  SkSL::Program& program,
679                                  SkSL::OutputStream& out) {
680             return SkSL::ToGLSL(program, shaderCaps, out, SkSL::PrettyPrint::kYes);
681         });
682     } else if (skstd::ends_with(outputPath, ".metal")) {
683         return compileProgram([](SkSL::Compiler& compiler,
684                                  const SkSL::ShaderCaps* shaderCaps,
685                                  SkSL::Program& program,
686                                  SkSL::OutputStream& out) {
687             return SkSL::ToMetal(program, shaderCaps, out, SkSL::PrettyPrint::kYes);
688         });
689     } else if (skstd::ends_with(outputPath, ".hlsl")) {
690         return compileProgram([](SkSL::Compiler& compiler,
691                                  const SkSL::ShaderCaps* shaderCaps,
692                                  SkSL::Program& program,
693                                  SkSL::OutputStream& out) {
694             return SkSL::ToHLSL(program, shaderCaps, out, SkSL::ValidateSPIRVAndDissassemble);
695         });
696     } else if (skstd::ends_with(outputPath, ".wgsl")) {
697         return compileProgram([](SkSL::Compiler& compiler,
698                                  const SkSL::ShaderCaps* shaderCaps,
699                                  SkSL::Program& program,
700                                  SkSL::OutputStream& out) {
701             return SkSL::ToWGSL(program,
702                                 shaderCaps,
703                                 out,
704                                 SkSL::PrettyPrint::kYes,
705                                 SkSL::IncludeSyntheticCode::kYes,
706                                 SkSL::ValidateWGSL);
707         });
708     } else if (skstd::ends_with(outputPath, ".skrp")) {
709         settings.fMaxVersionAllowed = SkSL::Version::k300;
710         return compileProgramAsRuntimeShader([&](SkSL::Compiler& compiler,
711                                                  const SkSL::ShaderCaps* shaderCaps,
712                                                  SkSL::Program& program,
713                                                  SkSL::OutputStream& out) {
714             SkSL::DebugTracePriv skrpDebugTrace;
715             const SkSL::FunctionDeclaration* main = program.getFunction("main");
716             if (!main) {
717                 compiler.errorReporter().error({}, "code has no entrypoint");
718                 return false;
719             }
720             bool wantTraceOps = (debugTrace != nullptr);
721             std::unique_ptr<SkSL::RP::Program> rasterProg = SkSL::MakeRasterPipelineProgram(
722                     program, *main->definition(), &skrpDebugTrace, wantTraceOps);
723             if (!rasterProg) {
724                 compiler.errorReporter().error({}, "code is not supported");
725                 return false;
726             }
727             rasterProg->dump(as_SkWStream(out).get(), /*writeInstructionCount=*/true);
728             return true;
729         });
730     } else if (skstd::ends_with(outputPath, ".stage")) {
731         return compileProgram([](SkSL::Compiler&,
732                                  const SkSL::ShaderCaps* shaderCaps,
733                                  SkSL::Program& program,
734                                  SkSL::OutputStream& out) {
735             class Callbacks : public SkSL::PipelineStage::Callbacks {
736             public:
737                 std::string getMangledName(const char* name) override {
738                     return std::string(name) + "_0";
739                 }
740 
741                 std::string declareUniform(const SkSL::VarDeclaration* decl) override {
742                     fOutput += decl->description();
743                     return std::string(decl->var()->name());
744                 }
745 
746                 void defineFunction(const char* decl, const char* body, bool /*isMain*/) override {
747                     fOutput += std::string(decl) + '{' + body + '}';
748                 }
749 
750                 void declareFunction(const char* decl) override { fOutput += decl; }
751 
752                 void defineStruct(const char* definition) override { fOutput += definition; }
753 
754                 void declareGlobal(const char* declaration) override { fOutput += declaration; }
755 
756                 std::string sampleShader(int index, std::string coords) override {
757                     return "child_" + std::to_string(index) + ".eval(" + coords + ')';
758                 }
759 
760                 std::string sampleColorFilter(int index, std::string color) override {
761                     return "child_" + std::to_string(index) + ".eval(" + color + ')';
762                 }
763 
764                 std::string sampleBlender(int index, std::string src, std::string dst) override {
765                     return "child_" + std::to_string(index) + ".eval(" + src + ", " + dst + ')';
766                 }
767 
768                 std::string toLinearSrgb(std::string color) override {
769                     return "toLinearSrgb(" + color + ')';
770                 }
771                 std::string fromLinearSrgb(std::string color) override {
772                     return "fromLinearSrgb(" + color + ')';
773                 }
774 
775                 std::string fOutput;
776             };
777             // The .stage output looks almost like valid SkSL, but not quite.
778             // The PipelineStageGenerator bridges the gap between the SkSL in `program`,
779             // and the C++ FP builder API (see GrSkSLFP). In that API, children don't need
780             // to be declared (so they don't emit declarations here). Children are sampled
781             // by index, not name - so all children here are just "child_N".
782             // The input color and coords have names in the original SkSL (as parameters to
783             // main), but those are ignored here. References to those variables become
784             // "_coords" and "_inColor". At runtime, those variable names are irrelevant
785             // when the new SkSL is emitted inside the FP - references to those variables
786             // are replaced with strings from EmitArgs, and might be varyings or differently
787             // named parameters.
788             Callbacks callbacks;
789             SkSL::PipelineStage::ConvertProgram(program, "_coords", "_inColor",
790                                                 "_canvasColor", &callbacks);
791             out.writeString(SkShaderUtils::PrettyPrint(callbacks.fOutput));
792             return true;
793         });
794     } else {
795         printf("expected output path to end with one of: .glsl, .html, .metal, .hlsl, .wgsl, "
796                ".spirv, .asm.vert, .asm.frag, .asm.comp, .skrp, .stage (got '%s')\n",
797                outputPath.c_str());
798         return ResultCode::kConfigurationError;
799     }
800     return ResultCode::kSuccess;
801 }
802 
main(int argc,const char ** argv)803 int main(int argc, const char** argv) {
804     if (argc == 2) {
805         // Worklists are the only two-argument case for skslc, and we don't intend to support
806         // nested worklists, so we can process them here.
807         return (int)ProcessWorklist(argv[1], process_command);
808     } else {
809         // Process non-worklist inputs.
810         std::vector<std::string> args;
811         for (int index=0; index<argc; ++index) {
812             args.push_back(argv[index]);
813         }
814 
815         return (int)process_command(args);
816     }
817 }
818