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 "src/sksl/SkSLCompiler.h"
9
10 #include "include/private/SkSLDefines.h"
11 #include "include/private/SkSLIRNode.h"
12 #include "include/private/SkSLProgramKind.h"
13 #include "include/private/SkSLSymbol.h"
14 #include "include/private/base/SkDebug.h"
15 #include "include/sksl/DSLCore.h"
16 #include "include/sksl/DSLModifiers.h"
17 #include "include/sksl/DSLType.h"
18 #include "src/core/SkTraceEvent.h"
19 #include "src/sksl/SkSLAnalysis.h"
20 #include "src/sksl/SkSLContext.h"
21 #include "src/sksl/SkSLInliner.h"
22 #include "src/sksl/SkSLModuleLoader.h"
23 #include "src/sksl/SkSLOutputStream.h"
24 #include "src/sksl/SkSLParser.h"
25 #include "src/sksl/SkSLProgramSettings.h"
26 #include "src/sksl/SkSLStringStream.h"
27 #include "src/sksl/analysis/SkSLProgramUsage.h"
28 #include "src/sksl/ir/SkSLExpression.h"
29 #include "src/sksl/ir/SkSLField.h"
30 #include "src/sksl/ir/SkSLFieldAccess.h"
31 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
32 #include "src/sksl/ir/SkSLFunctionReference.h"
33 #include "src/sksl/ir/SkSLProgram.h"
34 #include "src/sksl/ir/SkSLSymbolTable.h" // IWYU pragma: keep
35 #include "src/sksl/ir/SkSLTypeReference.h"
36 #include "src/sksl/ir/SkSLVariable.h"
37 #include "src/sksl/ir/SkSLVariableReference.h"
38 #include "src/sksl/transform/SkSLTransform.h"
39
40 #include <atomic>
41 #include <cstdint>
42 #include <memory>
43 #include <utility>
44
45 #if defined(SKSL_STANDALONE)
46 #include <fstream>
47 #endif
48
49 #if defined(SKSL_STANDALONE) || defined(SK_GANESH) || defined(SK_GRAPHITE)
50 #include "src/sksl/codegen/SkSLGLSLCodeGenerator.h"
51 #include "src/sksl/codegen/SkSLMetalCodeGenerator.h"
52 #include "src/sksl/codegen/SkSLSPIRVCodeGenerator.h"
53 #include "src/sksl/codegen/SkSLSPIRVtoHLSL.h"
54 #include "src/sksl/codegen/SkSLWGSLCodeGenerator.h"
55 #endif
56
57 #ifdef SK_ENABLE_SPIRV_VALIDATION
58 #include "spirv-tools/libspirv.hpp"
59 #endif
60
61 #ifdef SK_ENABLE_WGSL_VALIDATION
62 #include "tint/tint.h"
63 #endif
64
65 namespace SkSL {
66
67 class ModifiersPool;
68
69 // These flags allow tools like Viewer or Nanobench to override the compiler's ProgramSettings.
70 Compiler::OverrideFlag Compiler::sOptimizer = OverrideFlag::kDefault;
71 Compiler::OverrideFlag Compiler::sInliner = OverrideFlag::kDefault;
72
73 using RefKind = VariableReference::RefKind;
74
75 class AutoSource {
76 public:
AutoSource(Compiler * compiler,std::string_view source)77 AutoSource(Compiler* compiler, std::string_view source)
78 : fCompiler(compiler) {
79 SkASSERT(!fCompiler->errorReporter().source().data());
80 fCompiler->errorReporter().setSource(source);
81 }
82
~AutoSource()83 ~AutoSource() {
84 fCompiler->errorReporter().setSource(std::string_view());
85 }
86
87 Compiler* fCompiler;
88 };
89
90 class AutoProgramConfig {
91 public:
AutoProgramConfig(Context & context,ProgramConfig * config)92 AutoProgramConfig(Context& context, ProgramConfig* config)
93 : fContext(context)
94 , fOldConfig(context.fConfig) {
95 fContext.fConfig = config;
96 }
97
~AutoProgramConfig()98 ~AutoProgramConfig() {
99 fContext.fConfig = fOldConfig;
100 }
101
102 Context& fContext;
103 ProgramConfig* fOldConfig;
104 };
105
106 class AutoShaderCaps {
107 public:
AutoShaderCaps(std::shared_ptr<Context> & context,const ShaderCaps * caps)108 AutoShaderCaps(std::shared_ptr<Context>& context, const ShaderCaps* caps)
109 : fContext(context.get())
110 , fOldCaps(fContext->fCaps) {
111 fContext->fCaps = caps;
112 }
113
~AutoShaderCaps()114 ~AutoShaderCaps() {
115 fContext->fCaps = fOldCaps;
116 }
117
118 Context* fContext;
119 const ShaderCaps* fOldCaps;
120 };
121
122 class AutoModifiersPool {
123 public:
AutoModifiersPool(std::shared_ptr<Context> & context,ModifiersPool * modifiersPool)124 AutoModifiersPool(std::shared_ptr<Context>& context, ModifiersPool* modifiersPool)
125 : fContext(context.get()) {
126 SkASSERT(!fContext->fModifiersPool);
127 fContext->fModifiersPool = modifiersPool;
128 }
129
~AutoModifiersPool()130 ~AutoModifiersPool() {
131 fContext->fModifiersPool = nullptr;
132 }
133
134 Context* fContext;
135 };
136
Compiler(const ShaderCaps * caps)137 Compiler::Compiler(const ShaderCaps* caps) : fErrorReporter(this), fCaps(caps) {
138 SkASSERT(caps);
139
140 auto moduleLoader = ModuleLoader::Get();
141 fContext = std::make_shared<Context>(moduleLoader.builtinTypes(), /*caps=*/nullptr,
142 fErrorReporter);
143 }
144
~Compiler()145 Compiler::~Compiler() {}
146
moduleForProgramKind(ProgramKind kind)147 const Module* Compiler::moduleForProgramKind(ProgramKind kind) {
148 auto m = ModuleLoader::Get();
149 switch (kind) {
150 case ProgramKind::kVertex: return m.loadVertexModule(this);
151 case ProgramKind::kFragment: return m.loadFragmentModule(this);
152 case ProgramKind::kCompute: return m.loadComputeModule(this);
153 case ProgramKind::kGraphiteVertex: return m.loadGraphiteVertexModule(this);
154 case ProgramKind::kGraphiteFragment: return m.loadGraphiteFragmentModule(this);
155 case ProgramKind::kPrivateRuntimeShader: return m.loadPrivateRTShaderModule(this);
156 case ProgramKind::kRuntimeColorFilter:
157 case ProgramKind::kRuntimeShader:
158 case ProgramKind::kRuntimeBlender:
159 case ProgramKind::kPrivateRuntimeColorFilter:
160 case ProgramKind::kPrivateRuntimeBlender:
161 case ProgramKind::kMeshVertex:
162 case ProgramKind::kMeshFragment:
163 case ProgramKind::kGeneric: return m.loadPublicModule(this);
164 }
165 SkUNREACHABLE;
166 }
167
FinalizeSettings(ProgramSettings * settings,ProgramKind kind)168 void Compiler::FinalizeSettings(ProgramSettings* settings, ProgramKind kind) {
169 // Honor our optimization-override flags.
170 switch (sOptimizer) {
171 case OverrideFlag::kDefault:
172 break;
173 case OverrideFlag::kOff:
174 settings->fOptimize = false;
175 break;
176 case OverrideFlag::kOn:
177 settings->fOptimize = true;
178 break;
179 }
180
181 switch (sInliner) {
182 case OverrideFlag::kDefault:
183 break;
184 case OverrideFlag::kOff:
185 settings->fInlineThreshold = 0;
186 break;
187 case OverrideFlag::kOn:
188 if (settings->fInlineThreshold == 0) {
189 settings->fInlineThreshold = kDefaultInlineThreshold;
190 }
191 break;
192 }
193
194 // Disable optimization settings that depend on a parent setting which has been disabled.
195 settings->fInlineThreshold *= (int)settings->fOptimize;
196 settings->fRemoveDeadFunctions &= settings->fOptimize;
197 settings->fRemoveDeadVariables &= settings->fOptimize;
198
199 if (kind == ProgramKind::kGeneric) {
200 // For "generic" interpreter programs, leave all functions intact. (The SkVM API supports
201 // calling any function, not just 'main').
202 settings->fRemoveDeadFunctions = false;
203 }
204
205 // Runtime effects always allow narrowing conversions.
206 if (ProgramConfig::IsRuntimeEffect(kind)) {
207 settings->fAllowNarrowingConversions = true;
208 }
209 }
210
compileModule(ProgramKind kind,const char * moduleName,std::string moduleSource,const Module * parent,ModifiersPool & modifiersPool,bool shouldInline)211 std::unique_ptr<Module> Compiler::compileModule(ProgramKind kind,
212 const char* moduleName,
213 std::string moduleSource,
214 const Module* parent,
215 ModifiersPool& modifiersPool,
216 bool shouldInline) {
217 SkASSERT(parent);
218 SkASSERT(!moduleSource.empty());
219 SkASSERT(this->errorCount() == 0);
220
221 // Modules are shared and cannot rely on shader caps.
222 AutoShaderCaps autoCaps(fContext, nullptr);
223 AutoModifiersPool autoPool(fContext, &modifiersPool);
224
225 // Compile the module from source, using default program settings.
226 ProgramSettings settings;
227 FinalizeSettings(&settings, kind);
228 SkSL::Parser parser{this, settings, kind, std::move(moduleSource)};
229 std::unique_ptr<Module> module = parser.moduleInheritingFrom(parent);
230 if (this->errorCount() != 0) {
231 SkDebugf("Unexpected errors compiling %s:\n\n%s\n", moduleName, this->errorText().c_str());
232 return nullptr;
233 }
234 if (shouldInline) {
235 this->optimizeModuleAfterLoading(kind, *module);
236 }
237 return module;
238 }
239
convertProgram(ProgramKind kind,std::string text,ProgramSettings settings)240 std::unique_ptr<Program> Compiler::convertProgram(ProgramKind kind,
241 std::string text,
242 ProgramSettings settings) {
243 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::convertProgram");
244
245 // Make sure the passed-in settings are valid.
246 FinalizeSettings(&settings, kind);
247
248 // Put the ShaderCaps into the context while compiling a program.
249 AutoShaderCaps autoCaps(fContext, fCaps);
250
251 this->resetErrors();
252
253 return Parser(this, settings, kind, std::move(text)).program();
254 }
255
convertIdentifier(Position pos,std::string_view name)256 std::unique_ptr<Expression> Compiler::convertIdentifier(Position pos, std::string_view name) {
257 const Symbol* result = fSymbolTable->find(name);
258 if (!result) {
259 this->errorReporter().error(pos, "unknown identifier '" + std::string(name) + "'");
260 return nullptr;
261 }
262 switch (result->kind()) {
263 case Symbol::Kind::kFunctionDeclaration: {
264 return std::make_unique<FunctionReference>(*fContext, pos,
265 &result->as<FunctionDeclaration>());
266 }
267 case Symbol::Kind::kVariable: {
268 const Variable* var = &result->as<Variable>();
269 // default to kRead_RefKind; this will be corrected later if the variable is written to
270 return VariableReference::Make(pos, var, VariableReference::RefKind::kRead);
271 }
272 case Symbol::Kind::kField: {
273 const Field* field = &result->as<Field>();
274 auto base = VariableReference::Make(pos, &field->owner(),
275 VariableReference::RefKind::kRead);
276 return FieldAccess::Make(*fContext, pos, std::move(base), field->fieldIndex(),
277 FieldAccess::OwnerKind::kAnonymousInterfaceBlock);
278 }
279 case Symbol::Kind::kType: {
280 // go through DSLType so we report errors on private types
281 dsl::DSLModifiers modifiers;
282 dsl::DSLType dslType(result->name(), &modifiers, pos);
283 return TypeReference::Convert(*fContext, pos, &dslType.skslType());
284 }
285 default:
286 SK_ABORT("unsupported symbol type %d\n", (int) result->kind());
287 }
288 }
289
optimizeModuleBeforeMinifying(ProgramKind kind,Module & module)290 bool Compiler::optimizeModuleBeforeMinifying(ProgramKind kind, Module& module) {
291 SkASSERT(this->errorCount() == 0);
292
293 auto m = SkSL::ModuleLoader::Get();
294
295 // Create a temporary program configuration with default settings.
296 ProgramConfig config;
297 config.fIsBuiltinCode = true;
298 config.fKind = kind;
299 AutoProgramConfig autoConfig(this->context(), &config);
300 AutoModifiersPool autoPool(fContext, &m.coreModifiers());
301
302 std::unique_ptr<ProgramUsage> usage = Analysis::GetUsage(module);
303
304 // Assign shorter names to symbols as long as it won't change the external meaning of the code.
305 Transform::RenamePrivateSymbols(this->context(), module, usage.get(), kind);
306
307 // Replace constant variables with their literal values to save space.
308 Transform::ReplaceConstVarsWithLiterals(module, usage.get());
309
310 // Remove any unreachable code.
311 Transform::EliminateUnreachableCode(module, usage.get());
312
313 // We can only remove dead functions from runtime shaders, since runtime-effect helper functions
314 // are isolated from other parts of the program. In a module, an unreferenced function is
315 // intended to be called by the code that includes the module.
316 if (kind == ProgramKind::kRuntimeShader) {
317 while (Transform::EliminateDeadFunctions(this->context(), module, usage.get())) {
318 // Removing dead functions may cause more functions to become unreferenced. Try again.
319 }
320 }
321
322 while (Transform::EliminateDeadLocalVariables(this->context(), module, usage.get())) {
323 // Removing dead variables may cause more variables to become unreferenced. Try again.
324 }
325
326 // Runtime shaders are isolated from other parts of the program via name mangling, so we can
327 // eliminate public globals if they aren't referenced. Otherwise, we only eliminate private
328 // globals (prefixed with `$`) to avoid changing the meaning of the module code.
329 bool onlyPrivateGlobals = !ProgramConfig::IsRuntimeEffect(kind);
330 while (Transform::EliminateDeadGlobalVariables(this->context(), module, usage.get(),
331 onlyPrivateGlobals)) {
332 // Repeat until no changes occur.
333 }
334
335 // We eliminate empty statements to avoid runs of `;;;;;;` caused by the previous passes.
336 SkSL::Transform::EliminateEmptyStatements(module);
337
338 // Make sure that program usage is still correct after the optimization pass is complete.
339 SkASSERT(*usage == *Analysis::GetUsage(module));
340
341 return this->errorCount() == 0;
342 }
343
optimizeModuleAfterLoading(ProgramKind kind,Module & module)344 bool Compiler::optimizeModuleAfterLoading(ProgramKind kind, Module& module) {
345 SkASSERT(this->errorCount() == 0);
346
347 #ifndef SK_ENABLE_OPTIMIZE_SIZE
348 // Create a temporary program configuration with default settings.
349 ProgramConfig config;
350 config.fIsBuiltinCode = true;
351 config.fKind = kind;
352 AutoProgramConfig autoConfig(this->context(), &config);
353
354 std::unique_ptr<ProgramUsage> usage = Analysis::GetUsage(module);
355
356 // Perform inline-candidate analysis and inline any functions deemed suitable.
357 Inliner inliner(fContext.get());
358 while (this->errorCount() == 0) {
359 if (!this->runInliner(&inliner, module.fElements, module.fSymbols, usage.get())) {
360 break;
361 }
362 }
363 // Make sure that program usage is still correct after the optimization pass is complete.
364 SkASSERT(*usage == *Analysis::GetUsage(module));
365 #endif
366
367 return this->errorCount() == 0;
368 }
369
optimize(Program & program)370 bool Compiler::optimize(Program& program) {
371 // The optimizer only needs to run when it is enabled.
372 if (!program.fConfig->fSettings.fOptimize) {
373 return true;
374 }
375
376 AutoShaderCaps autoCaps(fContext, fCaps);
377
378 SkASSERT(!this->errorCount());
379 if (this->errorCount() == 0) {
380 #ifndef SK_ENABLE_OPTIMIZE_SIZE
381 // Run the inliner only once; it is expensive! Multiple passes can occasionally shake out
382 // more wins, but it's diminishing returns.
383 Inliner inliner(fContext.get());
384 this->runInliner(&inliner, program.fOwnedElements, program.fSymbols, program.fUsage.get());
385 #endif
386
387 // Unreachable code can confuse some drivers, so it's worth removing. (skia:12012)
388 Transform::EliminateUnreachableCode(program);
389
390 while (Transform::EliminateDeadFunctions(program)) {
391 // Removing dead functions may cause more functions to become unreferenced. Try again.
392 }
393 while (Transform::EliminateDeadLocalVariables(program)) {
394 // Removing dead variables may cause more variables to become unreferenced. Try again.
395 }
396 while (Transform::EliminateDeadGlobalVariables(program)) {
397 // Repeat until no changes occur.
398 }
399 // Make sure that program usage is still correct after the optimization pass is complete.
400 SkASSERT(*program.usage() == *Analysis::GetUsage(program));
401 }
402
403 return this->errorCount() == 0;
404 }
405
runInliner(Inliner * inliner,const std::vector<std::unique_ptr<ProgramElement>> & elements,std::shared_ptr<SymbolTable> symbols,ProgramUsage * usage)406 bool Compiler::runInliner(Inliner* inliner,
407 const std::vector<std::unique_ptr<ProgramElement>>& elements,
408 std::shared_ptr<SymbolTable> symbols,
409 ProgramUsage* usage) {
410 #ifdef SK_ENABLE_OPTIMIZE_SIZE
411 return true;
412 #else
413 // The program's SymbolTable was taken out of fSymbolTable when the program was bundled, but
414 // the inliner relies (indirectly) on having a valid SymbolTable.
415 // In particular, inlining can turn a non-optimizable expression like `normalize(myVec)` into
416 // `normalize(vec2(7))`, which is now optimizable. The optimizer can use DSL to simplify this
417 // expression--e.g., in the case of normalize, using DSL's Length(). The DSL relies on
418 // convertIdentifier() to look up `length`. convertIdentifier() needs a valid symbol table to
419 // find the declaration of `length`. To allow this chain of events to succeed, we re-insert the
420 // program's symbol table temporarily.
421 SkASSERT(!fSymbolTable);
422 fSymbolTable = symbols;
423
424 bool result = inliner->analyze(elements, symbols, usage);
425
426 fSymbolTable = nullptr;
427 return result;
428 #endif
429 }
430
finalize(Program & program)431 bool Compiler::finalize(Program& program) {
432 AutoShaderCaps autoCaps(fContext, fCaps);
433
434 // Copy all referenced built-in functions into the Program.
435 Transform::FindAndDeclareBuiltinFunctions(program);
436
437 // Variables defined in the pre-includes need their declaring elements added to the program.
438 Transform::FindAndDeclareBuiltinVariables(program);
439
440 // Do one last correctness-check pass. This looks for dangling FunctionReference/TypeReference
441 // expressions, and reports them as errors.
442 Analysis::DoFinalizationChecks(program);
443
444 if (fContext->fConfig->strictES2Mode() && this->errorCount() == 0) {
445 // Enforce Appendix A, Section 5 of the GLSL ES 1.00 spec -- Indexing. This logic assumes
446 // that all loops meet the criteria of Section 4, and if they don't, could crash.
447 for (const auto& pe : program.fOwnedElements) {
448 Analysis::ValidateIndexingForES2(*pe, this->errorReporter());
449 }
450 }
451 if (this->errorCount() == 0) {
452 bool enforceSizeLimit = ProgramConfig::IsRuntimeEffect(program.fConfig->fKind);
453 Analysis::CheckProgramStructure(program, enforceSizeLimit);
454 }
455
456 // Make sure that program usage is still correct after finalization is complete.
457 SkASSERT(*program.usage() == *Analysis::GetUsage(program));
458
459 return this->errorCount() == 0;
460 }
461
462 #if defined(SKSL_STANDALONE) || defined(SK_GANESH) || defined(SK_GRAPHITE)
463
464 #if defined(SK_ENABLE_SPIRV_VALIDATION)
validate_spirv(ErrorReporter & reporter,std::string_view program)465 static bool validate_spirv(ErrorReporter& reporter, std::string_view program) {
466 SkASSERT(0 == program.size() % 4);
467 const uint32_t* programData = reinterpret_cast<const uint32_t*>(program.data());
468 size_t programSize = program.size() / 4;
469
470 spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
471 std::string errors;
472 auto msgFn = [&errors](spv_message_level_t, const char*, const spv_position_t&, const char* m) {
473 errors += "SPIR-V validation error: ";
474 errors += m;
475 errors += '\n';
476 };
477 tools.SetMessageConsumer(msgFn);
478
479 // Verify that the SPIR-V we produced is valid. At runtime, we will abort() with a message
480 // explaining the error. In standalone mode (skslc), we will send the message, plus the
481 // entire disassembled SPIR-V (for easier context & debugging) as *our* error message.
482 bool result = tools.Validate(programData, programSize);
483 if (!result) {
484 #if defined(SKSL_STANDALONE)
485 // Convert the string-stream to a SPIR-V disassembly.
486 std::string disassembly;
487 if (tools.Disassemble(programData, programSize, &disassembly)) {
488 errors.append(disassembly);
489 }
490 reporter.error(Position(), errors);
491 #else
492 SkDEBUGFAILF("%s", errors.c_str());
493 #endif
494 }
495 return result;
496 }
497 #endif
498
toSPIRV(Program & program,OutputStream & out)499 bool Compiler::toSPIRV(Program& program, OutputStream& out) {
500 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toSPIRV");
501 AutoSource as(this, *program.fSource);
502 AutoShaderCaps autoCaps(fContext, fCaps);
503 ProgramSettings settings;
504 settings.fUseMemoryPool = false;
505 dsl::Start(this, program.fConfig->fKind, settings);
506 dsl::SetErrorReporter(&fErrorReporter);
507 fSymbolTable = program.fSymbols;
508 #ifdef SK_ENABLE_SPIRV_VALIDATION
509 StringStream buffer;
510 SPIRVCodeGenerator cg(fContext.get(), &program, &buffer);
511 bool result = cg.generateCode();
512
513 if (result && program.fConfig->fSettings.fValidateSPIRV) {
514 std::string_view binary = buffer.str();
515 result = validate_spirv(this->errorReporter(), binary);
516 out.write(binary.data(), binary.size());
517 }
518 #else
519 SPIRVCodeGenerator cg(fContext.get(), &program, &out);
520 bool result = cg.generateCode();
521 #endif
522 dsl::End();
523 return result;
524 }
525
toSPIRV(Program & program,std::string * out)526 bool Compiler::toSPIRV(Program& program, std::string* out) {
527 StringStream buffer;
528 bool result = this->toSPIRV(program, buffer);
529 if (result) {
530 *out = buffer.str();
531 }
532 return result;
533 }
534
toGLSL(Program & program,OutputStream & out)535 bool Compiler::toGLSL(Program& program, OutputStream& out) {
536 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toGLSL");
537 AutoSource as(this, *program.fSource);
538 AutoShaderCaps autoCaps(fContext, fCaps);
539 GLSLCodeGenerator cg(fContext.get(), &program, &out);
540 bool result = cg.generateCode();
541 return result;
542 }
543
toGLSL(Program & program,std::string * out)544 bool Compiler::toGLSL(Program& program, std::string* out) {
545 StringStream buffer;
546 bool result = this->toGLSL(program, buffer);
547 if (result) {
548 *out = buffer.str();
549 }
550 return result;
551 }
552
toHLSL(Program & program,OutputStream & out)553 bool Compiler::toHLSL(Program& program, OutputStream& out) {
554 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toHLSL");
555 std::string hlsl;
556 if (!this->toHLSL(program, &hlsl)) {
557 return false;
558 }
559 out.writeString(hlsl);
560 return true;
561 }
562
toHLSL(Program & program,std::string * out)563 bool Compiler::toHLSL(Program& program, std::string* out) {
564 std::string spirv;
565 if (!this->toSPIRV(program, &spirv)) {
566 return false;
567 }
568
569 if (!SPIRVtoHLSL(spirv, out)) {
570 fErrorText += "HLSL cross-compilation not enabled";
571 return false;
572 }
573
574 return true;
575 }
576
toMetal(Program & program,OutputStream & out)577 bool Compiler::toMetal(Program& program, OutputStream& out) {
578 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toMetal");
579 AutoSource as(this, *program.fSource);
580 AutoShaderCaps autoCaps(fContext, fCaps);
581 MetalCodeGenerator cg(fContext.get(), &program, &out);
582 bool result = cg.generateCode();
583 return result;
584 }
585
toMetal(Program & program,std::string * out)586 bool Compiler::toMetal(Program& program, std::string* out) {
587 StringStream buffer;
588 bool result = this->toMetal(program, buffer);
589 if (result) {
590 *out = buffer.str();
591 }
592 return result;
593 }
594
595 #if defined(SK_ENABLE_WGSL_VALIDATION)
validate_wgsl(ErrorReporter & reporter,const std::string & wgsl)596 static bool validate_wgsl(ErrorReporter& reporter, const std::string& wgsl) {
597 tint::Source::File srcFile("", wgsl);
598 tint::Program program(tint::reader::wgsl::Parse(&srcFile));
599 if (program.Diagnostics().count() > 0) {
600 tint::diag::Formatter diagFormatter;
601 std::string diagOutput = diagFormatter.format(program.Diagnostics());
602 #if defined(SKSL_STANDALONE)
603 reporter.error(Position(), diagOutput);
604 #else
605 SkDEBUGFAILF("%s", diagOutput.c_str());
606 #endif
607 return false;
608 }
609 return true;
610 }
611 #endif // defined(SK_ENABLE_WGSL_VALIDATION)
612
toWGSL(Program & program,OutputStream & out)613 bool Compiler::toWGSL(Program& program, OutputStream& out) {
614 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toWGSL");
615 AutoSource as(this, *program.fSource);
616 #ifdef SK_ENABLE_WGSL_VALIDATION
617 StringStream wgsl;
618 WGSLCodeGenerator cg(fContext.get(), &program, &wgsl);
619 bool result = cg.generateCode();
620 if (result) {
621 std::string wgslString = wgsl.str();
622 result = validate_wgsl(this->errorReporter(), wgslString);
623 out.writeString(wgslString);
624 }
625 #else
626 WGSLCodeGenerator cg(fContext.get(), &program, &out);
627 bool result = cg.generateCode();
628 #endif
629 return result;
630 }
631
632 #endif // defined(SKSL_STANDALONE) || defined(SK_GANESH) || defined(SK_GRAPHITE)
633
handleError(std::string_view msg,Position pos)634 void Compiler::handleError(std::string_view msg, Position pos) {
635 fErrorText += "error: ";
636 bool printLocation = false;
637 std::string_view src = this->errorReporter().source();
638 int line = -1;
639 if (pos.valid()) {
640 line = pos.line(src);
641 printLocation = pos.startOffset() < (int)src.length();
642 fErrorText += std::to_string(line) + ": ";
643 }
644 fErrorText += std::string(msg) + "\n";
645 if (printLocation) {
646 const int kMaxSurroundingChars = 100;
647
648 // Find the beginning of the line.
649 int lineStart = pos.startOffset();
650 while (lineStart > 0) {
651 if (src[lineStart - 1] == '\n') {
652 break;
653 }
654 --lineStart;
655 }
656
657 // We don't want to show more than 100 characters surrounding the error, so push the line
658 // start forward and add a leading ellipsis if there would be more than this.
659 std::string lineText;
660 std::string caretText;
661 if ((pos.startOffset() - lineStart) > kMaxSurroundingChars) {
662 lineStart = pos.startOffset() - kMaxSurroundingChars;
663 lineText = "...";
664 caretText = " ";
665 }
666
667 // Echo the line. Again, we don't want to show more than 100 characters after the end of the
668 // error, so truncate with a trailing ellipsis if needed.
669 const char* lineSuffix = "...\n";
670 int lineStop = pos.endOffset() + kMaxSurroundingChars;
671 if (lineStop >= (int)src.length()) {
672 lineStop = src.length() - 1;
673 lineSuffix = "\n"; // no ellipsis if we reach end-of-file
674 }
675 for (int i = lineStart; i < lineStop; ++i) {
676 char c = src[i];
677 if (c == '\n') {
678 lineSuffix = "\n"; // no ellipsis if we reach end-of-line
679 break;
680 }
681 switch (c) {
682 case '\t': lineText += " "; break;
683 case '\0': lineText += " "; break;
684 default: lineText += src[i]; break;
685 }
686 }
687 fErrorText += lineText + lineSuffix;
688
689 // print the carets underneath it, pointing to the range in question
690 for (int i = lineStart; i < (int)src.length(); i++) {
691 if (i >= pos.endOffset()) {
692 break;
693 }
694 switch (src[i]) {
695 case '\t':
696 caretText += (i >= pos.startOffset()) ? "^^^^" : " ";
697 break;
698 case '\n':
699 SkASSERT(i >= pos.startOffset());
700 // use an ellipsis if the error continues past the end of the line
701 caretText += (pos.endOffset() > i + 1) ? "..." : "^";
702 i = src.length();
703 break;
704 default:
705 caretText += (i >= pos.startOffset()) ? '^' : ' ';
706 break;
707 }
708 }
709 fErrorText += caretText + '\n';
710 }
711 }
712
errorText(bool showCount)713 std::string Compiler::errorText(bool showCount) {
714 if (showCount) {
715 this->writeErrorCount();
716 }
717 std::string result = fErrorText;
718 this->resetErrors();
719 return result;
720 }
721
writeErrorCount()722 void Compiler::writeErrorCount() {
723 int count = this->errorCount();
724 if (count) {
725 fErrorText += std::to_string(count) + " error";
726 if (count > 1) {
727 fErrorText += "s";
728 }
729 fErrorText += "\n";
730 }
731 }
732
733 } // namespace SkSL
734