💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

218 lines
6.4 KiB

  1. /*
  2. * Copyright (C) 2021 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "renderer/shader-template.hpp"
  20. #include <algorithm>
  21. #include <sstream>
  22. shader_template::shader_template(const std::string& source_code)
  23. {
  24. source(source_code);
  25. }
  26. shader_template::shader_template()
  27. {}
  28. void shader_template::source(const std::string& source)
  29. {
  30. // Reset template
  31. template_source.clear();
  32. vertex_directives.clear();
  33. fragment_directives.clear();
  34. geometry_directives.clear();
  35. define_directives.clear();
  36. // Iterate through source line-by-line
  37. std::istringstream source_stream(source);
  38. std::string line;
  39. while (std::getline(source_stream, line))
  40. {
  41. std::string token;
  42. std::istringstream line_stream(line);
  43. // Detect `#pragma` directives
  44. if (line_stream >> token && token == "#pragma")
  45. {
  46. if (line_stream >> token)
  47. {
  48. // Map line numbers of supported directives
  49. if (token == "define")
  50. {
  51. if (line_stream >> token)
  52. define_directives.insert({token, template_source.size()});
  53. }
  54. else if (token == "vertex")
  55. vertex_directives.insert(template_source.size());
  56. else if (token == "fragment")
  57. fragment_directives.insert(template_source.size());
  58. else if (token == "geometry")
  59. geometry_directives.insert(template_source.size());
  60. }
  61. }
  62. // Append line to template source
  63. template_source.push_back(line);
  64. }
  65. }
  66. std::string shader_template::configure(gl::shader_stage stage, const dictionary_type& definitions) const
  67. {
  68. replace_stage_directives(stage);
  69. replace_define_directives(definitions);
  70. // Join vector of source lines into single string
  71. std::ostringstream stream;
  72. std::copy(template_source.begin(), template_source.end(), std::ostream_iterator<std::string>(stream, "\n"));
  73. return stream.str();
  74. }
  75. gl::shader_object* shader_template::compile(gl::shader_stage stage, const dictionary_type& definitions) const
  76. {
  77. // Generate shader object source
  78. std::string object_source = configure(stage, definitions);
  79. // Create new shader object
  80. gl::shader_object* object = new gl::shader_object(stage);
  81. // Set shader object source
  82. object->source(object_source);
  83. // Compile shader object
  84. object->compile();
  85. return object;
  86. }
  87. gl::shader_program* shader_template::build(const dictionary_type& definitions) const
  88. {
  89. gl::shader_object* vertex_object = nullptr;
  90. gl::shader_object* fragment_object = nullptr;
  91. gl::shader_object* geometry_object = nullptr;
  92. // Create shader program
  93. gl::shader_program* program = new gl::shader_program();
  94. if (has_vertex_directive())
  95. {
  96. // Compile vertex shader object and attach to shader program
  97. vertex_object = compile(gl::shader_stage::vertex, definitions);
  98. program->attach(vertex_object);
  99. }
  100. if (has_fragment_directive())
  101. {
  102. // Compile fragment shader object and attach to shader program
  103. fragment_object = compile(gl::shader_stage::fragment, definitions);
  104. program->attach(fragment_object);
  105. }
  106. if (has_geometry_directive())
  107. {
  108. // Compile fragment shader object and attach to shader program
  109. geometry_object = compile(gl::shader_stage::geometry, definitions);
  110. program->attach(geometry_object);
  111. }
  112. // Link attached shader objects into shader program
  113. program->link();
  114. if (vertex_object)
  115. {
  116. // Detach and delete vertex shader object
  117. program->detach(vertex_object);
  118. delete vertex_object;
  119. }
  120. if (fragment_object)
  121. {
  122. // Detach and delete fragment shader object
  123. program->detach(fragment_object);
  124. delete fragment_object;
  125. }
  126. if (geometry_object)
  127. {
  128. // Detach and delete geometry shader object
  129. program->detach(geometry_object);
  130. delete geometry_object;
  131. }
  132. return program;
  133. }
  134. void shader_template::replace_stage_directives(gl::shader_stage stage) const
  135. {
  136. // Determine stage directives according to the shader stage being generated
  137. const std::string vertex_directive = (stage == gl::shader_stage::vertex) ? "#define __VERTEX__" : "/* #undef __VERTEX__ */";
  138. const std::string fragment_directive = (stage == gl::shader_stage::fragment) ? "#define __FRAGMENT__" : "/* #undef __FRAGMENT__ */";
  139. const std::string geometry_directive = (stage == gl::shader_stage::geometry) ? "#define __GEOMETRY__" : "/* #undef __GEOMETRY__ */";
  140. // Handle `#pragma <stage>` directives
  141. for (std::size_t i: vertex_directives)
  142. template_source[i] = vertex_directive;
  143. for (std::size_t i: fragment_directives)
  144. template_source[i] = fragment_directive;
  145. for (std::size_t i: geometry_directives)
  146. template_source[i] = geometry_directive;
  147. }
  148. void shader_template::replace_define_directives(const dictionary_type& definitions) const
  149. {
  150. // For each `#pragma define <key>` directive
  151. for (const auto& define_directive: define_directives)
  152. {
  153. // Get a reference to the directive line
  154. std::string& line = template_source[define_directive.second];
  155. // Check if the corresponding definition was given by the configuration
  156. auto definitions_it = definitions.find(define_directive.first);
  157. if (definitions_it != definitions.end())
  158. {
  159. // Definition found, Replace `#pragma define <key>` with `#define <key>` or `#define <key> <value>`
  160. line = "#define " + define_directive.first;
  161. if (!definitions_it->second.empty())
  162. line += " " + definitions_it->second;
  163. }
  164. else
  165. {
  166. // Definition not found, replace `#pragma define <key>` with the comment `/* #undef <key> */`.
  167. line = "/* #undef " + define_directive.first + " */";
  168. }
  169. }
  170. }
  171. bool shader_template::has_vertex_directive() const
  172. {
  173. return !vertex_directives.empty();
  174. }
  175. bool shader_template::has_fragment_directive() const
  176. {
  177. return !fragment_directives.empty();
  178. }
  179. bool shader_template::has_geometry_directive() const
  180. {
  181. return !geometry_directives.empty();
  182. }
  183. bool shader_template::has_define_directive(const std::string& key) const
  184. {
  185. return (define_directives.find(key) != define_directives.end());
  186. }