Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Namespaces | Macros | Functions | Variables
SkSLMinify.cpp File Reference
#include "include/core/SkStream.h"
#include "src/base/SkStringView.h"
#include "src/core/SkCpu.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/SkSLStringStream.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 <optional>
#include <stdarg.h>
#include <stdio.h>

Go to the source code of this file.

Namespaces

namespace  SkOpts
 

Macros

#define SK_OPTS_NS   sksl_minify_standalone
 

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
 

Macro Definition Documentation

◆ SK_OPTS_NS

#define SK_OPTS_NS   sksl_minify_standalone

Definition at line 8 of file SkSLMinify.cpp.

Function Documentation

◆ base_name()

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

Definition at line 51 of file SkSLMinify.cpp.

51 {
52 size_t slashPos = path.find_last_of("/\\");
53 return path.substr(slashPos == std::string::npos ? 0 : slashPos + 1);
54}
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 81 of file SkSLMinify.cpp.

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

215 {
216 size_t startingCount = args->size();
217 auto iter = std::remove_if(args->begin(), args->end(),
218 [&](const std::string& a) { return a == flagName; });
219 *args = args->subspan(0, std::distance(args->begin(), iter));
220 return args->size() < startingCount;
221}
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 142 of file SkSLMinify.cpp.

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

◆ has_overlapping_flags()

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

Definition at line 223 of file SkSLMinify.cpp.

223 {
224 // Returns true if more than one boolean is set.
225 return std::count(flags.begin(), flags.end(), true) > 1;
226}
FlutterSemanticsFlag flags

◆ is_plus_or_minus()

static bool is_plus_or_minus ( char  c)
static

Definition at line 77 of file SkSLMinify.cpp.

77 {
78 return c == '+' || c == '-';
79}

◆ main()

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

Definition at line 328 of file SkSLMinify.cpp.

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

◆ maybe_identifier()

static bool maybe_identifier ( char  c)
static

Definition at line 73 of file SkSLMinify.cpp.

73 {
74 return std::isalnum(c) || c == '$' || c == '_';
75}

◆ process_command()

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

Definition at line 228 of file SkSLMinify.cpp.

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

◆ remove_extension()

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

Definition at line 56 of file SkSLMinify.cpp.

56 {
57 size_t dotPos = path.find_last_of('.');
58 return path.substr(0, dotPos);
59}

◆ show_usage()

static void show_usage ( )
static

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

Definition at line 64 of file SkSLMinify.cpp.

64 {
65 printf("usage: sksl-minify <output> <input> [--frag|--vert|--compute|--shader|"
66 "--colorfilter|--blender|--meshfrag|--meshvert] [dependencies...]\n");
67}

◆ SkDebugf()

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

Definition at line 40 of file SkSLMinify.cpp.

40 {
41 va_list args;
43 vfprintf(stderr, format, args);
44 va_end(args);
45}
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 69 of file SkSLMinify.cpp.

69 {
70 return text.substr(token.fOffset, token.fLength);
71}
int32_t fLength
Definition SkSLLexer.h:120

Variable Documentation

◆ gProgramKind

Definition at line 38 of file SkSLMinify.cpp.

◆ gStringify

bool gStringify = false
static

Definition at line 37 of file SkSLMinify.cpp.

◆ gUnoptimized

bool gUnoptimized = false
static

Definition at line 36 of file SkSLMinify.cpp.