Flutter Engine
The Flutter Engine
Namespaces | Functions | Variables
SkSLMinify.cpp File Reference
#include "src/base/SkStringView.h"
#include "src/core/SkOpts.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLFileOutputStream.h"
#include "src/sksl/SkSLLexer.h"
#include "src/sksl/SkSLModuleLoader.h"
#include "src/sksl/SkSLProgramKind.h"
#include "src/sksl/SkSLProgramSettings.h"
#include "src/sksl/SkSLUtil.h"
#include "src/sksl/ir/SkSLStructDefinition.h"
#include "src/sksl/ir/SkSLSymbolTable.h"
#include "src/sksl/transform/SkSLTransform.h"
#include "src/utils/SkOSPath.h"
#include "tools/SkGetExecutablePath.h"
#include "tools/skslc/ProcessWorklist.h"
#include <cctype>
#include <forward_list>
#include <fstream>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>

Go to the source code of this file.

Namespaces

namespace  SkOpts
 

Functions

void SkDebugf (const char format[],...)
 
static std::string base_name (const std::string &path)
 
static std::string remove_extension (const std::string &path)
 
static void show_usage ()
 
static std::string_view stringize (const SkSL::Token &token, std::string_view text)
 
static bool maybe_identifier (char c)
 
static bool is_plus_or_minus (char c)
 
static std::forward_list< std::unique_ptr< const SkSL::Module > > compile_module_list (SkSpan< const std::string > paths, SkSL::ProgramKind kind)
 
static bool generate_minified_text (std::string_view inputPath, std::string_view text, SkSL::FileOutputStream &out)
 
static bool find_boolean_flag (SkSpan< std::string > *args, std::string_view flagName)
 
static bool has_overlapping_flags (SkSpan< const bool > flags)
 
static ResultCode process_command (SkSpan< std::string > args)
 
int main (int argc, const char **argv)
 

Variables

static bool gUnoptimized = false
 
static bool gStringify = false
 
static SkSL::ProgramKind gProgramKind = SkSL::ProgramKind::kFragment
 

Function Documentation

◆ base_name()

static std::string base_name ( const std::string &  path)
static

Definition at line 46 of file SkSLMinify.cpp.

46 {
47 size_t slashPos = path.find_last_of("/\\");
48 return path.substr(slashPos == std::string::npos ? 0 : slashPos + 1);
49}
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57

◆ compile_module_list()

static std::forward_list< std::unique_ptr< const SkSL::Module > > compile_module_list ( SkSpan< const std::string >  paths,
SkSL::ProgramKind  kind 
)
static

Definition at line 76 of file SkSLMinify.cpp.

