1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2016 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#define SK_OPTS_NS skslc_standalone
9cb93a386Sopenharmony_ci#include "src/core/SkOpts.h"
10cb93a386Sopenharmony_ci#include "src/opts/SkChecksum_opts.h"
11cb93a386Sopenharmony_ci#include "src/opts/SkVM_opts.h"
12cb93a386Sopenharmony_ci
13cb93a386Sopenharmony_ci#include "src/gpu/GrShaderUtils.h"
14cb93a386Sopenharmony_ci#include "src/sksl/SkSLCompiler.h"
15cb93a386Sopenharmony_ci#include "src/sksl/SkSLDehydrator.h"
16cb93a386Sopenharmony_ci#include "src/sksl/SkSLFileOutputStream.h"
17cb93a386Sopenharmony_ci#include "src/sksl/SkSLStringStream.h"
18cb93a386Sopenharmony_ci#include "src/sksl/SkSLUtil.h"
19cb93a386Sopenharmony_ci#include "src/sksl/codegen/SkSLPipelineStageCodeGenerator.h"
20cb93a386Sopenharmony_ci#include "src/sksl/codegen/SkSLVMCodeGenerator.h"
21cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLUnresolvedFunction.h"
22cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLVarDeclarations.h"
23cb93a386Sopenharmony_ci
24cb93a386Sopenharmony_ci#include "spirv-tools/libspirv.hpp"
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_ci#include <fstream>
27cb93a386Sopenharmony_ci#include <limits.h>
28cb93a386Sopenharmony_ci#include <stdarg.h>
29cb93a386Sopenharmony_ci#include <stdio.h>
30cb93a386Sopenharmony_ci
31cb93a386Sopenharmony_civoid SkDebugf(const char format[], ...) {
32cb93a386Sopenharmony_ci    va_list args;
33cb93a386Sopenharmony_ci    va_start(args, format);
34cb93a386Sopenharmony_ci    vfprintf(stderr, format, args);
35cb93a386Sopenharmony_ci    va_end(args);
36cb93a386Sopenharmony_ci}
37cb93a386Sopenharmony_ci
38cb93a386Sopenharmony_cinamespace SkOpts {
39cb93a386Sopenharmony_ci    decltype(hash_fn) hash_fn = skslc_standalone::hash_fn;
40cb93a386Sopenharmony_ci    decltype(interpret_skvm) interpret_skvm = skslc_standalone::interpret_skvm;
41cb93a386Sopenharmony_ci}
42cb93a386Sopenharmony_ci
43cb93a386Sopenharmony_cienum class ResultCode {
44cb93a386Sopenharmony_ci    kSuccess = 0,
45cb93a386Sopenharmony_ci    kCompileError = 1,
46cb93a386Sopenharmony_ci    kInputError = 2,
47cb93a386Sopenharmony_ci    kOutputError = 3,
48cb93a386Sopenharmony_ci    kConfigurationError = 4,
49cb93a386Sopenharmony_ci};
50cb93a386Sopenharmony_ci
51cb93a386Sopenharmony_cistatic std::unique_ptr<SkWStream> as_SkWStream(SkSL::OutputStream& s) {
52cb93a386Sopenharmony_ci    struct Adapter : public SkWStream {
53cb93a386Sopenharmony_ci    public:
54cb93a386Sopenharmony_ci        Adapter(SkSL::OutputStream& out) : fOut(out), fBytesWritten(0) {}
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci        bool write(const void* buffer, size_t size) override {
57cb93a386Sopenharmony_ci            fOut.write(buffer, size);
58cb93a386Sopenharmony_ci            fBytesWritten += size;
59cb93a386Sopenharmony_ci            return true;
60cb93a386Sopenharmony_ci        }
61cb93a386Sopenharmony_ci        void flush() override {}
62cb93a386Sopenharmony_ci        size_t bytesWritten() const override { return fBytesWritten; }
63cb93a386Sopenharmony_ci
64cb93a386Sopenharmony_ci    private:
65cb93a386Sopenharmony_ci        SkSL::OutputStream& fOut;
66cb93a386Sopenharmony_ci        size_t fBytesWritten;
67cb93a386Sopenharmony_ci    };
68cb93a386Sopenharmony_ci
69cb93a386Sopenharmony_ci    return std::make_unique<Adapter>(s);
70cb93a386Sopenharmony_ci}
71cb93a386Sopenharmony_ci
72cb93a386Sopenharmony_ci// Given the path to a file (e.g. src/gpu/effects/GrFooFragmentProcessor.fp) and the expected
73cb93a386Sopenharmony_ci// filename prefix and suffix (e.g. "Gr" and ".fp"), returns the "base name" of the
74cb93a386Sopenharmony_ci// file (in this case, 'FooFragmentProcessor'). If no match, returns the empty string.
75cb93a386Sopenharmony_cistatic SkSL::String base_name(const SkSL::String& fpPath, const char* prefix, const char* suffix) {
76cb93a386Sopenharmony_ci    SkSL::String result;
77cb93a386Sopenharmony_ci    const char* end = &*fpPath.end();
78cb93a386Sopenharmony_ci    const char* fileName = end;
79cb93a386Sopenharmony_ci    // back up until we find a slash
80cb93a386Sopenharmony_ci    while (fileName != fpPath && '/' != *(fileName - 1) && '\\' != *(fileName - 1)) {
81cb93a386Sopenharmony_ci        --fileName;
82cb93a386Sopenharmony_ci    }
83cb93a386Sopenharmony_ci    if (!strncmp(fileName, prefix, strlen(prefix)) &&
84cb93a386Sopenharmony_ci        !strncmp(end - strlen(suffix), suffix, strlen(suffix))) {
85cb93a386Sopenharmony_ci        result.append(fileName + strlen(prefix), end - fileName - strlen(prefix) - strlen(suffix));
86cb93a386Sopenharmony_ci    }
87cb93a386Sopenharmony_ci    return result;
88cb93a386Sopenharmony_ci}
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_ci// Given a string containing an SkSL program, searches for a #pragma settings comment, like so:
91cb93a386Sopenharmony_ci//    /*#pragma settings Default Sharpen*/
92cb93a386Sopenharmony_ci// The passed-in Settings object will be updated accordingly. Any number of options can be provided.
93cb93a386Sopenharmony_cistatic bool detect_shader_settings(const SkSL::String& text,
94cb93a386Sopenharmony_ci                                   SkSL::Program::Settings* settings,
95cb93a386Sopenharmony_ci                                   const SkSL::ShaderCapsClass** caps,
96cb93a386Sopenharmony_ci                                   std::unique_ptr<SkSL::SkVMDebugInfo>* debugInfo) {
97cb93a386Sopenharmony_ci    using Factory = SkSL::ShaderCapsFactory;
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_ci    // Find a matching comment and isolate the name portion.
100cb93a386Sopenharmony_ci    static constexpr char kPragmaSettings[] = "/*#pragma settings ";
101cb93a386Sopenharmony_ci    const char* settingsPtr = strstr(text.c_str(), kPragmaSettings);
102cb93a386Sopenharmony_ci    if (settingsPtr != nullptr) {
103cb93a386Sopenharmony_ci        // Subtract one here in order to preserve the leading space, which is necessary to allow
104cb93a386Sopenharmony_ci        // consumeSuffix to find the first item.
105cb93a386Sopenharmony_ci        settingsPtr += strlen(kPragmaSettings) - 1;
106cb93a386Sopenharmony_ci
107cb93a386Sopenharmony_ci        const char* settingsEnd = strstr(settingsPtr, "*/");
108cb93a386Sopenharmony_ci        if (settingsEnd != nullptr) {
109cb93a386Sopenharmony_ci            SkSL::String settingsText{settingsPtr, size_t(settingsEnd - settingsPtr)};
110cb93a386Sopenharmony_ci
111cb93a386Sopenharmony_ci            // Apply settings as requested. Since they can come in any order, repeat until we've
112cb93a386Sopenharmony_ci            // consumed them all.
113cb93a386Sopenharmony_ci            for (;;) {
114cb93a386Sopenharmony_ci                const size_t startingLength = settingsText.length();
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" AddAndTrueToLoopCondition")) {
117cb93a386Sopenharmony_ci                    static auto s_addAndTrueCaps = Factory::AddAndTrueToLoopCondition();
118cb93a386Sopenharmony_ci                    *caps = s_addAndTrueCaps.get();
119cb93a386Sopenharmony_ci                }
120cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" CannotUseFractForNegativeValues")) {
121cb93a386Sopenharmony_ci                    static auto s_negativeFractCaps = Factory::CannotUseFractForNegativeValues();
122cb93a386Sopenharmony_ci                    *caps = s_negativeFractCaps.get();
123cb93a386Sopenharmony_ci                }
124cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" CannotUseFragCoord")) {
125cb93a386Sopenharmony_ci                    static auto s_noFragCoordCaps = Factory::CannotUseFragCoord();
126cb93a386Sopenharmony_ci                    *caps = s_noFragCoordCaps.get();
127cb93a386Sopenharmony_ci                }
128cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" CannotUseMinAndAbsTogether")) {
129cb93a386Sopenharmony_ci                    static auto s_minAbsCaps = Factory::CannotUseMinAndAbsTogether();
130cb93a386Sopenharmony_ci                    *caps = s_minAbsCaps.get();
131cb93a386Sopenharmony_ci                }
132cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" Default")) {
133cb93a386Sopenharmony_ci                    static auto s_defaultCaps = Factory::Default();
134cb93a386Sopenharmony_ci                    *caps = s_defaultCaps.get();
135cb93a386Sopenharmony_ci                }
136cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" EmulateAbsIntFunction")) {
137cb93a386Sopenharmony_ci                    static auto s_emulateAbsIntCaps = Factory::EmulateAbsIntFunction();
138cb93a386Sopenharmony_ci                    *caps = s_emulateAbsIntCaps.get();
139cb93a386Sopenharmony_ci                }
140cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" FramebufferFetchSupport")) {
141cb93a386Sopenharmony_ci                    static auto s_fbFetchSupport = Factory::FramebufferFetchSupport();
142cb93a386Sopenharmony_ci                    *caps = s_fbFetchSupport.get();
143cb93a386Sopenharmony_ci                }
144cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" IncompleteShortIntPrecision")) {
145cb93a386Sopenharmony_ci                    static auto s_incompleteShortIntCaps = Factory::IncompleteShortIntPrecision();
146cb93a386Sopenharmony_ci                    *caps = s_incompleteShortIntCaps.get();
147cb93a386Sopenharmony_ci                }
148cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" MustGuardDivisionEvenAfterExplicitZeroCheck")) {
149cb93a386Sopenharmony_ci                    static auto s_div0Caps = Factory::MustGuardDivisionEvenAfterExplicitZeroCheck();
150cb93a386Sopenharmony_ci                    *caps = s_div0Caps.get();
151cb93a386Sopenharmony_ci                }
152cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" MustForceNegatedAtanParamToFloat")) {
153cb93a386Sopenharmony_ci                    static auto s_negativeAtanCaps = Factory::MustForceNegatedAtanParamToFloat();
154cb93a386Sopenharmony_ci                    *caps = s_negativeAtanCaps.get();
155cb93a386Sopenharmony_ci                }
156cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" MustForceNegatedLdexpParamToMultiply")) {
157cb93a386Sopenharmony_ci                    static auto s_negativeLdexpCaps =
158cb93a386Sopenharmony_ci                            Factory::MustForceNegatedLdexpParamToMultiply();
159cb93a386Sopenharmony_ci                    *caps = s_negativeLdexpCaps.get();
160cb93a386Sopenharmony_ci                }
161cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" RemovePowWithConstantExponent")) {
162cb93a386Sopenharmony_ci                    static auto s_powCaps = Factory::RemovePowWithConstantExponent();
163cb93a386Sopenharmony_ci                    *caps = s_powCaps.get();
164cb93a386Sopenharmony_ci                }
165cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" RewriteDoWhileLoops")) {
166cb93a386Sopenharmony_ci                    static auto s_rewriteLoopCaps = Factory::RewriteDoWhileLoops();
167cb93a386Sopenharmony_ci                    *caps = s_rewriteLoopCaps.get();
168cb93a386Sopenharmony_ci                }
169cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" RewriteSwitchStatements")) {
170cb93a386Sopenharmony_ci                    static auto s_rewriteSwitchCaps = Factory::RewriteSwitchStatements();
171cb93a386Sopenharmony_ci                    *caps = s_rewriteSwitchCaps.get();
172cb93a386Sopenharmony_ci                }
173cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" RewriteMatrixVectorMultiply")) {
174cb93a386Sopenharmony_ci                    static auto s_rewriteMatVecMulCaps = Factory::RewriteMatrixVectorMultiply();
175cb93a386Sopenharmony_ci                    *caps = s_rewriteMatVecMulCaps.get();
176cb93a386Sopenharmony_ci                }
177cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" RewriteMatrixComparisons")) {
178cb93a386Sopenharmony_ci                    static auto s_rewriteMatrixComparisons = Factory::RewriteMatrixComparisons();
179cb93a386Sopenharmony_ci                    *caps = s_rewriteMatrixComparisons.get();
180cb93a386Sopenharmony_ci                }
181cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" ShaderDerivativeExtensionString")) {
182cb93a386Sopenharmony_ci                    static auto s_derivativeCaps = Factory::ShaderDerivativeExtensionString();
183cb93a386Sopenharmony_ci                    *caps = s_derivativeCaps.get();
184cb93a386Sopenharmony_ci                }
185cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" UnfoldShortCircuitAsTernary")) {
186cb93a386Sopenharmony_ci                    static auto s_ternaryCaps = Factory::UnfoldShortCircuitAsTernary();
187cb93a386Sopenharmony_ci                    *caps = s_ternaryCaps.get();
188cb93a386Sopenharmony_ci                }
189cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" UsesPrecisionModifiers")) {
190cb93a386Sopenharmony_ci                    static auto s_precisionCaps = Factory::UsesPrecisionModifiers();
191cb93a386Sopenharmony_ci                    *caps = s_precisionCaps.get();
192cb93a386Sopenharmony_ci                }
193cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" Version110")) {
194cb93a386Sopenharmony_ci                    static auto s_version110Caps = Factory::Version110();
195cb93a386Sopenharmony_ci                    *caps = s_version110Caps.get();
196cb93a386Sopenharmony_ci                }
197cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" Version450Core")) {
198cb93a386Sopenharmony_ci                    static auto s_version450CoreCaps = Factory::Version450Core();
199cb93a386Sopenharmony_ci                    *caps = s_version450CoreCaps.get();
200cb93a386Sopenharmony_ci                }
201cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" AllowNarrowingConversions")) {
202cb93a386Sopenharmony_ci                    settings->fAllowNarrowingConversions = true;
203cb93a386Sopenharmony_ci                }
204cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" ForceHighPrecision")) {
205cb93a386Sopenharmony_ci                    settings->fForceHighPrecision = true;
206cb93a386Sopenharmony_ci                }
207cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" NoES2Restrictions")) {
208cb93a386Sopenharmony_ci                    settings->fEnforceES2Restrictions = false;
209cb93a386Sopenharmony_ci                }
210cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" NoInline")) {
211cb93a386Sopenharmony_ci                    settings->fInlineThreshold = 0;
212cb93a386Sopenharmony_ci                }
213cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" InlineThresholdMax")) {
214cb93a386Sopenharmony_ci                    settings->fInlineThreshold = INT_MAX;
215cb93a386Sopenharmony_ci                }
216cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" Sharpen")) {
217cb93a386Sopenharmony_ci                    settings->fSharpenTextures = true;
218cb93a386Sopenharmony_ci                }
219cb93a386Sopenharmony_ci                if (settingsText.consumeSuffix(" SkVMDebugTrace")) {
220cb93a386Sopenharmony_ci                    settings->fOptimize = false;
221cb93a386Sopenharmony_ci                    *debugInfo = std::make_unique<SkSL::SkVMDebugInfo>();
222cb93a386Sopenharmony_ci                }
223cb93a386Sopenharmony_ci
224cb93a386Sopenharmony_ci                if (settingsText.empty()) {
225cb93a386Sopenharmony_ci                    break;
226cb93a386Sopenharmony_ci                }
227cb93a386Sopenharmony_ci                if (settingsText.length() == startingLength) {
228cb93a386Sopenharmony_ci                    printf("Unrecognized #pragma settings: %s\n", settingsText.c_str());
229cb93a386Sopenharmony_ci                    return false;
230cb93a386Sopenharmony_ci                }
231cb93a386Sopenharmony_ci            }
232cb93a386Sopenharmony_ci        }
233cb93a386Sopenharmony_ci    }
234cb93a386Sopenharmony_ci
235cb93a386Sopenharmony_ci    return true;
236cb93a386Sopenharmony_ci}
237cb93a386Sopenharmony_ci
238cb93a386Sopenharmony_ci/**
239cb93a386Sopenharmony_ci * Displays a usage banner; used when the command line arguments don't make sense.
240cb93a386Sopenharmony_ci */
241cb93a386Sopenharmony_cistatic void show_usage() {
242cb93a386Sopenharmony_ci    printf("usage: skslc <input> <output> <flags>\n"
243cb93a386Sopenharmony_ci           "       skslc <worklist>\n"
244cb93a386Sopenharmony_ci           "\n"
245cb93a386Sopenharmony_ci           "Allowed flags:\n"
246cb93a386Sopenharmony_ci           "--settings:   honor embedded /*#pragma settings*/ comments.\n"
247cb93a386Sopenharmony_ci           "--nosettings: ignore /*#pragma settings*/ comments\n");
248cb93a386Sopenharmony_ci}
249cb93a386Sopenharmony_ci
250cb93a386Sopenharmony_ci/**
251cb93a386Sopenharmony_ci * Handle a single input.
252cb93a386Sopenharmony_ci */
253cb93a386Sopenharmony_ciResultCode processCommand(std::vector<SkSL::String>& args) {
254cb93a386Sopenharmony_ci    bool honorSettings = true;
255cb93a386Sopenharmony_ci    if (args.size() == 4) {
256cb93a386Sopenharmony_ci        // Handle four-argument case: `skslc in.sksl out.glsl --settings`
257cb93a386Sopenharmony_ci        const SkSL::String& settingsArg = args[3];
258cb93a386Sopenharmony_ci        if (settingsArg == "--settings") {
259cb93a386Sopenharmony_ci            honorSettings = true;
260cb93a386Sopenharmony_ci        } else if (settingsArg == "--nosettings") {
261cb93a386Sopenharmony_ci            honorSettings = false;
262cb93a386Sopenharmony_ci        } else {
263cb93a386Sopenharmony_ci            printf("unrecognized flag: %s\n\n", settingsArg.c_str());
264cb93a386Sopenharmony_ci            show_usage();
265cb93a386Sopenharmony_ci            return ResultCode::kInputError;
266cb93a386Sopenharmony_ci        }
267cb93a386Sopenharmony_ci    } else if (args.size() != 3) {
268cb93a386Sopenharmony_ci        show_usage();
269cb93a386Sopenharmony_ci        return ResultCode::kInputError;
270cb93a386Sopenharmony_ci    }
271cb93a386Sopenharmony_ci
272cb93a386Sopenharmony_ci    SkSL::ProgramKind kind;
273cb93a386Sopenharmony_ci    const SkSL::String& inputPath = args[1];
274cb93a386Sopenharmony_ci    if (inputPath.ends_with(".vert")) {
275cb93a386Sopenharmony_ci        kind = SkSL::ProgramKind::kVertex;
276cb93a386Sopenharmony_ci    } else if (inputPath.ends_with(".frag") || inputPath.ends_with(".sksl")) {
277cb93a386Sopenharmony_ci        kind = SkSL::ProgramKind::kFragment;
278cb93a386Sopenharmony_ci    } else if (inputPath.ends_with(".rtb")) {
279cb93a386Sopenharmony_ci        kind = SkSL::ProgramKind::kRuntimeBlender;
280cb93a386Sopenharmony_ci    } else if (inputPath.ends_with(".rtcf")) {
281cb93a386Sopenharmony_ci        kind = SkSL::ProgramKind::kRuntimeColorFilter;
282cb93a386Sopenharmony_ci    } else if (inputPath.ends_with(".rts")) {
283cb93a386Sopenharmony_ci        kind = SkSL::ProgramKind::kRuntimeShader;
284cb93a386Sopenharmony_ci    } else {
285cb93a386Sopenharmony_ci        printf("input filename must end in '.vert', '.frag', '.rtb', '.rtcf', "
286cb93a386Sopenharmony_ci               "'.rts', or '.sksl'\n");
287cb93a386Sopenharmony_ci        return ResultCode::kInputError;
288cb93a386Sopenharmony_ci    }
289cb93a386Sopenharmony_ci
290cb93a386Sopenharmony_ci    std::ifstream in(inputPath);
291cb93a386Sopenharmony_ci    SkSL::String text((std::istreambuf_iterator<char>(in)),
292cb93a386Sopenharmony_ci                       std::istreambuf_iterator<char>());
293cb93a386Sopenharmony_ci    if (in.rdstate()) {
294cb93a386Sopenharmony_ci        printf("error reading '%s'\n", inputPath.c_str());
295cb93a386Sopenharmony_ci        return ResultCode::kInputError;
296cb93a386Sopenharmony_ci    }
297cb93a386Sopenharmony_ci
298cb93a386Sopenharmony_ci    SkSL::Program::Settings settings;
299cb93a386Sopenharmony_ci    SkSL::StandaloneShaderCaps standaloneCaps;
300cb93a386Sopenharmony_ci    const SkSL::ShaderCapsClass* caps = &standaloneCaps;
301cb93a386Sopenharmony_ci    std::unique_ptr<SkSL::SkVMDebugInfo> debugInfo;
302cb93a386Sopenharmony_ci    if (honorSettings) {
303cb93a386Sopenharmony_ci        if (!detect_shader_settings(text, &settings, &caps, &debugInfo)) {
304cb93a386Sopenharmony_ci            return ResultCode::kInputError;
305cb93a386Sopenharmony_ci        }
306cb93a386Sopenharmony_ci    }
307cb93a386Sopenharmony_ci
308cb93a386Sopenharmony_ci    // This tells the compiler where the rt-flip uniform will live should it be required. For
309cb93a386Sopenharmony_ci    // testing purposes we don't care where that is, but the compiler will report an error if we
310cb93a386Sopenharmony_ci    // leave them at their default invalid values, or if the offset overlaps another uniform.
311cb93a386Sopenharmony_ci    settings.fRTFlipOffset  = 16384;
312cb93a386Sopenharmony_ci    settings.fRTFlipSet     = 0;
313cb93a386Sopenharmony_ci    settings.fRTFlipBinding = 0;
314cb93a386Sopenharmony_ci
315cb93a386Sopenharmony_ci    const SkSL::String& outputPath = args[2];
316cb93a386Sopenharmony_ci    auto emitCompileError = [&](SkSL::FileOutputStream& out, const char* errorText) {
317cb93a386Sopenharmony_ci        // Overwrite the compiler output, if any, with an error message.
318cb93a386Sopenharmony_ci        out.close();
319cb93a386Sopenharmony_ci        SkSL::FileOutputStream errorStream(outputPath);
320cb93a386Sopenharmony_ci        errorStream.writeText("### Compilation failed:\n\n");
321cb93a386Sopenharmony_ci        errorStream.writeText(errorText);
322cb93a386Sopenharmony_ci        errorStream.close();
323cb93a386Sopenharmony_ci        // Also emit the error directly to stdout.
324cb93a386Sopenharmony_ci        puts(errorText);
325cb93a386Sopenharmony_ci    };
326cb93a386Sopenharmony_ci
327cb93a386Sopenharmony_ci    auto compileProgram = [&](const auto& writeFn) -> ResultCode {
328cb93a386Sopenharmony_ci        SkSL::FileOutputStream out(outputPath);
329cb93a386Sopenharmony_ci        SkSL::Compiler compiler(caps);
330cb93a386Sopenharmony_ci        if (!out.isValid()) {
331cb93a386Sopenharmony_ci            printf("error writing '%s'\n", outputPath.c_str());
332cb93a386Sopenharmony_ci            return ResultCode::kOutputError;
333cb93a386Sopenharmony_ci        }
334cb93a386Sopenharmony_ci        std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, text, settings);
335cb93a386Sopenharmony_ci        if (!program || !writeFn(compiler, *program, out)) {
336cb93a386Sopenharmony_ci            emitCompileError(out, compiler.errorText().c_str());
337cb93a386Sopenharmony_ci            return ResultCode::kCompileError;
338cb93a386Sopenharmony_ci        }
339cb93a386Sopenharmony_ci        if (!out.close()) {
340cb93a386Sopenharmony_ci            printf("error writing '%s'\n", outputPath.c_str());
341cb93a386Sopenharmony_ci            return ResultCode::kOutputError;
342cb93a386Sopenharmony_ci        }
343cb93a386Sopenharmony_ci        return ResultCode::kSuccess;
344cb93a386Sopenharmony_ci    };
345cb93a386Sopenharmony_ci
346cb93a386Sopenharmony_ci    if (outputPath.ends_with(".spirv")) {
347cb93a386Sopenharmony_ci        return compileProgram(
348cb93a386Sopenharmony_ci                [](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
349cb93a386Sopenharmony_ci                    return compiler.toSPIRV(program, out);
350cb93a386Sopenharmony_ci                });
351cb93a386Sopenharmony_ci    } else if (outputPath.ends_with(".asm.frag") || outputPath.ends_with(".asm.vert")) {
352cb93a386Sopenharmony_ci        return compileProgram(
353cb93a386Sopenharmony_ci                [](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
354cb93a386Sopenharmony_ci                    // Compile program to SPIR-V assembly in a string-stream.
355cb93a386Sopenharmony_ci                    SkSL::StringStream assembly;
356cb93a386Sopenharmony_ci                    if (!compiler.toSPIRV(program, assembly)) {
357cb93a386Sopenharmony_ci                        return false;
358cb93a386Sopenharmony_ci                    }
359cb93a386Sopenharmony_ci                    // Convert the string-stream to a SPIR-V disassembly.
360cb93a386Sopenharmony_ci                    spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
361cb93a386Sopenharmony_ci                    const SkSL::String& spirv(assembly.str());
362cb93a386Sopenharmony_ci                    std::string disassembly;
363cb93a386Sopenharmony_ci                    if (!tools.Disassemble((const uint32_t*)spirv.data(),
364cb93a386Sopenharmony_ci                                           spirv.size() / 4, &disassembly)) {
365cb93a386Sopenharmony_ci                        return false;
366cb93a386Sopenharmony_ci                    }
367cb93a386Sopenharmony_ci                    // Finally, write the disassembly to our output stream.
368cb93a386Sopenharmony_ci                    out.write(disassembly.data(), disassembly.size());
369cb93a386Sopenharmony_ci                    return true;
370cb93a386Sopenharmony_ci                });
371cb93a386Sopenharmony_ci    } else if (outputPath.ends_with(".glsl")) {
372cb93a386Sopenharmony_ci        return compileProgram(
373cb93a386Sopenharmony_ci                [](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
374cb93a386Sopenharmony_ci                    return compiler.toGLSL(program, out);
375cb93a386Sopenharmony_ci                });
376cb93a386Sopenharmony_ci    } else if (outputPath.ends_with(".metal")) {
377cb93a386Sopenharmony_ci        return compileProgram(
378cb93a386Sopenharmony_ci                [](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
379cb93a386Sopenharmony_ci                    return compiler.toMetal(program, out);
380cb93a386Sopenharmony_ci                });
381cb93a386Sopenharmony_ci    } else if (outputPath.ends_with(".skvm")) {
382cb93a386Sopenharmony_ci        return compileProgram(
383cb93a386Sopenharmony_ci                [&](SkSL::Compiler&, SkSL::Program& program, SkSL::OutputStream& out) {
384cb93a386Sopenharmony_ci                    skvm::Builder builder{skvm::Features{}};
385cb93a386Sopenharmony_ci                    if (!SkSL::testingOnly_ProgramToSkVMShader(program, &builder,
386cb93a386Sopenharmony_ci                                                               debugInfo.get())) {
387cb93a386Sopenharmony_ci                        return false;
388cb93a386Sopenharmony_ci                    }
389cb93a386Sopenharmony_ci
390cb93a386Sopenharmony_ci                    std::unique_ptr<SkWStream> redirect = as_SkWStream(out);
391cb93a386Sopenharmony_ci                    if (debugInfo) {
392cb93a386Sopenharmony_ci                        debugInfo->dump(redirect.get());
393cb93a386Sopenharmony_ci                    }
394cb93a386Sopenharmony_ci                    builder.done().dump(redirect.get());
395cb93a386Sopenharmony_ci                    return true;
396cb93a386Sopenharmony_ci                });
397cb93a386Sopenharmony_ci    } else if (outputPath.ends_with(".stage")) {
398cb93a386Sopenharmony_ci        return compileProgram(
399cb93a386Sopenharmony_ci                [](SkSL::Compiler&, SkSL::Program& program, SkSL::OutputStream& out) {
400cb93a386Sopenharmony_ci                    class Callbacks : public SkSL::PipelineStage::Callbacks {
401cb93a386Sopenharmony_ci                    public:
402cb93a386Sopenharmony_ci                        using String = SkSL::String;
403cb93a386Sopenharmony_ci
404cb93a386Sopenharmony_ci                        String getMangledName(const char* name) override {
405cb93a386Sopenharmony_ci                            return String(name) + "_0";
406cb93a386Sopenharmony_ci                        }
407cb93a386Sopenharmony_ci
408cb93a386Sopenharmony_ci                        String declareUniform(const SkSL::VarDeclaration* decl) override {
409cb93a386Sopenharmony_ci                            fOutput += decl->description();
410cb93a386Sopenharmony_ci                            return String(decl->var().name());
411cb93a386Sopenharmony_ci                        }
412cb93a386Sopenharmony_ci
413cb93a386Sopenharmony_ci                        void defineFunction(const char* decl,
414cb93a386Sopenharmony_ci                                            const char* body,
415cb93a386Sopenharmony_ci                                            bool /*isMain*/) override {
416cb93a386Sopenharmony_ci                            fOutput += String(decl) + "{" + body + "}";
417cb93a386Sopenharmony_ci                        }
418cb93a386Sopenharmony_ci
419cb93a386Sopenharmony_ci                        void declareFunction(const char* decl) override {
420cb93a386Sopenharmony_ci                            fOutput += String(decl) + ";";
421cb93a386Sopenharmony_ci                        }
422cb93a386Sopenharmony_ci
423cb93a386Sopenharmony_ci                        void defineStruct(const char* definition) override {
424cb93a386Sopenharmony_ci                            fOutput += definition;
425cb93a386Sopenharmony_ci                        }
426cb93a386Sopenharmony_ci
427cb93a386Sopenharmony_ci                        void declareGlobal(const char* declaration) override {
428cb93a386Sopenharmony_ci                            fOutput += declaration;
429cb93a386Sopenharmony_ci                        }
430cb93a386Sopenharmony_ci
431cb93a386Sopenharmony_ci                        String sampleShader(int index, String coords) override {
432cb93a386Sopenharmony_ci                            return "child_" + SkSL::to_string(index) + ".eval(" + coords + ")";
433cb93a386Sopenharmony_ci                        }
434cb93a386Sopenharmony_ci
435cb93a386Sopenharmony_ci                        String sampleColorFilter(int index, String color) override {
436cb93a386Sopenharmony_ci                            return "child_" + SkSL::to_string(index) + ".eval(" + color + ")";
437cb93a386Sopenharmony_ci                        }
438cb93a386Sopenharmony_ci
439cb93a386Sopenharmony_ci                        String sampleBlender(int index, String src, String dst) override {
440cb93a386Sopenharmony_ci                            return "child_" + SkSL::to_string(index) + ".eval(" + src + ", " +
441cb93a386Sopenharmony_ci                                   dst + ")";
442cb93a386Sopenharmony_ci                        }
443cb93a386Sopenharmony_ci
444cb93a386Sopenharmony_ci                        String fOutput;
445cb93a386Sopenharmony_ci                    };
446cb93a386Sopenharmony_ci                    // The .stage output looks almost like valid SkSL, but not quite.
447cb93a386Sopenharmony_ci                    // The PipelineStageGenerator bridges the gap between the SkSL in `program`,
448cb93a386Sopenharmony_ci                    // and the C++ FP builder API (see GrSkSLFP). In that API, children don't need
449cb93a386Sopenharmony_ci                    // to be declared (so they don't emit declarations here). Children are sampled
450cb93a386Sopenharmony_ci                    // by index, not name - so all children here are just "child_N".
451cb93a386Sopenharmony_ci                    // The input color and coords have names in the original SkSL (as parameters to
452cb93a386Sopenharmony_ci                    // main), but those are ignored here. References to those variables become
453cb93a386Sopenharmony_ci                    // "_coords" and "_inColor". At runtime, those variable names are irrelevant
454cb93a386Sopenharmony_ci                    // when the new SkSL is emitted inside the FP - references to those variables
455cb93a386Sopenharmony_ci                    // are replaced with strings from EmitArgs, and might be varyings or differently
456cb93a386Sopenharmony_ci                    // named parameters.
457cb93a386Sopenharmony_ci                    Callbacks callbacks;
458cb93a386Sopenharmony_ci                    SkSL::PipelineStage::ConvertProgram(program, "_coords", "_inColor",
459cb93a386Sopenharmony_ci                                                        "_canvasColor", &callbacks);
460cb93a386Sopenharmony_ci                    out.writeString(GrShaderUtils::PrettyPrint(callbacks.fOutput));
461cb93a386Sopenharmony_ci                    return true;
462cb93a386Sopenharmony_ci                });
463cb93a386Sopenharmony_ci    } else if (outputPath.ends_with(".dehydrated.sksl")) {
464cb93a386Sopenharmony_ci        SkSL::FileOutputStream out(outputPath);
465cb93a386Sopenharmony_ci        SkSL::Compiler compiler(caps);
466cb93a386Sopenharmony_ci        if (!out.isValid()) {
467cb93a386Sopenharmony_ci            printf("error writing '%s'\n", outputPath.c_str());
468cb93a386Sopenharmony_ci            return ResultCode::kOutputError;
469cb93a386Sopenharmony_ci        }
470cb93a386Sopenharmony_ci        SkSL::LoadedModule module =
471cb93a386Sopenharmony_ci                compiler.loadModule(kind, SkSL::Compiler::MakeModulePath(inputPath.c_str()),
472cb93a386Sopenharmony_ci                                    /*base=*/nullptr, /*dehydrate=*/true);
473cb93a386Sopenharmony_ci        SkSL::Dehydrator dehydrator;
474cb93a386Sopenharmony_ci        dehydrator.write(*module.fSymbols);
475cb93a386Sopenharmony_ci        dehydrator.write(module.fElements);
476cb93a386Sopenharmony_ci        SkSL::String baseName = base_name(inputPath, "", ".sksl");
477cb93a386Sopenharmony_ci        SkSL::StringStream buffer;
478cb93a386Sopenharmony_ci        dehydrator.finish(buffer);
479cb93a386Sopenharmony_ci        const SkSL::String& data = buffer.str();
480cb93a386Sopenharmony_ci        out.printf("static uint8_t SKSL_INCLUDE_%s[] = {", baseName.c_str());
481cb93a386Sopenharmony_ci        for (size_t i = 0; i < data.length(); ++i) {
482cb93a386Sopenharmony_ci            out.printf("%s%d,", dehydrator.prefixAtOffset(i), uint8_t(data[i]));
483cb93a386Sopenharmony_ci        }
484cb93a386Sopenharmony_ci        out.printf("};\n");
485cb93a386Sopenharmony_ci        out.printf("static constexpr size_t SKSL_INCLUDE_%s_LENGTH = sizeof(SKSL_INCLUDE_%s);\n",
486cb93a386Sopenharmony_ci                   baseName.c_str(), baseName.c_str());
487cb93a386Sopenharmony_ci        if (!out.close()) {
488cb93a386Sopenharmony_ci            printf("error writing '%s'\n", outputPath.c_str());
489cb93a386Sopenharmony_ci            return ResultCode::kOutputError;
490cb93a386Sopenharmony_ci        }
491cb93a386Sopenharmony_ci    } else {
492cb93a386Sopenharmony_ci        printf("expected output path to end with one of: .glsl, .metal, .spirv, .asm.frag, .skvm, "
493cb93a386Sopenharmony_ci               ".stage, .asm.vert (got '%s')\n", outputPath.c_str());
494cb93a386Sopenharmony_ci        return ResultCode::kConfigurationError;
495cb93a386Sopenharmony_ci    }
496cb93a386Sopenharmony_ci    return ResultCode::kSuccess;
497cb93a386Sopenharmony_ci}
498cb93a386Sopenharmony_ci
499cb93a386Sopenharmony_ci/**
500cb93a386Sopenharmony_ci * Processes multiple inputs in a single invocation of skslc.
501cb93a386Sopenharmony_ci */
502cb93a386Sopenharmony_ciResultCode processWorklist(const char* worklistPath) {
503cb93a386Sopenharmony_ci    SkSL::String inputPath(worklistPath);
504cb93a386Sopenharmony_ci    if (!inputPath.ends_with(".worklist")) {
505cb93a386Sopenharmony_ci        printf("expected .worklist file, found: %s\n\n", worklistPath);
506cb93a386Sopenharmony_ci        show_usage();
507cb93a386Sopenharmony_ci        return ResultCode::kConfigurationError;
508cb93a386Sopenharmony_ci    }
509cb93a386Sopenharmony_ci
510cb93a386Sopenharmony_ci    // The worklist contains one line per argument to pass to skslc. When a blank line is reached,
511cb93a386Sopenharmony_ci    // those arguments will be passed to `processCommand`.
512cb93a386Sopenharmony_ci    auto resultCode = ResultCode::kSuccess;
513cb93a386Sopenharmony_ci    std::vector<SkSL::String> args = {"skslc"};
514cb93a386Sopenharmony_ci    std::ifstream in(worklistPath);
515cb93a386Sopenharmony_ci    for (SkSL::String line; std::getline(in, line); ) {
516cb93a386Sopenharmony_ci        if (in.rdstate()) {
517cb93a386Sopenharmony_ci            printf("error reading '%s'\n", worklistPath);
518cb93a386Sopenharmony_ci            return ResultCode::kInputError;
519cb93a386Sopenharmony_ci        }
520cb93a386Sopenharmony_ci
521cb93a386Sopenharmony_ci        if (!line.empty()) {
522cb93a386Sopenharmony_ci            // We found an argument. Remember it.
523cb93a386Sopenharmony_ci            args.push_back(std::move(line));
524cb93a386Sopenharmony_ci        } else {
525cb93a386Sopenharmony_ci            // We found a blank line. If we have any arguments stored up, process them as a command.
526cb93a386Sopenharmony_ci            if (!args.empty()) {
527cb93a386Sopenharmony_ci                ResultCode outcome = processCommand(args);
528cb93a386Sopenharmony_ci                resultCode = std::max(resultCode, outcome);
529cb93a386Sopenharmony_ci
530cb93a386Sopenharmony_ci                // Clear every argument except the first ("skslc").
531cb93a386Sopenharmony_ci                args.resize(1);
532cb93a386Sopenharmony_ci            }
533cb93a386Sopenharmony_ci        }
534cb93a386Sopenharmony_ci    }
535cb93a386Sopenharmony_ci
536cb93a386Sopenharmony_ci    // If the worklist ended with a list of arguments but no blank line, process those now.
537cb93a386Sopenharmony_ci    if (args.size() > 1) {
538cb93a386Sopenharmony_ci        ResultCode outcome = processCommand(args);
539cb93a386Sopenharmony_ci        resultCode = std::max(resultCode, outcome);
540cb93a386Sopenharmony_ci    }
541cb93a386Sopenharmony_ci
542cb93a386Sopenharmony_ci    // Return the "worst" status we encountered. For our purposes, compilation errors are the least
543cb93a386Sopenharmony_ci    // serious, because they are expected to occur in unit tests. Other types of errors are not
544cb93a386Sopenharmony_ci    // expected at all during a build.
545cb93a386Sopenharmony_ci    return resultCode;
546cb93a386Sopenharmony_ci}
547cb93a386Sopenharmony_ci
548cb93a386Sopenharmony_ciint main(int argc, const char** argv) {
549cb93a386Sopenharmony_ci    if (argc == 2) {
550cb93a386Sopenharmony_ci        // Worklists are the only two-argument case for skslc, and we don't intend to support
551cb93a386Sopenharmony_ci        // nested worklists, so we can process them here.
552cb93a386Sopenharmony_ci        return (int)processWorklist(argv[1]);
553cb93a386Sopenharmony_ci    } else {
554cb93a386Sopenharmony_ci        // Process non-worklist inputs.
555cb93a386Sopenharmony_ci        std::vector<SkSL::String> args;
556cb93a386Sopenharmony_ci        for (int index=0; index<argc; ++index) {
557cb93a386Sopenharmony_ci            args.push_back(argv[index]);
558cb93a386Sopenharmony_ci        }
559cb93a386Sopenharmony_ci
560cb93a386Sopenharmony_ci        return (int)processCommand(args);
561cb93a386Sopenharmony_ci    }
562cb93a386Sopenharmony_ci}
563