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