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