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