Ion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
shaderhandler.cc
Go to the documentation of this file.
1 
18 #if !ION_PRODUCTION
19 
21 
22 #include <sstream>
23 
24 #include "base/integral_types.h"
25 #include "ion/base/invalid.h"
26 #include "ion/base/stringutils.h"
29 #include "ion/gfx/shaderprogram.h"
32 
33 ION_REGISTER_ASSETS(IonRemoteShadersRoot);
34 
35 namespace ion {
36 namespace remote {
37 
38 using gfxutils::ShaderManager;
41 
42 namespace {
43 
45 typedef std::vector<std::string> StringVector;
46 
47 static const char kInfoLogString[] = "|info log|";
48 
52 static std::string SimpleHtmlList(const std::string& title,
53  const std::string& description,
54  const std::string& dir,
55  const StringVector& elements) {
56  std::stringstream str;
57  str << "<!DOCTYPE html>\n<html><head><title>" << title << "</title></head>"
58  << "<body><link rel=\"stylesheet\" href=\"/ion/css/style.css\" />\n"
59  << description << "\n<ul>\n";
60 
61  const size_t count = elements.size();
62  for (size_t i = 0; i < count; ++i) {
63  str << " <li><a href=\"" << dir << "/" << elements[i] << "\">"
64  << elements[i] << "</a></li>\n";
65  }
66  str << "</body>\n</html>\n";
67  return str.str();
68 }
69 
71 
77 
78 
81 static const std::string GetProgramNamesString(const ShaderManagerPtr& sm,
82  bool serve_raw,
83  std::string* content_type) {
84  StringVector shaders = sm->GetShaderProgramNames();
85  if (serve_raw) {
87  if (shaders.empty())
88  return "\n";
89  else
90  return base::JoinStrings(shaders, "\n");
91  } else {
93  *content_type = "text/html";
95  shaders.push_back("shader_editor");
96  return SimpleHtmlList("Registered shader programs",
97  "<h3>List of registered shader programs. Click on a"
98  " name to see shader stages.</h3>", "/ion/shaders",
99  shaders);
100  }
101 }
102 
104 static const std::string GetShaderStagesString(const std::string& program_name,
105  bool serve_raw,
106  std::string* content_type) {
107  StringVector stages(3);
108  stages[0] = kInfoLogString;
109  stages[1] = "vertex";
110  stages[2] = "fragment";
111  if (serve_raw) {
112  return base::JoinStrings(stages, "\n");
113  } else {
114  *content_type = "text/html";
115  return SimpleHtmlList(
116  std::string("Info log and shader stages for ") + program_name,
117  std::string("<h3>Info log and shader stages for program '") +
118  program_name + "'. Click on a stage to see shader sources.</h3>",
119  program_name, stages);
120  }
121 }
122 
124 static const std::string GetDependenciesString(
125  const std::string& program_name, const std::string& stage,
126  const StringVector& dependencies, bool serve_raw,
127  std::string* content_type) {
128  if (serve_raw) {
130  return base::JoinStrings(dependencies, "\n");
131  } else {
133  *content_type = "text/html";
134  return SimpleHtmlList(
135  std::string("List of dependencies for the ") + stage +
136  std::string(" stage of ") + program_name,
137  std::string("<h3>List of dependencies for the ") + stage +
138  std::string(" stage of program '") + program_name +
139  "'. Click on a stage to see shader sources.</h3>",
140  stage, dependencies);
141  }
142 }
143 
146 static const std::string FormatInfoLog(
147  const std::string& log,
148  const ShaderSourceComposerPtr& composer) {
149  const StringVector lines = base::SplitString(log, "\n");
150  const size_t count = lines.size();
151  std::stringstream str;
152  for (size_t i = 0; i < count; ++i) {
153  const StringVector tokens = base::SplitString(lines[i], ":");
159  if (tokens[0].find('(') != std::string::npos) {
161  const StringVector numbers = base::SplitString(tokens[0], "()");
162  const uint32 input_id = base::StringToInt32(numbers[0]);
163  str << composer->GetDependencyName(input_id) << ":" << numbers[1] << ":"
165  StringVector(tokens.begin() + 2, tokens.end()), ":");
166  } else {
168  const uint32 input_id = base::StringToInt32(tokens[1]);
169  str << composer->GetDependencyName(input_id) << ":" << tokens[2] << ":"
171  StringVector(tokens.begin() + 3, tokens.end()), ":");
172  }
173  str << "<br>\n";
174  }
175  return str.str();
176 }
177 
181 static const std::string GetShaderProgramInfoLog(
182  const gfx::ShaderProgram* program,
183  const ShaderSourceComposerPtr& composer,
184  const std::string& stage) {
185  DCHECK(program);
186  std::string log;
187  if (stage == "vertex" && program->GetVertexShader().Get())
188  log = FormatInfoLog(program->GetVertexShader()->GetInfoLog(), composer);
189  else if (stage == "fragment" && program->GetFragmentShader().Get())
190  log = FormatInfoLog(program->GetFragmentShader()->GetInfoLog(), composer);
191  else
192  log = program->GetInfoLog();
193 
195  if (log.empty())
196  log = "OK";
197  return log;
198 }
199 
202 static const std::string GetShadersRootString(const ShaderManagerPtr& sm,
203  const gfx::RendererPtr& renderer,
204  const std::string& path,
205  const HttpServer::QueryMap& args,
206  std::string* content_type) {
207  const bool serve_raw = args.find("raw") != args.end();
208  if (path == "") {
209  return GetProgramNamesString(sm, serve_raw, content_type);
210  } else {
211  const StringVector names = base::SplitString(path, "/");
212  if (const gfx::ShaderProgram* program =
213  sm->GetShaderProgram(names[0]).Get()) {
215  if (names.size() == 1) {
216  return GetShaderStagesString(names[0], serve_raw, content_type);
217  } else if (names.size() >= 2) {
219  ShaderSourceComposerPtr composer;
220  if (names[1] == "vertex")
221  sm->GetShaderProgramComposers(names[0], &composer, NULL);
222  else if (names[1] == "fragment")
223  sm->GetShaderProgramComposers(names[0], NULL, &composer);
224  else if (names[1] == kInfoLogString)
225  return GetShaderProgramInfoLog(program, composer, "link");
226 
227  if (composer.Get()) {
228  StringVector dependencies;
230  dependencies.push_back(kInfoLogString);
231  const StringVector deps = composer->GetDependencyNames();
232  dependencies.insert(dependencies.end(), deps.begin(), deps.end());
233 
235  HttpServer::QueryMap::const_iterator it = args.find("set_source");
236  if (it == args.end()) {
239  if (names.size() == 2) {
240  return GetDependenciesString(
241  names[0], names[1], dependencies, serve_raw, content_type);
242  } else {
247  if (names[2] == kInfoLogString)
248  return GetShaderProgramInfoLog(program, composer, names[1]);
249  else
250  return composer->GetDependencySource(base::JoinStrings(
251  StringVector(names.begin() + 2, names.end()), "/"));
252  }
253  } else {
256  const std::string dep_name = base::JoinStrings(
257  StringVector(names.begin() + 2, names.end()), "/");
258  composer->SetDependencySource(dep_name, it->second);
259  sm->RecreateShaderProgramsThatDependOn(dep_name);
261  if (renderer.Get()) {
262  const bool wait_for_completion =
263  (args.find("nonblocking") == args.end());
265  new(renderer->GetAllocatorForLifetime(base::kShortTerm))
266  gfxutils::ProgramCallback(wait_for_completion));
267  gfx::ResourceManager* rm = renderer->GetResourceManager();
268  rm->RequestAllResourceInfos<gfx::ShaderProgram,
271  callback.Get(), std::placeholders::_1));
272  callback->WaitForCompletion(NULL);
273  }
274  return "Shader source changed.";
275  }
276  }
277  }
278  }
279  }
280  return std::string();
281 }
282 
285 static const std::string ServeShaderStatus(const ShaderManagerPtr& sm) {
286  StringVector shaders = sm->GetShaderProgramNames();
287  const size_t count = shaders.size();
288  StringVector logs(3);
289  for (size_t i = 0; i < count; ++i) {
290  const gfx::ShaderProgramPtr program =
291  sm->GetShaderProgram(shaders[i]);
292  ShaderSourceComposerPtr vertex_composer;
293  ShaderSourceComposerPtr fragment_composer;
294  sm->GetShaderProgramComposers(shaders[i], &vertex_composer,
295  &fragment_composer);
296 
297  if (program.Get()) {
298  static const char kOk[] = "OK";
299  static const char kError[] = "Error";
300  logs[0] = (!program->GetVertexShader().Get() ||
301  program->GetVertexShader()->GetInfoLog().empty())
302  ? kOk
303  : kError;
304  logs[1] = (!program->GetFragmentShader().Get() ||
305  program->GetFragmentShader()->GetInfoLog().empty())
306  ? kOk
307  : kError;
308  logs[2] = program->GetInfoLog().empty() ? kOk : kError;
309  shaders[i] += "," + base::JoinStrings(logs, ",");
310  }
311  }
315  if (shaders.empty())
316  return "\n";
317  else
318  return base::JoinStrings(shaders, "\n");
319 }
320 
323 static const std::string UpdateAndServeChangedDependencies(
324  const ShaderManagerPtr& sm) {
325  StringVector shaders = sm->GetShaderProgramNames();
326  const size_t count = shaders.size();
327  std::set<std::string> changed_set;
328  for (size_t i = 0; i < count; ++i) {
329  const gfx::ShaderProgramPtr program =
330  sm->GetShaderProgram(shaders[i]);
331  ShaderSourceComposerPtr vertex_composer;
332  ShaderSourceComposerPtr fragment_composer;
333  sm->GetShaderProgramComposers(shaders[i], &vertex_composer,
334  &fragment_composer);
335  if (vertex_composer.Get()) {
336  const std::vector<std::string> changed =
337  vertex_composer->GetChangedDependencies();
338  changed_set.insert(changed.begin(), changed.end());
339  }
340  if (fragment_composer.Get()) {
341  const std::vector<std::string> changed =
342  fragment_composer->GetChangedDependencies();
343  changed_set.insert(changed.begin(), changed.end());
344  }
345  }
346 
348  if (const size_t count = changed_set.size()) {
349  const std::vector<std::string> changed(changed_set.begin(),
350  changed_set.end());
351  for (size_t i = 0; i < count; ++i)
352  sm->RecreateShaderProgramsThatDependOn(changed[i]);
353  return base::JoinStrings(changed, ";");
354  } else {
355  return ";";
356  }
357 }
358 
359 } // anonymous namespace
360 
362  const gfx::RendererPtr& renderer)
363  : HttpServer::RequestHandler("/ion/shaders"),
364  sm_(shader_manager),
365  renderer_(renderer) {
367  IonRemoteShadersRoot::RegisterAssetsOnce();
368 }
369 
371 
372 const std::string ShaderHandler::HandleRequest(const std::string& path_in,
373  const HttpServer::QueryMap& args,
374  std::string* content_type) {
375  const std::string path =
376  path_in == "shader_editor" ? "shader_editor/index.html" : path_in;
377 
378  if (path == "shader_status") {
379  return ServeShaderStatus(sm_);
380  } else if (path == "update_changed_dependencies") {
381  return UpdateAndServeChangedDependencies(sm_);
382  } else if (base::StartsWith(path, "shader_editor")) {
383  const std::string& data = base::ZipAssetManager::GetFileData(
384  "ion/shaders/" + path);
385  if (base::IsInvalidReference(data)) {
386  return std::string();
387  } else {
389  if (base::EndsWith(path, "html"))
390  *content_type = "text/html";
391  return data;
392  }
393  } else {
394  return GetShadersRootString(sm_, renderer_, path, args, content_type);
395  }
396 }
397 
398 } // namespace remote
399 } // namespace ion
400 
401 #endif
base::ReferentPtr< ShaderManager >::Type ShaderManagerPtr
Definition: shadermanager.h:85
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
kShortTerm is used for objects that are very transient in nature, such as scratch memory used to comp...
Definition: allocator.h:36
void Callback(const std::vector< T > &data)
This function is compatible with ResourceManager::InfoCallback, and should be used as the callback pa...
ION_REGISTER_ASSETS(IonRemoteShadersRoot)
Copyright 2016 Google Inc.
bool StartsWith(const std::string &target, const std::string &start)
Returns whether target begins with start.
Definition: stringutils.h:76
const std::string & str
base::ReferentPtr< ResourceCallback< T > >::Type RefPtr
ShaderHandler(const gfxutils::ShaderManagerPtr &shader_manager, const gfx::RendererPtr &renderer)
A ShaderHandler requires a valid ShaderManager as well as a Renderer to notify of changes made to sha...
#define DCHECK(expr)
Definition: logging.h:331
const std::string HandleRequest(const std::string &path, const HttpServer::QueryMap &args, std::string *content_type) override
The HandleRequest() function is passed the path (relative to its base path) of the file or directory ...
std::vector< std::string > ION_API SplitString(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:187
static const std::string & GetFileData(const std::string &filename)
Returns the data of the passed filename if the manager contains it.
gfx::ProgramInfo< ResourceInfo > ProgramInfo
int32 ION_API StringToInt32(const std::string &str)
Extracts and returns an integral value from str.
Definition: stringutils.cc:328
base::ReferentPtr< ShaderProgram >::Type ShaderProgramPtr
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
bool EndsWith(const std::string &target, const std::string &end)
Returns whether target ends with end.
Definition: stringutils.h:81
ResourceCallback< gfx::ResourceManager::ProgramInfo > ProgramCallback
const RendererPtr & renderer_
Renderer used to render images.
std::map< std::string, std::string > QueryMap
Definition: httpserver.h:35
base::ReferentPtr< Renderer >::Type RendererPtr
Convenience typedef for shared pointer to a Renderer.
Definition: renderer.h:428
A SharedPtr is a smart shared pointer to an instance of some class that implements reference counting...
Definition: sharedptr.h:60
base::ReferentPtr< ShaderSourceComposer >::Type ShaderSourceComposerPtr