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