blob: 94ce944aa58bc1ed0b2731e2489dc68cf0b0d422 [file] [log] [blame]
ethannicholasb3058bd2016-07-01 08:22:01 -07001/*
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 */
Mike Klein6ad99092016-10-26 10:35:22 -04007
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/sksl/SkSLCompiler.h"
ethannicholasb3058bd2016-07-01 08:22:01 -07009
John Stilesfbd050b2020-08-03 13:21:46 -040010#include <memory>
John Stilesb8e010c2020-08-11 18:05:39 -040011#include <unordered_set>
John Stilesfbd050b2020-08-03 13:21:46 -040012
John Stiles270cec22021-02-17 12:59:36 -050013#include "src/core/SkScopeExit.h"
Leon Scrogginsb66214e2021-02-11 17:14:18 -050014#include "src/core/SkTraceEvent.h"
John Stilesb92641c2020-08-31 18:09:01 -040015#include "src/sksl/SkSLAnalysis.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#include "src/sksl/SkSLCPPCodeGenerator.h"
John Stilesf3a28db2021-03-10 23:00:47 -050017#include "src/sksl/SkSLConstantFolder.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "src/sksl/SkSLGLSLCodeGenerator.h"
19#include "src/sksl/SkSLHCodeGenerator.h"
20#include "src/sksl/SkSLIRGenerator.h"
21#include "src/sksl/SkSLMetalCodeGenerator.h"
Brian Osman00185012021-02-04 16:07:11 -050022#include "src/sksl/SkSLOperators.h"
John Stiles270cec22021-02-17 12:59:36 -050023#include "src/sksl/SkSLProgramSettings.h"
Ethan Nicholasc18bb512020-07-28 14:46:53 -040024#include "src/sksl/SkSLRehydrator.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/sksl/SkSLSPIRVCodeGenerator.h"
Brian Osmanc0243912020-02-19 15:35:26 -050026#include "src/sksl/SkSLSPIRVtoHLSL.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "src/sksl/ir/SkSLEnum.h"
28#include "src/sksl/ir/SkSLExpression.h"
29#include "src/sksl/ir/SkSLExpressionStatement.h"
30#include "src/sksl/ir/SkSLFunctionCall.h"
31#include "src/sksl/ir/SkSLIntLiteral.h"
32#include "src/sksl/ir/SkSLModifiersDeclaration.h"
33#include "src/sksl/ir/SkSLNop.h"
34#include "src/sksl/ir/SkSLSymbolTable.h"
35#include "src/sksl/ir/SkSLTernaryExpression.h"
36#include "src/sksl/ir/SkSLUnresolvedFunction.h"
37#include "src/sksl/ir/SkSLVarDeclarations.h"
John Stilese6150002020-10-05 12:03:53 -040038#include "src/utils/SkBitSet.h"
ethannicholasb3058bd2016-07-01 08:22:01 -070039
Ethan Nicholasb33fa3f2020-08-06 13:00:19 -040040#include <fstream>
41
Ethan Nicholasa11035b2019-11-26 16:27:47 -050042#if !defined(SKSL_STANDALONE) & SK_SUPPORT_GPU
43#include "include/gpu/GrContextOptions.h"
44#include "src/gpu/GrShaderCaps.h"
45#endif
46
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -040047#ifdef SK_ENABLE_SPIRV_VALIDATION
48#include "spirv-tools/libspirv.hpp"
49#endif
50
Brian Osman3d87e9f2020-10-08 11:50:22 -040051#if defined(SKSL_STANDALONE)
Ethan Nicholasc18bb512020-07-28 14:46:53 -040052
Brian Osman3d87e9f2020-10-08 11:50:22 -040053// In standalone mode, we load the textual sksl source files. GN generates or copies these files
54// to the skslc executable directory. The "data" in this mode is just the filename.
55#define MODULE_DATA(name) MakeModulePath("sksl_" #name ".sksl")
56
57#else
58
59// At runtime, we load the dehydrated sksl data files. The data is a (pointer, size) pair.
Ethan Nicholasc18bb512020-07-28 14:46:53 -040060#include "src/sksl/generated/sksl_fp.dehydrated.sksl"
61#include "src/sksl/generated/sksl_frag.dehydrated.sksl"
62#include "src/sksl/generated/sksl_geom.dehydrated.sksl"
63#include "src/sksl/generated/sksl_gpu.dehydrated.sksl"
Brian Osmanb06301e2020-11-06 11:45:36 -050064#include "src/sksl/generated/sksl_public.dehydrated.sksl"
Brian Osman91946752020-12-21 13:20:40 -050065#include "src/sksl/generated/sksl_runtime.dehydrated.sksl"
Ethan Nicholasc18bb512020-07-28 14:46:53 -040066#include "src/sksl/generated/sksl_vert.dehydrated.sksl"
67
Brian Osman3d87e9f2020-10-08 11:50:22 -040068#define MODULE_DATA(name) MakeModuleData(SKSL_INCLUDE_sksl_##name,\
69 SKSL_INCLUDE_sksl_##name##_LENGTH)
Ethan Nicholasc18bb512020-07-28 14:46:53 -040070
71#endif
Ethan Nicholas0d997662019-04-08 09:46:01 -040072
ethannicholasb3058bd2016-07-01 08:22:01 -070073namespace SkSL {
74
John Stiles7247b482021-03-08 10:40:35 -050075// Set these flags to `false` to disable optimization passes unilaterally.
76// These flags allow tools like Viewer or Nanobench to override the compiler's ProgramSettings.
77bool gSkSLOptimizer = true;
78bool gSkSLInliner = true;
John Stiles0bfeae62021-03-11 09:09:42 -050079bool gSkSLDeadCodeElimination = true;
John Stiles8ef4d6c2021-03-05 16:01:45 -050080
John Stiles47c0a742021-02-09 09:30:35 -050081using RefKind = VariableReference::RefKind;
82
Brian Osman88cda172020-10-09 12:05:16 -040083class AutoSource {
84public:
85 AutoSource(Compiler* compiler, const String* source)
86 : fCompiler(compiler), fOldSource(fCompiler->fSource) {
87 fCompiler->fSource = source;
88 }
89
90 ~AutoSource() { fCompiler->fSource = fOldSource; }
91
92 Compiler* fCompiler;
93 const String* fOldSource;
94};
95
John Stilesa935c3f2021-02-25 10:35:49 -050096class AutoProgramConfig {
97public:
98 AutoProgramConfig(std::shared_ptr<Context>& context, ProgramConfig* config)
99 : fContext(context.get()) {
100 SkASSERT(!fContext->fConfig);
101 fContext->fConfig = config;
102 }
103
104 ~AutoProgramConfig() {
105 fContext->fConfig = nullptr;
106 }
107
108 Context* fContext;
109};
110
John Stilesd6a5f4492021-02-11 15:46:11 -0500111Compiler::Compiler(const ShaderCapsClass* caps)
John Stilesc1a98b82021-02-24 13:35:02 -0500112 : fContext(std::make_shared<Context>(/*errors=*/*this, *caps))
John Stiles7b920442020-12-17 10:43:41 -0500113 , fInliner(fContext.get())
Brian Osman0006ad02020-11-18 15:38:39 -0500114 , fErrorCount(0) {
John Stilesc1a98b82021-02-24 13:35:02 -0500115 SkASSERT(caps);
John Stiles7c3515b2020-10-16 18:38:39 -0400116 fRootSymbolTable = std::make_shared<SymbolTable>(this, /*builtin=*/true);
Brian Osmanb06301e2020-11-06 11:45:36 -0500117 fPrivateSymbolTable = std::make_shared<SymbolTable>(fRootSymbolTable, /*builtin=*/true);
John Stilesc1a98b82021-02-24 13:35:02 -0500118 fIRGenerator = std::make_unique<IRGenerator>(fContext.get());
ethannicholasb3058bd2016-07-01 08:22:01 -0700119
John Stiles54e7c052021-01-11 14:22:36 -0500120#define TYPE(t) fContext->fTypes.f ## t .get()
ethannicholasb3058bd2016-07-01 08:22:01 -0700121
Brian Osmanb06301e2020-11-06 11:45:36 -0500122 const SkSL::Symbol* rootTypes[] = {
123 TYPE(Void),
Brian Salomonbf7b6202016-11-11 16:08:03 -0500124
Brian Osmanb06301e2020-11-06 11:45:36 -0500125 TYPE( Float), TYPE( Float2), TYPE( Float3), TYPE( Float4),
126 TYPE( Half), TYPE( Half2), TYPE( Half3), TYPE( Half4),
127 TYPE( Int), TYPE( Int2), TYPE( Int3), TYPE( Int4),
Brian Osmanb06301e2020-11-06 11:45:36 -0500128 TYPE( Bool), TYPE( Bool2), TYPE( Bool3), TYPE( Bool4),
Brian Salomon2a51de82016-11-16 12:06:01 -0500129
Brian Osmanc0f2b642020-12-22 13:35:55 -0500130 TYPE(Float2x2), TYPE(Float3x3), TYPE(Float4x4),
Brian Osmanc63f4312020-12-23 11:44:14 -0500131 TYPE( Half2x2), TYPE( Half3x3), TYPE(Half4x4),
Greg Daniel64773e62016-11-22 09:44:03 -0500132
Brian Osmanc63f4312020-12-23 11:44:14 -0500133 TYPE(SquareMat), TYPE(SquareHMat),
ethannicholasb3058bd2016-07-01 08:22:01 -0700134
Brian Osman20fad322020-12-23 12:42:33 -0500135 TYPE(GenType), TYPE(GenHType), TYPE(GenIType), TYPE(GenBType),
136 TYPE(Vec), TYPE(HVec), TYPE(IVec), TYPE(BVec),
Brian Osmanb06301e2020-11-06 11:45:36 -0500137
138 TYPE(FragmentProcessor),
139 };
140
141 const SkSL::Symbol* privateTypes[] = {
Brian Osman20fad322020-12-23 12:42:33 -0500142 TYPE( UInt), TYPE( UInt2), TYPE( UInt3), TYPE( UInt4),
143 TYPE( Short), TYPE( Short2), TYPE( Short3), TYPE( Short4),
144 TYPE(UShort), TYPE(UShort2), TYPE(UShort3), TYPE(UShort4),
145 TYPE( Byte), TYPE( Byte2), TYPE( Byte3), TYPE( Byte4),
146 TYPE( UByte), TYPE( UByte2), TYPE( UByte3), TYPE( UByte4),
147
148 TYPE(GenUType), TYPE(UVec),
149 TYPE(SVec), TYPE(USVec), TYPE(ByteVec), TYPE(UByteVec),
150
Brian Osmanc0f2b642020-12-22 13:35:55 -0500151 TYPE(Float2x3), TYPE(Float2x4),
152 TYPE(Float3x2), TYPE(Float3x4),
153 TYPE(Float4x2), TYPE(Float4x3),
154
Brian Osmanc63f4312020-12-23 11:44:14 -0500155 TYPE(Half2x3), TYPE(Half2x4),
156 TYPE(Half3x2), TYPE(Half3x4),
157 TYPE(Half4x2), TYPE(Half4x3),
158
Brian Osmanc0f2b642020-12-22 13:35:55 -0500159 TYPE(Mat), TYPE(HMat),
160
Brian Osmanb06301e2020-11-06 11:45:36 -0500161 TYPE(Sampler1D), TYPE(Sampler2D), TYPE(Sampler3D),
162 TYPE(SamplerExternalOES),
Brian Osmanb06301e2020-11-06 11:45:36 -0500163 TYPE(Sampler2DRect),
Brian Osmanb06301e2020-11-06 11:45:36 -0500164
165 TYPE(ISampler2D),
Brian Osmanb06301e2020-11-06 11:45:36 -0500166 TYPE(SubpassInput), TYPE(SubpassInputMS),
167
Brian Osmanb06301e2020-11-06 11:45:36 -0500168 TYPE(Sampler),
169 TYPE(Texture2D),
170 };
171
172 for (const SkSL::Symbol* type : rootTypes) {
173 fRootSymbolTable->addWithoutOwnership(type);
174 }
175 for (const SkSL::Symbol* type : privateTypes) {
176 fPrivateSymbolTable->addWithoutOwnership(type);
177 }
178
179#undef TYPE
ethannicholasb3058bd2016-07-01 08:22:01 -0700180
Brian Osman3887a012020-09-30 13:22:27 -0400181 // sk_Caps is "builtin", but all references to it are resolved to Settings, so we don't need to
182 // treat it as builtin (ie, no need to clone it into the Program).
Brian Osmanb06301e2020-11-06 11:45:36 -0500183 fPrivateSymbolTable->add(
184 std::make_unique<Variable>(/*offset=*/-1,
John Stiles586df952020-11-12 18:27:13 -0500185 fIRGenerator->fModifiers->addToPool(Modifiers()),
Brian Osmanb06301e2020-11-06 11:45:36 -0500186 "sk_Caps",
John Stiles54e7c052021-01-11 14:22:36 -0500187 fContext->fTypes.fSkCaps.get(),
Brian Osmanb06301e2020-11-06 11:45:36 -0500188 /*builtin=*/false,
189 Variable::Storage::kGlobal));
Ethan Nicholas3605ace2016-11-21 15:59:48 -0500190
Brian Osman3d87e9f2020-10-08 11:50:22 -0400191 fRootModule = {fRootSymbolTable, /*fIntrinsics=*/nullptr};
Brian Osmanb06301e2020-11-06 11:45:36 -0500192 fPrivateModule = {fPrivateSymbolTable, /*fIntrinsics=*/nullptr};
ethannicholasb3058bd2016-07-01 08:22:01 -0700193}
194
John Stilesdd13dba2020-10-29 10:45:34 -0400195Compiler::~Compiler() {}
ethannicholasb3058bd2016-07-01 08:22:01 -0700196
Brian Osman56269982020-11-20 12:38:07 -0500197const ParsedModule& Compiler::loadGPUModule() {
198 if (!fGPUModule.fSymbols) {
John Stilesdbd4e6f2021-02-16 13:29:15 -0500199 fGPUModule = this->parseModule(ProgramKind::kFragment, MODULE_DATA(gpu), fPrivateModule);
Brian Osman56269982020-11-20 12:38:07 -0500200 }
201 return fGPUModule;
202}
203
204const ParsedModule& Compiler::loadFragmentModule() {
205 if (!fFragmentModule.fSymbols) {
John Stilesdbd4e6f2021-02-16 13:29:15 -0500206 fFragmentModule = this->parseModule(ProgramKind::kFragment, MODULE_DATA(frag),
Brian Osman56269982020-11-20 12:38:07 -0500207 this->loadGPUModule());
208 }
209 return fFragmentModule;
210}
211
212const ParsedModule& Compiler::loadVertexModule() {
213 if (!fVertexModule.fSymbols) {
John Stilesdbd4e6f2021-02-16 13:29:15 -0500214 fVertexModule = this->parseModule(ProgramKind::kVertex, MODULE_DATA(vert),
Brian Osman56269982020-11-20 12:38:07 -0500215 this->loadGPUModule());
216 }
217 return fVertexModule;
218}
219
Brian Osman88cda172020-10-09 12:05:16 -0400220const ParsedModule& Compiler::loadGeometryModule() {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400221 if (!fGeometryModule.fSymbols) {
John Stilesdbd4e6f2021-02-16 13:29:15 -0500222 fGeometryModule = this->parseModule(ProgramKind::kGeometry, MODULE_DATA(geom),
Brian Osman56269982020-11-20 12:38:07 -0500223 this->loadGPUModule());
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400224 }
Brian Osman88cda172020-10-09 12:05:16 -0400225 return fGeometryModule;
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400226}
227
Brian Osman88cda172020-10-09 12:05:16 -0400228const ParsedModule& Compiler::loadFPModule() {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400229 if (!fFPModule.fSymbols) {
John Stilesdbd4e6f2021-02-16 13:29:15 -0500230 fFPModule = this->parseModule(ProgramKind::kFragmentProcessor, MODULE_DATA(fp),
Brian Osman56269982020-11-20 12:38:07 -0500231 this->loadGPUModule());
Brian Osman8e2ef022020-09-30 13:26:43 -0400232 }
Brian Osman88cda172020-10-09 12:05:16 -0400233 return fFPModule;
Brian Osman8e2ef022020-09-30 13:26:43 -0400234}
235
Brian Osmanb06301e2020-11-06 11:45:36 -0500236const ParsedModule& Compiler::loadPublicModule() {
237 if (!fPublicModule.fSymbols) {
John Stilesdbd4e6f2021-02-16 13:29:15 -0500238 fPublicModule = this->parseModule(ProgramKind::kGeneric, MODULE_DATA(public), fRootModule);
Brian Osmanb06301e2020-11-06 11:45:36 -0500239 }
240 return fPublicModule;
241}
242
Brian Osman91946752020-12-21 13:20:40 -0500243const ParsedModule& Compiler::loadRuntimeEffectModule() {
244 if (!fRuntimeEffectModule.fSymbols) {
John Stilesdbd4e6f2021-02-16 13:29:15 -0500245 fRuntimeEffectModule = this->parseModule(ProgramKind::kRuntimeEffect, MODULE_DATA(runtime),
Brian Osman91946752020-12-21 13:20:40 -0500246 this->loadPublicModule());
Brian Osmanf1319c32020-10-13 09:34:23 -0400247
Brian Osman91946752020-12-21 13:20:40 -0500248 // Add some aliases to the runtime effect module so that it's friendlier, and more like GLSL
John Stiles54e7c052021-01-11 14:22:36 -0500249 fRuntimeEffectModule.fSymbols->addAlias("shader", fContext->fTypes.fFragmentProcessor.get());
Brian Osmanf1319c32020-10-13 09:34:23 -0400250
John Stiles54e7c052021-01-11 14:22:36 -0500251 fRuntimeEffectModule.fSymbols->addAlias("vec2", fContext->fTypes.fFloat2.get());
252 fRuntimeEffectModule.fSymbols->addAlias("vec3", fContext->fTypes.fFloat3.get());
253 fRuntimeEffectModule.fSymbols->addAlias("vec4", fContext->fTypes.fFloat4.get());
Brian Osmanf1319c32020-10-13 09:34:23 -0400254
John Stiles54e7c052021-01-11 14:22:36 -0500255 fRuntimeEffectModule.fSymbols->addAlias("bvec2", fContext->fTypes.fBool2.get());
256 fRuntimeEffectModule.fSymbols->addAlias("bvec3", fContext->fTypes.fBool3.get());
257 fRuntimeEffectModule.fSymbols->addAlias("bvec4", fContext->fTypes.fBool4.get());
Brian Osmanf1319c32020-10-13 09:34:23 -0400258
John Stiles54e7c052021-01-11 14:22:36 -0500259 fRuntimeEffectModule.fSymbols->addAlias("mat2", fContext->fTypes.fFloat2x2.get());
260 fRuntimeEffectModule.fSymbols->addAlias("mat3", fContext->fTypes.fFloat3x3.get());
261 fRuntimeEffectModule.fSymbols->addAlias("mat4", fContext->fTypes.fFloat4x4.get());
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400262 }
Brian Osman91946752020-12-21 13:20:40 -0500263 return fRuntimeEffectModule;
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400264}
265
John Stilesdbd4e6f2021-02-16 13:29:15 -0500266const ParsedModule& Compiler::moduleForProgramKind(ProgramKind kind) {
Brian Osman88cda172020-10-09 12:05:16 -0400267 switch (kind) {
John Stilesdbd4e6f2021-02-16 13:29:15 -0500268 case ProgramKind::kVertex: return this->loadVertexModule(); break;
269 case ProgramKind::kFragment: return this->loadFragmentModule(); break;
270 case ProgramKind::kGeometry: return this->loadGeometryModule(); break;
271 case ProgramKind::kFragmentProcessor: return this->loadFPModule(); break;
272 case ProgramKind::kRuntimeEffect: return this->loadRuntimeEffectModule(); break;
273 case ProgramKind::kGeneric: return this->loadPublicModule(); break;
Brian Osman88cda172020-10-09 12:05:16 -0400274 }
275 SkUNREACHABLE;
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400276}
277
John Stilesdbd4e6f2021-02-16 13:29:15 -0500278LoadedModule Compiler::loadModule(ProgramKind kind,
Brian Osman3d87e9f2020-10-08 11:50:22 -0400279 ModuleData data,
John Stilesa935c3f2021-02-25 10:35:49 -0500280 std::shared_ptr<SymbolTable> base,
281 bool dehydrate) {
282 if (dehydrate) {
283 // NOTE: This is a workaround. When dehydrating includes, skslc doesn't know which module
284 // it's preparing, nor what the correct base module is. We can't use 'Root', because many
285 // GPU intrinsics reference private types, like samplers or textures. Today, 'Private' does
286 // contain the union of all known types, so this is safe. If we ever have types that only
287 // exist in 'Public' (for example), this logic needs to be smarter (by choosing the correct
288 // base for the module we're compiling).
Brian Osmanb06301e2020-11-06 11:45:36 -0500289 base = fPrivateSymbolTable;
Brian Osman3d87e9f2020-10-08 11:50:22 -0400290 }
John Stilesa935c3f2021-02-25 10:35:49 -0500291 SkASSERT(base);
292
293 // Built-in modules always use default program settings.
294 ProgramConfig config;
295 config.fKind = kind;
296 config.fSettings.fReplaceSettings = !dehydrate;
297 AutoProgramConfig autoConfig(fContext, &config);
Brian Osman3d87e9f2020-10-08 11:50:22 -0400298
299#if defined(SKSL_STANDALONE)
300 SkASSERT(data.fPath);
301 std::ifstream in(data.fPath);
John Stilesd51c9792021-03-18 11:40:14 -0400302 String text{std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>()};
Ethan Nicholasb33fa3f2020-08-06 13:00:19 -0400303 if (in.rdstate()) {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400304 printf("error reading %s\n", data.fPath);
Ethan Nicholasb33fa3f2020-08-06 13:00:19 -0400305 abort();
306 }
Brian Osmane498b3c2020-09-23 14:42:11 -0400307 const String* source = fRootSymbolTable->takeOwnershipOfString(std::move(text));
Brian Osman88cda172020-10-09 12:05:16 -0400308 AutoSource as(this, source);
John Stilesd1204642021-02-17 16:30:02 -0500309
John Stiles881a10c2020-09-19 10:13:24 -0400310 SkASSERT(fIRGenerator->fCanInline);
311 fIRGenerator->fCanInline = false;
John Stilesd1204642021-02-17 16:30:02 -0500312
Brian Osman88cda172020-10-09 12:05:16 -0400313 ParsedModule baseModule = {base, /*fIntrinsics=*/nullptr};
John Stilesd1204642021-02-17 16:30:02 -0500314 IRGenerator::IRBundle ir = fIRGenerator->convertProgram(baseModule, /*isBuiltinCode=*/true,
315 source->c_str(), source->length(),
316 /*externalFunctions=*/nullptr);
Brian Osman133724c2020-10-28 14:14:39 -0400317 SkASSERT(ir.fSharedElements.empty());
Brian Osman0006ad02020-11-18 15:38:39 -0500318 LoadedModule module = { kind, std::move(ir.fSymbolTable), std::move(ir.fElements) };
John Stiles881a10c2020-09-19 10:13:24 -0400319 fIRGenerator->fCanInline = true;
Ethan Nicholas8da1e652019-05-24 11:01:59 -0400320 if (this->fErrorCount) {
321 printf("Unexpected errors: %s\n", this->fErrorText.c_str());
Brian Osman3d87e9f2020-10-08 11:50:22 -0400322 SkDEBUGFAILF("%s %s\n", data.fPath, this->fErrorText.c_str());
Ethan Nicholas8da1e652019-05-24 11:01:59 -0400323 }
Brian Osman88cda172020-10-09 12:05:16 -0400324 fModifiers.push_back(std::move(ir.fModifiers));
Brian Osman3d87e9f2020-10-08 11:50:22 -0400325#else
326 SkASSERT(data.fData && (data.fSize != 0));
327 Rehydrator rehydrator(fContext.get(), fIRGenerator->fModifiers.get(), base, this,
328 data.fData, data.fSize);
Brian Osman0006ad02020-11-18 15:38:39 -0500329 LoadedModule module = { kind, rehydrator.symbolTable(), rehydrator.elements() };
Brian Osman3d87e9f2020-10-08 11:50:22 -0400330 fModifiers.push_back(fIRGenerator->releaseModifiers());
331#endif
332
333 return module;
334}
335
John Stilesdbd4e6f2021-02-16 13:29:15 -0500336ParsedModule Compiler::parseModule(ProgramKind kind, ModuleData data, const ParsedModule& base) {
John Stilesa935c3f2021-02-25 10:35:49 -0500337 LoadedModule module = this->loadModule(kind, data, base.fSymbols, /*dehydrate=*/false);
Brian Osman0006ad02020-11-18 15:38:39 -0500338 this->optimize(module);
Brian Osman3d87e9f2020-10-08 11:50:22 -0400339
340 // For modules that just declare (but don't define) intrinsic functions, there will be no new
341 // program elements. In that case, we can share our parent's intrinsic map:
Brian Osman0006ad02020-11-18 15:38:39 -0500342 if (module.fElements.empty()) {
343 return {module.fSymbols, base.fIntrinsics};
Brian Osman3d87e9f2020-10-08 11:50:22 -0400344 }
345
346 auto intrinsics = std::make_shared<IRIntrinsicMap>(base.fIntrinsics.get());
347
348 // Now, transfer all of the program elements to an intrinsic map. This maps certain types of
349 // global objects to the declaring ProgramElement.
Brian Osman0006ad02020-11-18 15:38:39 -0500350 for (std::unique_ptr<ProgramElement>& element : module.fElements) {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400351 switch (element->kind()) {
352 case ProgramElement::Kind::kFunction: {
353 const FunctionDefinition& f = element->as<FunctionDefinition>();
Ethan Nicholas0a5d0962020-10-14 13:33:18 -0400354 SkASSERT(f.declaration().isBuiltin());
355 intrinsics->insertOrDie(f.declaration().description(), std::move(element));
Brian Osman3d87e9f2020-10-08 11:50:22 -0400356 break;
357 }
John Stiles569249b2020-11-03 12:18:22 -0500358 case ProgramElement::Kind::kFunctionPrototype: {
359 // These are already in the symbol table.
360 break;
361 }
Brian Osman3d87e9f2020-10-08 11:50:22 -0400362 case ProgramElement::Kind::kEnum: {
363 const Enum& e = element->as<Enum>();
364 SkASSERT(e.isBuiltin());
365 intrinsics->insertOrDie(e.typeName(), std::move(element));
366 break;
367 }
368 case ProgramElement::Kind::kGlobalVar: {
Ethan Nicholasc51f33e2020-10-13 13:49:44 -0400369 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
370 const Variable& var = global.declaration()->as<VarDeclaration>().var();
371 SkASSERT(var.isBuiltin());
372 intrinsics->insertOrDie(var.name(), std::move(element));
Brian Osman3d87e9f2020-10-08 11:50:22 -0400373 break;
374 }
375 case ProgramElement::Kind::kInterfaceBlock: {
Ethan Nicholaseaf47882020-10-15 10:10:08 -0400376 const Variable& var = element->as<InterfaceBlock>().variable();
377 SkASSERT(var.isBuiltin());
378 intrinsics->insertOrDie(var.name(), std::move(element));
Brian Osman3d87e9f2020-10-08 11:50:22 -0400379 break;
380 }
381 default:
382 printf("Unsupported element: %s\n", element->description().c_str());
383 SkASSERT(false);
384 break;
385 }
386 }
387
Brian Osman0006ad02020-11-18 15:38:39 -0500388 return {module.fSymbols, std::move(intrinsics)};
Ethan Nicholas8da1e652019-05-24 11:01:59 -0400389}
390
Brian Osman32d53552020-09-23 13:55:20 -0400391std::unique_ptr<Program> Compiler::convertProgram(
John Stilesdbd4e6f2021-02-16 13:29:15 -0500392 ProgramKind kind,
Brian Osman32d53552020-09-23 13:55:20 -0400393 String text,
394 const Program::Settings& settings,
Brian Osmanbe0b3b72021-01-06 14:27:35 -0500395 const std::vector<std::unique_ptr<ExternalFunction>>* externalFunctions) {
Brian Osman7a20b5c2021-03-15 16:23:33 -0400396 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::convertProgram");
Leon Scrogginsb66214e2021-02-11 17:14:18 -0500397
John Stilesdbd4e6f2021-02-16 13:29:15 -0500398 SkASSERT(!externalFunctions || (kind == ProgramKind::kGeneric));
Ethan Nicholas91164d12019-05-15 15:29:54 -0400399
Brian Osman0006ad02020-11-18 15:38:39 -0500400 // Loading and optimizing our base module might reset the inliner, so do that first,
401 // *then* configure the inliner with the settings for this program.
402 const ParsedModule& baseModule = this->moduleForProgramKind(kind);
403
John Stiles270cec22021-02-17 12:59:36 -0500404 // Update our context to point to the program configuration for the duration of compilation.
405 auto config = std::make_unique<ProgramConfig>(ProgramConfig{kind, settings});
John Stilesa935c3f2021-02-25 10:35:49 -0500406 AutoProgramConfig autoConfig(fContext, config.get());
John Stiles270cec22021-02-17 12:59:36 -0500407
John Stiles7247b482021-03-08 10:40:35 -0500408 // Honor our global optimization-disable flags.
409 config->fSettings.fOptimize &= gSkSLOptimizer;
John Stiles7247b482021-03-08 10:40:35 -0500410 config->fSettings.fInlineThreshold *= (int)gSkSLInliner;
John Stiles0bfeae62021-03-11 09:09:42 -0500411 config->fSettings.fRemoveDeadVariables &= gSkSLDeadCodeElimination;
412 config->fSettings.fRemoveDeadFunctions &= gSkSLDeadCodeElimination;
John Stiles7247b482021-03-08 10:40:35 -0500413
414 // Disable optimization settings that depend on a parent setting which has been disabled.
John Stiles7247b482021-03-08 10:40:35 -0500415 config->fSettings.fInlineThreshold *= (int)config->fSettings.fOptimize;
John Stiles0bfeae62021-03-11 09:09:42 -0500416 config->fSettings.fRemoveDeadFunctions &= config->fSettings.fOptimize;
417 config->fSettings.fRemoveDeadVariables &= config->fSettings.fOptimize;
John Stiles7247b482021-03-08 10:40:35 -0500418
ethannicholasb3058bd2016-07-01 08:22:01 -0700419 fErrorText = "";
420 fErrorCount = 0;
John Stilesd1204642021-02-17 16:30:02 -0500421 fInliner.reset(fIRGenerator->fModifiers.get());
Brian Osman88cda172020-10-09 12:05:16 -0400422
423 // Not using AutoSource, because caller is likely to call errorText() if we fail to compile
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700424 std::unique_ptr<String> textPtr(new String(std::move(text)));
425 fSource = textPtr.get();
Brian Osman88cda172020-10-09 12:05:16 -0400426
John Stiles5c7bb322020-10-22 11:09:15 -0400427 // Enable node pooling while converting and optimizing the program for a performance boost.
428 // The Program will take ownership of the pool.
Brian Osman28f702c2021-02-02 11:52:07 -0500429 std::unique_ptr<Pool> pool;
John Stilesc1a98b82021-02-24 13:35:02 -0500430 if (fContext->fCaps.useNodePools()) {
Brian Osman28f702c2021-02-02 11:52:07 -0500431 pool = Pool::Create();
432 pool->attachToThread();
433 }
John Stilesd1204642021-02-17 16:30:02 -0500434 IRGenerator::IRBundle ir = fIRGenerator->convertProgram(baseModule, /*isBuiltinCode=*/false,
435 textPtr->c_str(), textPtr->size(),
436 externalFunctions);
John Stiles270cec22021-02-17 12:59:36 -0500437 auto program = std::make_unique<Program>(std::move(textPtr),
438 std::move(config),
John Stiles5c7bb322020-10-22 11:09:15 -0400439 fContext,
440 std::move(ir.fElements),
Brian Osman133724c2020-10-28 14:14:39 -0400441 std::move(ir.fSharedElements),
John Stiles5c7bb322020-10-22 11:09:15 -0400442 std::move(ir.fModifiers),
443 std::move(ir.fSymbolTable),
444 std::move(pool),
445 ir.fInputs);
446 bool success = false;
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500447 if (fErrorCount) {
John Stiles5c7bb322020-10-22 11:09:15 -0400448 // Do not return programs that failed to compile.
John Stiles7247b482021-03-08 10:40:35 -0500449 } else if (!this->optimize(*program)) {
John Stiles5c7bb322020-10-22 11:09:15 -0400450 // Do not return programs that failed to optimize.
451 } else {
452 // We have a successful program!
453 success = true;
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500454 }
John Stiles5c7bb322020-10-22 11:09:15 -0400455
Brian Osman28f702c2021-02-02 11:52:07 -0500456 if (program->fPool) {
457 program->fPool->detachFromThread();
458 }
John Stiles5c7bb322020-10-22 11:09:15 -0400459 return success ? std::move(program) : nullptr;
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500460}
461
John Stilesbb1505f2021-02-12 09:17:53 -0500462void Compiler::verifyStaticTests(const Program& program) {
463 class StaticTestVerifier : public ProgramVisitor {
464 public:
465 StaticTestVerifier(ErrorReporter* r) : fReporter(r) {}
466
467 using ProgramVisitor::visitProgramElement;
468
469 bool visitStatement(const Statement& stmt) override {
470 switch (stmt.kind()) {
471 case Statement::Kind::kIf:
472 if (stmt.as<IfStatement>().isStatic()) {
473 fReporter->error(stmt.fOffset, "static if has non-static test");
474 }
475 break;
476
477 case Statement::Kind::kSwitch:
478 if (stmt.as<SwitchStatement>().isStatic()) {
479 fReporter->error(stmt.fOffset, "static switch has non-static test");
480 }
481 break;
482
483 default:
484 break;
485 }
486 return INHERITED::visitStatement(stmt);
487 }
488
John Stiles59e34562021-02-12 16:56:39 -0500489 bool visitExpression(const Expression&) override {
490 // We aren't looking for anything inside an Expression, so skip them entirely.
491 return false;
492 }
493
John Stilesbb1505f2021-02-12 09:17:53 -0500494 private:
495 using INHERITED = ProgramVisitor;
496 ErrorReporter* fReporter;
497 };
498
499 // If invalid static tests are permitted, we don't need to check anything.
John Stilesd1204642021-02-17 16:30:02 -0500500 if (fContext->fConfig->fSettings.fPermitInvalidStaticTests) {
John Stilesbb1505f2021-02-12 09:17:53 -0500501 return;
502 }
503
504 // Check all of the program's owned elements. (Built-in elements are assumed to be valid.)
505 StaticTestVerifier visitor{this};
506 for (const std::unique_ptr<ProgramElement>& element : program.ownedElements()) {
507 if (element->is<FunctionDefinition>()) {
508 visitor.visitProgramElement(*element);
509 }
510 }
511}
512
Brian Osman0006ad02020-11-18 15:38:39 -0500513bool Compiler::optimize(LoadedModule& module) {
514 SkASSERT(!fErrorCount);
Brian Osman0006ad02020-11-18 15:38:39 -0500515
John Stiles270cec22021-02-17 12:59:36 -0500516 // Create a temporary program configuration with default settings.
517 ProgramConfig config;
518 config.fKind = module.fKind;
John Stilesa935c3f2021-02-25 10:35:49 -0500519 AutoProgramConfig autoConfig(fContext, &config);
John Stiles270cec22021-02-17 12:59:36 -0500520
John Stilesd1204642021-02-17 16:30:02 -0500521 // Reset the Inliner.
522 fInliner.reset(fModifiers.back().get());
John Stiles270cec22021-02-17 12:59:36 -0500523
524 std::unique_ptr<ProgramUsage> usage = Analysis::GetUsage(module);
Brian Osman0006ad02020-11-18 15:38:39 -0500525
526 while (fErrorCount == 0) {
Brian Osman0006ad02020-11-18 15:38:39 -0500527 // Perform inline-candidate analysis and inline any functions deemed suitable.
John Stilesf3a28db2021-03-10 23:00:47 -0500528 if (!fInliner.analyze(module.fElements, module.fSymbols, usage.get())) {
Brian Osman0006ad02020-11-18 15:38:39 -0500529 break;
530 }
531 }
532 return fErrorCount == 0;
533}
534
John Stiles0bfeae62021-03-11 09:09:42 -0500535bool Compiler::removeDeadFunctions(Program& program, ProgramUsage* usage) {
536 bool madeChanges = false;
537
538 if (program.fConfig->fSettings.fRemoveDeadFunctions) {
539 auto isDeadFunction = [&](const ProgramElement* element) {
540 if (!element->is<FunctionDefinition>()) {
541 return false;
542 }
543 const FunctionDefinition& fn = element->as<FunctionDefinition>();
544 if (fn.declaration().name() == "main" || usage->get(fn.declaration()) > 0) {
545 return false;
546 }
547 usage->remove(*element);
548 madeChanges = true;
549 return true;
550 };
551
552 program.fElements.erase(std::remove_if(program.fElements.begin(),
553 program.fElements.end(),
554 [&](const std::unique_ptr<ProgramElement>& element) {
555 return isDeadFunction(element.get());
556 }),
557 program.fElements.end());
558 program.fSharedElements.erase(std::remove_if(program.fSharedElements.begin(),
559 program.fSharedElements.end(),
560 isDeadFunction),
561 program.fSharedElements.end());
562 }
563 return madeChanges;
564}
565
566bool Compiler::removeDeadGlobalVariables(Program& program, ProgramUsage* usage) {
567 bool madeChanges = false;
568
569 if (program.fConfig->fSettings.fRemoveDeadVariables) {
570 auto isDeadVariable = [&](const ProgramElement* element) {
571 if (!element->is<GlobalVarDeclaration>()) {
572 return false;
573 }
574 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
575 const VarDeclaration& varDecl = global.declaration()->as<VarDeclaration>();
576 if (!usage->isDead(varDecl.var())) {
577 return false;
578 }
579 madeChanges = true;
580 return true;
581 };
582
583 program.fElements.erase(std::remove_if(program.fElements.begin(),
584 program.fElements.end(),
585 [&](const std::unique_ptr<ProgramElement>& element) {
586 return isDeadVariable(element.get());
587 }),
588 program.fElements.end());
589 program.fSharedElements.erase(std::remove_if(program.fSharedElements.begin(),
590 program.fSharedElements.end(),
591 isDeadVariable),
592 program.fSharedElements.end());
593 }
594 return madeChanges;
595}
596
John Stiles26541872021-03-16 12:19:54 -0400597bool Compiler::removeDeadLocalVariables(Program& program, ProgramUsage* usage) {
598 class DeadLocalVariableEliminator : public ProgramWriter {
599 public:
600 DeadLocalVariableEliminator(const Context& context, ProgramUsage* usage)
601 : fContext(context)
602 , fUsage(usage) {}
603
604 using ProgramWriter::visitProgramElement;
605
606 bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
607 // We don't need to look inside expressions at all.
608 return false;
609 }
610
611 bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
612 if (stmt->is<VarDeclaration>()) {
613 VarDeclaration& varDecl = stmt->as<VarDeclaration>();
614 const Variable* var = &varDecl.var();
615 ProgramUsage::VariableCounts* counts = fUsage->fVariableCounts.find(var);
616 SkASSERT(counts);
617 SkASSERT(counts->fDeclared);
618 if (CanEliminate(var, *counts)) {
619 if (var->initialValue()) {
620 // The variable has an initial-value expression, which might have side
621 // effects. ExpressionStatement::Make will preserve side effects, but
622 // replaces pure expressions with Nop.
623 fUsage->remove(stmt.get());
624 stmt = ExpressionStatement::Make(fContext, std::move(varDecl.value()));
625 fUsage->add(stmt.get());
626 } else {
627 // The variable has no initial-value and can be cleanly eliminated.
628 fUsage->remove(stmt.get());
629 stmt = std::make_unique<Nop>();
630 }
631 fMadeChanges = true;
632 }
633 return false;
634 }
635 return INHERITED::visitStatementPtr(stmt);
636 }
637
638 static bool CanEliminate(const Variable* var, const ProgramUsage::VariableCounts& counts) {
639 if (!counts.fDeclared || counts.fRead || var->storage() != VariableStorage::kLocal) {
640 return false;
641 }
642 if (var->initialValue()) {
643 SkASSERT(counts.fWrite >= 1);
644 return counts.fWrite == 1;
645 } else {
646 return counts.fWrite == 0;
647 }
648 }
649
650 bool fMadeChanges = false;
651 const Context& fContext;
652 ProgramUsage* fUsage;
653
654 using INHERITED = ProgramWriter;
655 };
656
657 DeadLocalVariableEliminator visitor{*fContext, usage};
658
659 if (program.fConfig->fSettings.fRemoveDeadVariables) {
660 for (auto& [var, counts] : usage->fVariableCounts) {
661 if (DeadLocalVariableEliminator::CanEliminate(var, counts)) {
662 // This program contains at least one dead local variable.
663 // Scan the program for any dead local variables and eliminate them all.
664 for (std::unique_ptr<ProgramElement>& pe : program.ownedElements()) {
665 if (pe->is<FunctionDefinition>()) {
666 visitor.visitProgramElement(*pe);
667 }
668 }
669 break;
670 }
671 }
672 }
673
674 return visitor.fMadeChanges;
675}
676
Ethan Nicholas00543112018-07-31 09:44:36 -0400677bool Compiler::optimize(Program& program) {
John Stiles7247b482021-03-08 10:40:35 -0500678 // The optimizer only needs to run when it is enabled.
679 if (!program.fConfig->fSettings.fOptimize) {
680 return true;
681 }
682
Ethan Nicholas00543112018-07-31 09:44:36 -0400683 SkASSERT(!fErrorCount);
Brian Osman010ce6a2020-10-19 16:34:10 -0400684 ProgramUsage* usage = program.fUsage.get();
John Stiles7954d6c2020-09-01 10:53:02 -0400685
John Stilesb6664582021-03-19 09:46:00 -0400686 if (fErrorCount == 0) {
687 // Run the inliner only once; it is expensive! Multiple passes can occasionally shake out
688 // more wins, but it's diminishing returns.
689 fInliner.analyze(program.ownedElements(), program.fSymbols, usage);
Ethan Nicholas34b19c52020-09-14 11:33:47 -0400690
John Stilesb6664582021-03-19 09:46:00 -0400691 while (this->removeDeadFunctions(program, usage)) {
692 // Removing dead functions may cause more functions to become unreferenced. Try again.
Ethan Nicholas34b19c52020-09-14 11:33:47 -0400693 }
John Stilesb6664582021-03-19 09:46:00 -0400694 while (this->removeDeadLocalVariables(program, usage)) {
695 // Removing dead variables may cause more variables to become unreferenced. Try again.
696 }
697 if (program.fConfig->fKind != ProgramKind::kFragmentProcessor) {
698 this->removeDeadGlobalVariables(program, usage);
Ethan Nicholas0dc80872019-02-08 15:46:24 -0500699 }
Ethan Nicholas00543112018-07-31 09:44:36 -0400700 }
John Stilesbb1505f2021-02-12 09:17:53 -0500701
702 if (fErrorCount == 0) {
703 this->verifyStaticTests(program);
704 }
705
Ethan Nicholas00543112018-07-31 09:44:36 -0400706 return fErrorCount == 0;
707}
708
Brian Osmanfb32ddf2019-06-18 10:14:20 -0400709#if defined(SKSL_STANDALONE) || SK_SUPPORT_GPU
710
Ethan Nicholas00543112018-07-31 09:44:36 -0400711bool Compiler::toSPIRV(Program& program, OutputStream& out) {
Brian Osman7a20b5c2021-03-15 16:23:33 -0400712 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toSPIRV");
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -0400713#ifdef SK_ENABLE_SPIRV_VALIDATION
Ethan Nicholas0df1b042017-03-31 13:56:23 -0400714 StringStream buffer;
Brian Osman88cda172020-10-09 12:05:16 -0400715 AutoSource as(this, program.fSource.get());
Brian Osman8b43dad2020-10-09 13:31:42 -0400716 SPIRVCodeGenerator cg(fContext.get(), &program, this, &buffer);
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -0400717 bool result = cg.generateCode();
John Stiles270cec22021-02-17 12:59:36 -0500718 if (result && program.fConfig->fSettings.fValidateSPIRV) {
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -0400719 spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
Ethan Nicholas762466e2017-06-29 10:03:38 -0400720 const String& data = buffer.str();
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400721 SkASSERT(0 == data.size() % 4);
Brian Osman8d09d4a2020-11-24 15:51:06 -0500722 String errors;
723 auto dumpmsg = [&errors](spv_message_level_t, const char*, const spv_position_t&,
724 const char* m) {
725 errors.appendf("SPIR-V validation error: %s\n", m);
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -0400726 };
727 tools.SetMessageConsumer(dumpmsg);
Brian Osman8d09d4a2020-11-24 15:51:06 -0500728
729 // Verify that the SPIR-V we produced is valid. At runtime, we will abort() with a message
730 // explaining the error. In standalone mode (skslc), we will send the message, plus the
731 // entire disassembled SPIR-V (for easier context & debugging) as *our* error message.
732 result = tools.Validate((const uint32_t*) data.c_str(), data.size() / 4);
733
734 if (!result) {
735#if defined(SKSL_STANDALONE)
736 // Convert the string-stream to a SPIR-V disassembly.
737 std::string disassembly;
738 if (tools.Disassemble((const uint32_t*)data.data(), data.size() / 4, &disassembly)) {
739 errors.append(disassembly);
740 }
741 this->error(-1, errors);
742#else
743 SkDEBUGFAILF("%s", errors.c_str());
744#endif
745 }
Ethan Nicholas762466e2017-06-29 10:03:38 -0400746 out.write(data.c_str(), data.size());
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -0400747 }
748#else
Brian Osman88cda172020-10-09 12:05:16 -0400749 AutoSource as(this, program.fSource.get());
Brian Osman8b43dad2020-10-09 13:31:42 -0400750 SPIRVCodeGenerator cg(fContext.get(), &program, this, &out);
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500751 bool result = cg.generateCode();
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -0400752#endif
Ethan Nicholasce33f102016-12-09 17:22:59 -0500753 return result;
754}
755
Ethan Nicholas00543112018-07-31 09:44:36 -0400756bool Compiler::toSPIRV(Program& program, String* out) {
Ethan Nicholas0df1b042017-03-31 13:56:23 -0400757 StringStream buffer;
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500758 bool result = this->toSPIRV(program, buffer);
759 if (result) {
Ethan Nicholas762466e2017-06-29 10:03:38 -0400760 *out = buffer.str();
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500761 }
762 return result;
763}
764
Ethan Nicholas00543112018-07-31 09:44:36 -0400765bool Compiler::toGLSL(Program& program, OutputStream& out) {
Brian Osman7a20b5c2021-03-15 16:23:33 -0400766 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toGLSL");
Brian Osman88cda172020-10-09 12:05:16 -0400767 AutoSource as(this, program.fSource.get());
Ethan Nicholas26a9aad2018-03-27 14:10:52 -0400768 GLSLCodeGenerator cg(fContext.get(), &program, this, &out);
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500769 bool result = cg.generateCode();
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500770 return result;
771}
772
Ethan Nicholas00543112018-07-31 09:44:36 -0400773bool Compiler::toGLSL(Program& program, String* out) {
Ethan Nicholas0df1b042017-03-31 13:56:23 -0400774 StringStream buffer;
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500775 bool result = this->toGLSL(program, buffer);
776 if (result) {
Ethan Nicholas762466e2017-06-29 10:03:38 -0400777 *out = buffer.str();
Ethan Nicholas941e7e22016-12-12 15:33:30 -0500778 }
779 return result;
780}
781
Brian Osmanc0243912020-02-19 15:35:26 -0500782bool Compiler::toHLSL(Program& program, String* out) {
783 String spirv;
784 if (!this->toSPIRV(program, &spirv)) {
785 return false;
786 }
787
788 return SPIRVtoHLSL(spirv, out);
789}
790
Ethan Nicholas00543112018-07-31 09:44:36 -0400791bool Compiler::toMetal(Program& program, OutputStream& out) {
Brian Osman7a20b5c2021-03-15 16:23:33 -0400792 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toMetal");
Ethan Nicholas26a9aad2018-03-27 14:10:52 -0400793 MetalCodeGenerator cg(fContext.get(), &program, this, &out);
Ethan Nicholascc305772017-10-13 16:17:45 -0400794 bool result = cg.generateCode();
Ethan Nicholascc305772017-10-13 16:17:45 -0400795 return result;
796}
797
Ethan Nicholas00543112018-07-31 09:44:36 -0400798bool Compiler::toMetal(Program& program, String* out) {
Timothy Liangb8eeb802018-07-23 16:46:16 -0400799 StringStream buffer;
800 bool result = this->toMetal(program, buffer);
801 if (result) {
802 *out = buffer.str();
803 }
804 return result;
805}
806
Greg Daniela28ea672020-09-25 11:12:56 -0400807#if defined(SKSL_STANDALONE) || GR_TEST_UTILS
Ethan Nicholas00543112018-07-31 09:44:36 -0400808bool Compiler::toCPP(Program& program, String name, OutputStream& out) {
Brian Osman88cda172020-10-09 12:05:16 -0400809 AutoSource as(this, program.fSource.get());
Ethan Nicholas26a9aad2018-03-27 14:10:52 -0400810 CPPCodeGenerator cg(fContext.get(), &program, this, name, &out);
Ethan Nicholas762466e2017-06-29 10:03:38 -0400811 bool result = cg.generateCode();
Ethan Nicholas762466e2017-06-29 10:03:38 -0400812 return result;
813}
814
Ethan Nicholas00543112018-07-31 09:44:36 -0400815bool Compiler::toH(Program& program, String name, OutputStream& out) {
Brian Osman88cda172020-10-09 12:05:16 -0400816 AutoSource as(this, program.fSource.get());
Ethan Nicholas26a9aad2018-03-27 14:10:52 -0400817 HCodeGenerator cg(fContext.get(), &program, this, name, &out);
Ethan Nicholas762466e2017-06-29 10:03:38 -0400818 bool result = cg.generateCode();
Ethan Nicholas00543112018-07-31 09:44:36 -0400819 return result;
820}
Greg Daniela28ea672020-09-25 11:12:56 -0400821#endif // defined(SKSL_STANDALONE) || GR_TEST_UTILS
Ethan Nicholas00543112018-07-31 09:44:36 -0400822
Ethan Nicholas2a479a52020-08-18 16:29:45 -0400823#endif // defined(SKSL_STANDALONE) || SK_SUPPORT_GPU
Brian Osman2e29ab52019-09-20 12:19:11 -0400824
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700825Position Compiler::position(int offset) {
Ethan Nicholas3c729892020-12-07 12:47:17 -0500826 if (fSource && offset >= 0) {
827 int line = 1;
828 int column = 1;
829 for (int i = 0; i < offset; i++) {
830 if ((*fSource)[i] == '\n') {
831 ++line;
832 column = 1;
833 }
834 else {
835 ++column;
836 }
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700837 }
Ethan Nicholas3c729892020-12-07 12:47:17 -0500838 return Position(line, column);
839 } else {
840 return Position(-1, -1);
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700841 }
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700842}
843
844void Compiler::error(int offset, String msg) {
ethannicholasb3058bd2016-07-01 08:22:01 -0700845 fErrorCount++;
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700846 Position pos = this->position(offset);
John Stiles8d3642e2021-01-22 09:50:04 -0500847 fErrorTextLength.push_back(fErrorText.length());
Ethan Nicholas3c729892020-12-07 12:47:17 -0500848 fErrorText += "error: " + (pos.fLine >= 1 ? to_string(pos.fLine) + ": " : "") + msg + "\n";
ethannicholasb3058bd2016-07-01 08:22:01 -0700849}
850
John Stiles8d3642e2021-01-22 09:50:04 -0500851void Compiler::setErrorCount(int c) {
852 if (c < fErrorCount) {
853 fErrorText.resize(fErrorTextLength[c]);
854 fErrorTextLength.resize(c);
855 fErrorCount = c;
856 }
857}
858
Ethan Nicholas95046142021-01-07 10:57:27 -0500859String Compiler::errorText(bool showCount) {
860 if (showCount) {
861 this->writeErrorCount();
862 }
Ethan Nicholas00543112018-07-31 09:44:36 -0400863 fErrorCount = 0;
Ethan Nicholas0df1b042017-03-31 13:56:23 -0400864 String result = fErrorText;
Ethan Nicholas95046142021-01-07 10:57:27 -0500865 fErrorText = "";
ethannicholasb3058bd2016-07-01 08:22:01 -0700866 return result;
867}
868
869void Compiler::writeErrorCount() {
870 if (fErrorCount) {
871 fErrorText += to_string(fErrorCount) + " error";
872 if (fErrorCount > 1) {
873 fErrorText += "s";
874 }
875 fErrorText += "\n";
876 }
877}
878
John Stilesa6841be2020-08-06 14:11:56 -0400879} // namespace SkSL