• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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