77 {
78 std::forward_list<std::unique_ptr<const SkSL::Module>> modules;
79
80 // If we are compiling a Runtime Effect...
82 // ... the parent modules still need to be compiled as Fragment programs.
83 // If no modules are explicitly specified, we automatically include the built-in modules for
84 // runtime effects (sksl_shared, sksl_public) so that casual users don't need to always
85 // remember to specify these modules.
86 if (paths.size() == 1) {
87 const std::string minifyDir = SkOSPath::Dirname(SkGetExecutablePath().c_str()).c_str();
88 std::string defaultRuntimeShaderPaths[] = {
89 minifyDir + SkOSPath::SEPARATOR + "sksl_public.sksl",
90 minifyDir + SkOSPath::SEPARATOR + "sksl_shared.sksl",
91 };
92 modules = compile_module_list(defaultRuntimeShaderPaths, SkSL::ProgramKind::kFragment);
93 } else {
94 // The parent modules were listed on the command line; we need to compile them as
95 // fragment programs. The final module keeps the Runtime Shader program-kind.
97 paths = paths.first(1);
98 }
99 // Set up the public type aliases so that Runtime Shader code with GLSL types works as-is.
101 }
102
103 // Load in each input as a module, from right to left.
104 // Each module inherits the symbols from its parent module.
106 for (auto modulePath = paths.rbegin(); modulePath != paths.rend(); ++modulePath) {
107 std::ifstream in(*modulePath);
108 std::string moduleSource{std::istreambuf_iterator<char>(in),
109 std::istreambuf_iterator<char>()};
110 if (in.rdstate()) {
111 printf("error reading '%s'\n", modulePath->c_str());
112 return {};
113 }
114
115 const SkSL::Module* parent = modules.empty() ? SkSL::ModuleLoader::Get().rootModule()
116 : modules.front().get();
117 std::unique_ptr<SkSL::Module> m = compiler.compileModule(kind,
118 modulePath->c_str(),
119 std::move(moduleSource),
120 parent,
121 /*shouldInline=*/false);
122 if (!m) {
123 return {};
124 }
125 // We need to optimize every module in the chain. We rename private functions at global
126 // scope, and we need to make sure there are no name collisions between nested modules.
127 // (i.e., if module A claims names `$a` and `$b` at global scope, module B will need to
128 // start at `$c`. The most straightforward way to handle this is to actually perform the
129 // renames.)
130 compiler.optimizeModuleBeforeMinifying(kind, *m, /*shrinkSymbols=*/!gUnoptimized);
131 modules.push_front(std::move(m));
132 }
133 // Return all of the modules to transfer their ownership to the caller.
134 return modules;
135}
std::string SkGetExecutablePath()
static std::forward_list< std::unique_ptr< const SkSL::Module > > compile_module_list(SkSpan< const std::string > paths, SkSL::ProgramKind kind)
Definition: SkSLMinify.cpp:76
static bool gUnoptimized
Definition: SkSLMinify.cpp:31
static constexpr char SEPARATOR
Definition: SkOSPath.h:21
static SkString Dirname(const char *fullPath)
Definition: SkOSPath.cpp:36
const Module * rootModule()
void addPublicTypeAliases(const SkSL::Module *module)
static ModuleLoader Get()
constexpr SkSpan< T > first(size_t prefixLen) const
Definition: SkSpan_impl.h:98
constexpr SkSpan< T > subspan(size_t offset) const
Definition: SkSpan_impl.h:105
constexpr auto rbegin() const
Definition: SkSpan_impl.h:92
constexpr auto rend() const
Definition: SkSpan_impl.h:93
constexpr size_t size() const
Definition: SkSpan_impl.h:95
const char * c_str() const
Definition: SkString.h:133
std::string printf(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: SkSLString.cpp:83
compiler
Definition: malisc.py:17
static bool IsRuntimeEffect(ProgramKind kind)

◆ find_boolean_flag()

static bool find_boolean_flag ( SkSpan< std::string > *  args,
std::string_view  flagName 
)
static

Definition at line 210 of file SkSLMinify.cpp.

210 {
211 size_t startingCount = args->size();
212 auto iter = std::remove_if(args->begin(), args->end(),
213 [&](const std::string& a) { return a == flagName; });
214 *args = args->subspan(0, std::distance(args->begin(), iter));
215 return args->size() < startingCount;
216}
struct MyStruct a[10]
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args

◆ generate_minified_text()

static bool generate_minified_text ( std::string_view  inputPath,
std::string_view  text,
SkSL::FileOutputStream out 
)
static

Definition at line 137 of file SkSLMinify.cpp.

139 {
140 using TokenKind = SkSL::Token::Kind;
141
142 SkSL::Lexer lexer;
143 lexer.start(text);
144
145 SkSL::Token token;
146 std::string_view lastTokenText = " ";
147 int lineWidth = 1;
148 for (;;) {
149 token = lexer.next();
150 if (token.fKind == TokenKind::TK_END_OF_FILE) {
151 break;
152 }
153 if (token.fKind == TokenKind::TK_LINE_COMMENT ||
154 token.fKind == TokenKind::TK_BLOCK_COMMENT ||
155 token.fKind == TokenKind::TK_WHITESPACE) {
156 continue;
157 }
158 std::string_view thisTokenText = stringize(token, text);
159 if (token.fKind == TokenKind::TK_INVALID) {
160 printf("%.*s: unable to parse '%.*s' at offset %d\n",
161 (int)inputPath.size(), inputPath.data(),
162 (int)thisTokenText.size(), thisTokenText.data(),
163 token.fOffset);
164 return false;
165 }
166 if (thisTokenText.empty()) {
167 continue;
168 }
169 if (token.fKind == TokenKind::TK_FLOAT_LITERAL) {
170 // We can reduce `3.0` to `3.` safely.
171 if (skstd::contains(thisTokenText, '.')) {
172 while (thisTokenText.back() == '0' && thisTokenText.size() >= 3) {
173 thisTokenText.remove_suffix(1);
174 }
175 }
176 // We can reduce `0.5` to `.5` safely.
177 if (skstd::starts_with(thisTokenText, "0.") && thisTokenText.size() >= 3) {
178 thisTokenText.remove_prefix(1);
179 }
180 }
181 SkASSERT(!lastTokenText.empty());
182 if (gStringify && lineWidth > 75) {
183 // We're getting full-ish; wrap to a new line.
184 out.writeText("\"\n\"");
185 lineWidth = 1;
186 }
187
188 // Detect tokens with abutting alphanumeric characters side-by-side.
189 bool adjacentIdentifiers =
190 maybe_identifier(lastTokenText.back()) && maybe_identifier(thisTokenText.front());
191
192 // Detect potentially ambiguous preincrement/postincrement operators.
193 // For instance, `x + ++y` and `x++ + y` require whitespace for differentiation.
194 bool adjacentPlusOrMinus =
195 is_plus_or_minus(lastTokenText.back()) && is_plus_or_minus(thisTokenText.front());
196
197 // Insert whitespace when it is necessary for program correctness.
198 if (adjacentIdentifiers || adjacentPlusOrMinus) {
199 out.writeText(" ");
200 lineWidth++;
201 }
202 out.write(thisTokenText.data(), thisTokenText.size());
203 lineWidth += thisTokenText.size();
204 lastTokenText = thisTokenText;
205 }
206
207 return true;
208}
#define SkASSERT(cond)
Definition: SkAssert.h:116
static std::string_view stringize(const SkSL::Token &token, std::string_view text)
Definition: SkSLMinify.cpp:64
static bool maybe_identifier(char c)
Definition: SkSLMinify.cpp:68
static bool is_plus_or_minus(char c)
Definition: SkSLMinify.cpp:72
static bool gStringify
Definition: SkSLMinify.cpp:32
void start(std::string_view text)
Definition: SkSLLexer.h:125
Token next()
Definition: SkSLLexer.cpp:800
std::u16string text
constexpr bool starts_with(std::string_view str, std::string_view prefix)
Definition: SkStringView.h:17
constexpr bool contains(std::string_view str, std::string_view needle)
Definition: SkStringView.h:41
int32_t fOffset
Definition: SkSLLexer.h:119
Kind fKind
Definition: SkSLLexer.h:118

◆ has_overlapping_flags()

static bool has_overlapping_flags ( SkSpan< const bool >  flags)
static

Definition at line 218 of file SkSLMinify.cpp.

218 {
219 // Returns true if more than one boolean is set.
220 return std::count(flags.begin(), flags.end(), true) > 1;
221}
int count
Definition: FontMgrTest.cpp:50
FlutterSemanticsFlag flags

◆ is_plus_or_minus()

static bool is_plus_or_minus ( char  c)
static

Definition at line 72 of file SkSLMinify.cpp.

72 {
73 return c == '+' || c == '-';
74}

◆ main()

int main ( int  argc,
const char **  argv 
)

Definition at line 326 of file SkSLMinify.cpp.

326 {
327 if (argc == 2) {
328 // Worklists are the only two-argument case for sksl-minify, and we don't intend to support
329 // nested worklists, so we can process them here.
330 return (int)ProcessWorklist(argv[1], process_command);
331 } else {
332 // Process non-worklist inputs.
333 std::vector<std::string> args;
334 for (int index=0; index<argc; ++index) {
335 args.push_back(argv[index]);
336 }
337
338 return (int)process_command(args);
339 }
340}
ResultCode ProcessWorklist(const char *worklistPath, const std::function< ResultCode(SkSpan< std::string > args)> &processCommandFn)
static ResultCode process_command(SkSpan< std::string > args)
Definition: SkSLMinify.cpp:223
char ** argv
Definition: library.h:9

◆ maybe_identifier()

static bool maybe_identifier ( char  c)
static

Definition at line 68 of file SkSLMinify.cpp.

68 {
69 return std::isalnum(c) || c == '$' || c == '_';
70}

◆ process_command()

static ResultCode process_command ( SkSpan< std::string >  args)
static

Definition at line 223 of file SkSLMinify.cpp.

223 {
224 // Ignore the process name.
225 SkASSERT(!args.empty());
226 args = args.subspan(1);
227
228 // Process command line flags.
229 gUnoptimized = find_boolean_flag(&args, "--unoptimized");
230 gStringify = find_boolean_flag(&args, "--stringify");
231 bool isFrag = find_boolean_flag(&args, "--frag");
232 bool isVert = find_boolean_flag(&args, "--vert");
233 bool isCompute = find_boolean_flag(&args, "--compute");
234 bool isShader = find_boolean_flag(&args, "--shader");
235 bool isPrivateShader = find_boolean_flag(&args, "--privshader");
236 bool isColorFilter = find_boolean_flag(&args, "--colorfilter");
237 bool isBlender = find_boolean_flag(&args, "--blender");
238 bool isMeshFrag = find_boolean_flag(&args, "--meshfrag");
239 bool isMeshVert = find_boolean_flag(&args, "--meshvert");
240 if (has_overlapping_flags({isFrag, isVert, isCompute, isShader, isColorFilter,
241 isBlender, isMeshFrag, isMeshVert})) {
242 show_usage();
244 }
245 if (isFrag) {
247 } else if (isVert) {
249 } else if (isCompute) {
251 } else if (isColorFilter) {
253 } else if (isBlender) {
255 } else if (isMeshFrag) {
257 } else if (isMeshVert) {
259 } else if (isPrivateShader) {
261 } else {
262 // Default case, if no option is specified.
264 }
265
266 // We expect, at a minimum, an output path and one or more input paths.
267 if (args.size() < 2) {
268 show_usage();
270 }
271 const std::string& outputPath = args[0];
272 SkSpan inputPaths = args.subspan(1);
273
274 // Compile the original SkSL from the input path.
275 std::forward_list<std::unique_ptr<const SkSL::Module>> modules =
277 if (modules.empty()) {
279 }
280 const SkSL::Module* module = modules.front().get();
281
282 // Emit the minified SkSL into our output path.
283 SkSL::FileOutputStream out(outputPath.c_str());
284 if (!out.isValid()) {
285 printf("error writing '%s'\n", outputPath.c_str());
287 }
288
289 std::string baseName = remove_extension(base_name(inputPaths.front()));
290 if (gStringify) {
291 out.printf("static constexpr char SKSL_MINIFIED_%s[] =\n\"", baseName.c_str());
292 }
293
294 // Generate the program text by getting the program's description.
295 std::string text;
296 for (const std::unique_ptr<SkSL::ProgramElement>& element : module->fElements) {
297 if ((isMeshFrag || isMeshVert) && element->is<SkSL::StructDefinition>()) {
298 std::string_view name = element->as<SkSL::StructDefinition>().type().name();
299 if (name == "Attributes" || name == "Varyings") {
300 // Don't emit the Attributes or Varyings structs from a mesh program into the
301 // minified output; those are synthesized via the SkMeshSpecification.
302 continue;
303 }
304 }
305 text += element->description();
306 }
307
308 // Eliminate whitespace and perform other basic simplifications via a lexer pass.
309 if (!generate_minified_text(inputPaths.front(), text, out)) {
311 }
312
313 if (gStringify) {
314 out.writeText("\";");
315 }
316 out.writeText("\n");
317
318 if (!out.close()) {
319 printf("error writing '%s'\n", outputPath.c_str());
321 }
322
324}
static void show_usage()
Definition: SkSLMinify.cpp:59
static std::string base_name(const std::string &path)
Definition: SkSLMinify.cpp:46
static bool find_boolean_flag(SkSpan< std::string > *args, std::string_view flagName)
Definition: SkSLMinify.cpp:210
static std::string remove_extension(const std::string &path)
Definition: SkSLMinify.cpp:51
static bool generate_minified_text(std::string_view inputPath, std::string_view text, SkSL::FileOutputStream &out)
Definition: SkSLMinify.cpp:137
static SkSL::ProgramKind gProgramKind
Definition: SkSLMinify.cpp:33
static bool has_overlapping_flags(SkSpan< const bool > flags)
Definition: SkSLMinify.cpp:218
GLenum type
constexpr T & front() const
Definition: SkSpan_impl.h:88
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
std::vector< std::unique_ptr< ProgramElement > > fElements
Definition: SkSLCompiler.h:59

◆ remove_extension()

static std::string remove_extension ( const std::string &  path)
static

Definition at line 51 of file SkSLMinify.cpp.

51 {
52 size_t dotPos = path.find_last_of('.');
53 return path.substr(0, dotPos);
54}

◆ show_usage()

static void show_usage ( )
static

Displays a usage banner; used when the command line arguments don't make sense.

Definition at line 59 of file SkSLMinify.cpp.

59 {
60 printf("usage: sksl-minify <output> <input> [--frag|--vert|--compute|--shader|"
61 "--colorfilter|--blender|--meshfrag|--meshvert] [dependencies...]\n");
62}

◆ SkDebugf()

void SkDebugf ( const char  format[],
  ... 
)

Definition at line 35 of file SkSLMinify.cpp.

35 {
36 va_list args;
38 vfprintf(stderr, format, args);
39 va_end(args);
40}
uint32_t uint32_t * format
va_start(args, format)
va_end(args)

◆ stringize()

static std::string_view stringize ( const SkSL::Token token,
std::string_view  text 
)
static

Definition at line 64 of file SkSLMinify.cpp.

64 {
65 return text.substr(token.fOffset, token.fLength);
66}
int32_t fLength
Definition: SkSLLexer.h:120

Variable Documentation

◆ gProgramKind

Definition at line 33 of file SkSLMinify.cpp.

◆ gStringify

bool gStringify = false
static

Definition at line 32 of file SkSLMinify.cpp.

◆ gUnoptimized

bool gUnoptimized = false
static

Definition at line 31 of file SkSLMinify.cpp.