Ion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
shadersourcecomposer.cc
Go to the documentation of this file.
1 
19 
20 #include <set>
21 #include <sstream>
22 #include <stack>
23 #include <string>
24 #include <vector>
25 
26 #include "ion/base/allocatable.h"
27 #include "ion/base/invalid.h"
30 #include "ion/base/stringutils.h"
32 
33 namespace ion {
34 namespace gfxutils {
35 
36 namespace {
37 
38 static bool SetAndSaveZipAssetData(const std::string& filename,
39  const std::string& source) {
40  return base::ZipAssetManager::SetFileData(filename, source) &&
42 }
43 
44 static const std::string GetZipAssetFileData(const std::string& filename) {
45  const std::string& data = base::ZipAssetManager::GetFileData(filename);
46  return base::IsInvalidReference(data) ? std::string() : data;
47 }
48 
49 } // anonymous namespace
50 
52 
58 
59 class IncludeComposer::IncludeComposerHelper : public base::Allocatable {
60  public:
61  IncludeComposerHelper(const base::Allocatable& owner,
62  const std::string& filename,
63  const SourceLoader& source_loader,
64  const SourceSaver& source_saver,
65  const SourceModificationTime& source_time,
66  bool insert_line_directives)
67  : file_to_id_(owner),
68  id_to_file_(owner),
69  filename_(filename),
70  source_loader_(source_loader),
71  source_saver_(source_saver),
72  source_time_(source_time),
73  insert_line_directives_(insert_line_directives),
74  used_files_(owner) {
75  const size_t pos = filename_.rfind('/');
76  if (pos != std::string::npos) {
77  search_path_ = filename_.substr(0, pos);
78  filename_ = filename_.substr(pos + 1, std::string::npos);
79  }
80  }
81 
82  void SetBasePath(const std::string& path) {
83  base_path_ = path;
84  }
85 
86  const std::vector<std::string> GetChangedDependencies() {
87  std::vector<std::string> changed;
88  for (FileInfoMap::iterator it = used_files_.begin();
89  it != used_files_.end(); ++it) {
90  std::chrono::system_clock::time_point timestamp;
91  const bool found = source_time_(it->first, &timestamp);
92  if (found && timestamp > it->second.timestamp) {
93  it->second.timestamp = timestamp;
94  changed.push_back(it->first);
95  }
96  }
97  return changed;
98  }
99 
100  const std::string GetSource() {
103  std::stack<InputInfo> stack;
105  std::set<std::string> file_names;
107  std::vector<std::string> output_source;
108 
110  unsigned int input_id = 1;
111 
113  file_to_id_.clear();
114  id_to_file_.clear();
115  used_files_.clear();
116 
118  stack.push(InputInfo(BuildFilename(filename_)));
119  while (!stack.empty()) {
121  InputInfo info = stack.top();
122  stack.pop();
124  if (info.lines.size() == 0) {
126  if (file_names.count(info.name) != 0) {
127  LOG(WARNING) << stack.top().name << ":" << (stack.top().line - 1U)
128  << ": Recursive $input ignored while trying to $input"
129  << " \"" << info.name << "\".\n";
130  continue;
131  }
132 
134  const std::string source = source_loader_(info.name);
136  if (source.empty())
137  continue;
138 
140  info.lines.push_back("");
141  const std::vector<std::string> lines =
142  base::SplitStringWithoutSkipping(source, "\n");
143  info.lines.insert(info.lines.end(), lines.begin(), lines.end());
144 
146  file_names.insert(info.name);
147  std::chrono::system_clock::time_point timestamp;
148  source_time_(info.name, &timestamp);
149  used_files_[info.name] = FileInfo(timestamp);
150 
152  if (file_to_id_.count(info.name) == 0) {
153  file_to_id_[info.name] = input_id;
154  id_to_file_[input_id] = info.name;
155  }
156  input_id++;
157 
159  info.id = file_to_id_[info.name];
160  info.line = 1U;
161 
162  if (insert_line_directives_ && !stack.empty()) {
165  output_source.push_back(GetLineDirectiveString(1, info.id));
166  }
167  } else if (insert_line_directives_) {
170  output_source.push_back(GetLineDirectiveString(info.line - 1, info.id));
171  }
172  if (ParseInputLines(&stack, &info, &output_source)) {
175  file_names.erase(info.name);
176  }
177  }
178 
180  return base::JoinStrings(output_source, "\n");
181  }
182 
185  const std::string GetDependencySource(const std::string& dependency) {
186  if (DependsOn(dependency)) {
187  source_time_(dependency, &used_files_[dependency].timestamp);
188  return source_loader_(dependency);
189  } else {
190  return std::string();
191  }
192  }
193 
195  bool SetDependencySource(const std::string& dependency,
196  const std::string& source) {
197  if (DependsOn(dependency)) {
198  const bool ret = source_saver_(dependency, source);
199  source_time_(dependency, &used_files_[dependency].timestamp);
200  return ret;
201  } else {
202  return false;
203  }
204  }
205 
207  bool DependsOn(const std::string& dependency) const {
208  return file_to_id_.find(dependency) != file_to_id_.end();
209  }
210 
213  const std::string GetDependencyName(unsigned int id) const {
214  std::string name;
215  IdToFileMap::const_iterator it = id_to_file_.find(id);
216  if (it != id_to_file_.end())
217  name = it->second;
218  return name;
219  }
220 
222  const std::vector<std::string> GetDependencyNames() const {
223  std::vector<std::string> names(file_to_id_.size());
224  size_t i = 0;
225  for (FileToIdMap::const_iterator it = file_to_id_.begin();
226  it != file_to_id_.end(); ++it, ++i)
227  names[i] = it->first;
228  return names;
229  }
230 
231  private:
233  struct InputInfo {
234  explicit InputInfo(const std::string& file_name)
235  : name(file_name), lines(), id(-1), line(-1) {}
236  std::string name;
237  std::vector<std::string> lines;
238  int id;
239  size_t line;
240  };
241 
243  struct FileInfo {
244  FileInfo() {}
245  explicit FileInfo(std::chrono::system_clock::time_point timestamp)
246  : timestamp(timestamp) {}
247  std::chrono::system_clock::time_point timestamp;
248  };
249 
251  typedef base::AllocMap<std::string, unsigned int> FileToIdMap;
252  typedef base::AllocMap<unsigned int, std::string> IdToFileMap;
253  typedef base::AllocMap<std::string, FileInfo> FileInfoMap;
254 
256  const std::string BuildFilename(const std::string& filename) {
257  const std::string base_path = base_path_.empty() ?
258  "" : base_path_ + '/';
259  const std::string search_path = search_path_.empty() ?
260  "" : search_path_ + '/';
261  return base_path + search_path + filename;
262  }
263 
265  std::string GetLineDirectiveString(size_t line, unsigned int file_id) {
266  std::stringstream str;
267  str << "#line " << line << " " << file_id;
268  return str.str();
269  }
270 
275  bool ParseInputLines(std::stack<InputInfo>* stack, InputInfo* info,
276  std::vector<std::string>* output_lines) {
278  for (; info->line < info->lines.size(); ++info->line) {
279  const std::string trimmed =
280  base::TrimStartAndEndWhitespace(info->lines[info->line]);
281  if (base::StartsWith(trimmed, "$input")) {
285  const size_t start_pos = trimmed.find("\"") + 1;
286  const size_t end_pos = trimmed.find_last_of("\"");
287  const std::string input_file =
288  trimmed.substr(start_pos, end_pos - start_pos);
289  const InputInfo new_info(BuildFilename(input_file));
290  if (new_info.name.empty()) {
293  LOG(WARNING) << info->name << ":" << info->line
294  << ": Invalid $input directive, perhaps missing a '\"'?";
296  continue;
297  } else {
299  ++info->line; // Mark $input line as parsed.
300  stack->push(*info);
302  stack->push(new_info);
303  return false;
304  }
305  } else {
307  output_lines->push_back(info->lines[info->line]);
308  if (insert_line_directives_) {
313  if (trimmed.find("#if") != std::string::npos ||
314  trimmed.find("#el") != std::string::npos ||
315  trimmed.find("#endif") != std::string::npos)
316  output_lines->push_back(
317  GetLineDirectiveString(info->line, info->id));
318  }
319  }
320  }
321  return true;
322  }
323 
325  FileToIdMap file_to_id_;
326  IdToFileMap id_to_file_;
327 
329  std::string filename_;
331  std::string search_path_;
333  std::string base_path_;
335  SourceLoader source_loader_;
337  SourceSaver source_saver_;
339  SourceModificationTime source_time_;
341  bool insert_line_directives_;
344  FileInfoMap used_files_;
345 };
346 
348 
353 
355 
357 
359 
364 
366  const std::string& filename, const SourceLoader& source_loader,
367  const SourceSaver& source_saver, const SourceModificationTime& source_time,
368  bool insert_line_directives)
369  : helper_(new(GetAllocator()) IncludeComposerHelper(
370  *this, filename, source_loader, source_saver, source_time,
371  insert_line_directives)) {}
373 
374 void IncludeComposer::SetBasePath(const std::string& path) {
375  helper_->SetBasePath(path);
376 }
377 
378 const std::vector<std::string> IncludeComposer::GetChangedDependencies() {
379  return helper_->GetChangedDependencies();
380 }
381 
382 const std::string IncludeComposer::GetSource() {
383  return helper_->GetSource();
384 }
385 
387  const std::string& dependency) const {
388  return helper_->GetDependencySource(dependency);
389 }
390 
391 bool IncludeComposer::SetDependencySource(const std::string& dependency,
392  const std::string& source) {
393  return helper_->SetDependencySource(dependency, source);
394 }
395 
396 bool IncludeComposer::DependsOn(const std::string& resource) const {
397  return helper_->DependsOn(resource);
398 }
399 
400 const std::string IncludeComposer::GetDependencyName(unsigned int id) const {
401  return helper_->GetDependencyName(id);
402 }
403 
404 const std::vector<std::string> IncludeComposer::GetDependencyNames() const {
405  return helper_->GetDependencyNames();
406 }
407 
409 
414 
415 ZipAssetComposer::ZipAssetComposer(const std::string& filename,
416  bool insert_line_directives)
417  : IncludeComposer(filename, GetZipAssetFileData,
418  SetAndSaveZipAssetData,
419  base::ZipAssetManager::UpdateFileIfChanged,
420  insert_line_directives) {}
421 
423 
424 } // namespace gfxutils
425 } // namespace ion
std::function< bool(const std::string &name, const std::string &source)> SourceSaver
A function that saves a string source given a filename.
bool IsInvalidReference(const T &value)
IsInvalidReference() returns true if a passed const reference of type T has an address of InvalidRefe...
Definition: invalid.h:41
bool StartsWith(const std::string &target, const std::string &start)
Returns whether target begins with start.
Definition: stringutils.h:76
const std::string & str
const std::vector< std::string > GetDependencyNames() const override
Returns a vector containing all names that this composer depends on, or an empty vector if there are ...
void SetBasePath(const std::string &path)
Sets a path that will be prepended to all files (including the top-level filename) loaded by this com...
static bool SetFileData(const std::string &filename, const std::string &source)
Sets the data of the passed filename if the manager contains it.
std::function< bool(const std::string &filename, std::chrono::system_clock::time_point *timestamp)> SourceModificationTime
A function that returns the last time the source in filename was modified.
#define LOG(severity)
Logs the streamed message unconditionally with a severity of severity.
Definition: logging.h:216
std::string TrimStartAndEndWhitespace(const std::string &target)
Removes any whitespace characters at the beginning and end of the string.
Definition: stringutils.h:155
~ShaderSourceComposer() override
The destructor is protected since this is derived from base::Referent.
const std::string GetDependencySource(const std::string &dependency) const override
Returns the source of the passed dependency.
static const std::string & GetFileData(const std::string &filename)
Returns the data of the passed filename if the manager contains it.
uint32 id
bool SetDependencySource(const std::string &dependency, const std::string &source) override
Requests that the composer set the source of the dependency.
IncludeComposer(const std::string &filename, const SourceLoader &source_loader, const SourceSaver &source_saver, const SourceModificationTime &source_time, bool insert_line_directives)
The constructor requires a base filename that represents the top-level file, functions for loading...
std::string name
Definition: printer.cc:324
~ZipAssetComposer() override
The destructor is protected since this is derived from base::Referent.
ZipAssetComposer(const std::string &filename, bool insert_line_directives)
ZipAssetComposer definition.
static bool SaveFileData(const std::string &filename)
Attempts to save the latest cached data of the passed filename back to the original source file on di...
~IncludeComposer() override
The destructor is protected since this is derived from base::Referent.
std::string JoinStrings(const std::vector< std::string > &strings, const std::string &glue)
Joins the strings in the passed vector together with the passed glue.
Definition: stringutils.h:89
std::function< const std::string(const std::string &name)> SourceLoader
A function that returns a string source given a filename.
ShaderSourceComposer()
The constructor is protected since this is an abstract base class.
Loads a shader source from a resource that may include other resources using the special directive '$...
const std::vector< std::string > GetChangedDependencies() override
Determines if any dependencies have changed (e.g., if a file has changed on disk since the last call ...
std::vector< std::string > ION_API SplitStringWithoutSkipping(const std::string &str, const std::string &delimiters)
Splits a string into a vector of substrings, given a set of delimiter characters (expressed as a stri...
Definition: stringutils.cc:202
const std::string GetDependencyName(unsigned int id) const override
Returns the name of a dependency identified by the passed id.
const std::string GetSource() override
Returns the source string of a shader.
bool DependsOn(const std::string &dependency) const override
Returns whether this composer depends on the named dependency, which might be a filename or some othe...