glsl_builders.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. """Functions used to generate source files during build time
  2. All such functions are invoked in a subprocess on Windows to prevent build flakiness.
  3. """
  4. import os.path
  5. from typing import Optional, Iterable
  6. from platform_methods import subprocess_main
  7. def generate_inline_code(input_lines: Iterable[str], insert_newline: bool = True):
  8. """Take header data and generate inline code
  9. :param: input_lines: values for shared inline code
  10. :return: str - generated inline value
  11. """
  12. output = []
  13. for line in input_lines:
  14. if line:
  15. output.append(",".join(str(ord(c)) for c in line))
  16. if insert_newline:
  17. output.append("%s" % ord("\n"))
  18. output.append("0")
  19. return ",".join(output)
  20. class RDHeaderStruct:
  21. def __init__(self):
  22. self.vertex_lines = []
  23. self.fragment_lines = []
  24. self.compute_lines = []
  25. self.vertex_included_files = []
  26. self.fragment_included_files = []
  27. self.compute_included_files = []
  28. self.reading = ""
  29. self.line_offset = 0
  30. self.vertex_offset = 0
  31. self.fragment_offset = 0
  32. self.compute_offset = 0
  33. def include_file_in_rd_header(filename: str, header_data: RDHeaderStruct, depth: int) -> RDHeaderStruct:
  34. fs = open(filename, "r")
  35. line = fs.readline()
  36. while line:
  37. index = line.find("//")
  38. if index != -1:
  39. line = line[:index]
  40. if line.find("#[vertex]") != -1:
  41. header_data.reading = "vertex"
  42. line = fs.readline()
  43. header_data.line_offset += 1
  44. header_data.vertex_offset = header_data.line_offset
  45. continue
  46. if line.find("#[fragment]") != -1:
  47. header_data.reading = "fragment"
  48. line = fs.readline()
  49. header_data.line_offset += 1
  50. header_data.fragment_offset = header_data.line_offset
  51. continue
  52. if line.find("#[compute]") != -1:
  53. header_data.reading = "compute"
  54. line = fs.readline()
  55. header_data.line_offset += 1
  56. header_data.compute_offset = header_data.line_offset
  57. continue
  58. while line.find("#include ") != -1:
  59. includeline = line.replace("#include ", "").strip()[1:-1]
  60. if includeline.startswith("thirdparty/"):
  61. included_file = os.path.relpath(includeline)
  62. else:
  63. included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline)
  64. if not included_file in header_data.vertex_included_files and header_data.reading == "vertex":
  65. header_data.vertex_included_files += [included_file]
  66. if include_file_in_rd_header(included_file, header_data, depth + 1) is None:
  67. print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
  68. elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment":
  69. header_data.fragment_included_files += [included_file]
  70. if include_file_in_rd_header(included_file, header_data, depth + 1) is None:
  71. print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
  72. elif not included_file in header_data.compute_included_files and header_data.reading == "compute":
  73. header_data.compute_included_files += [included_file]
  74. if include_file_in_rd_header(included_file, header_data, depth + 1) is None:
  75. print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
  76. line = fs.readline()
  77. line = line.replace("\r", "").replace("\n", "")
  78. if header_data.reading == "vertex":
  79. header_data.vertex_lines += [line]
  80. if header_data.reading == "fragment":
  81. header_data.fragment_lines += [line]
  82. if header_data.reading == "compute":
  83. header_data.compute_lines += [line]
  84. line = fs.readline()
  85. header_data.line_offset += 1
  86. fs.close()
  87. return header_data
  88. def build_rd_header(
  89. filename: str, optional_output_filename: str = None, header_data: Optional[RDHeaderStruct] = None
  90. ) -> None:
  91. header_data = header_data or RDHeaderStruct()
  92. include_file_in_rd_header(filename, header_data, 0)
  93. if optional_output_filename is None:
  94. out_file = filename + ".gen.h"
  95. else:
  96. out_file = optional_output_filename
  97. out_file_base = out_file
  98. out_file_base = out_file_base[out_file_base.rfind("/") + 1 :]
  99. out_file_base = out_file_base[out_file_base.rfind("\\") + 1 :]
  100. out_file_ifdef = out_file_base.replace(".", "_").upper()
  101. out_file_class = out_file_base.replace(".glsl.gen.h", "").title().replace("_", "").replace(".", "") + "ShaderRD"
  102. if header_data.compute_lines:
  103. body_parts = [
  104. "static const char _compute_code[] = {\n%s\n\t\t};" % generate_inline_code(header_data.compute_lines),
  105. f'setup(nullptr, nullptr, _compute_code, "{out_file_class}");',
  106. ]
  107. else:
  108. body_parts = [
  109. "static const char _vertex_code[] = {\n%s\n\t\t};" % generate_inline_code(header_data.vertex_lines),
  110. "static const char _fragment_code[] = {\n%s\n\t\t};" % generate_inline_code(header_data.fragment_lines),
  111. f'setup(_vertex_code, _fragment_code, nullptr, "{out_file_class}");',
  112. ]
  113. body_content = "\n\t\t".join(body_parts)
  114. # Intended curly brackets are doubled so f-string doesn't eat them up.
  115. shader_template = f"""/* WARNING, THIS FILE WAS GENERATED, DO NOT EDIT */
  116. #ifndef {out_file_ifdef}_RD
  117. #define {out_file_ifdef}_RD
  118. #include "servers/rendering/renderer_rd/shader_rd.h"
  119. class {out_file_class} : public ShaderRD {{
  120. public:
  121. {out_file_class}() {{
  122. {body_content}
  123. }}
  124. }};
  125. #endif
  126. """
  127. with open(out_file, "w") as fd:
  128. fd.write(shader_template)
  129. def build_rd_headers(target, source, env):
  130. for x in source:
  131. build_rd_header(filename=str(x))
  132. class RAWHeaderStruct:
  133. def __init__(self):
  134. self.code = ""
  135. def include_file_in_raw_header(filename: str, header_data: RAWHeaderStruct, depth: int) -> None:
  136. fs = open(filename, "r")
  137. line = fs.readline()
  138. while line:
  139. while line.find("#include ") != -1:
  140. includeline = line.replace("#include ", "").strip()[1:-1]
  141. included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline)
  142. include_file_in_raw_header(included_file, header_data, depth + 1)
  143. line = fs.readline()
  144. header_data.code += line
  145. line = fs.readline()
  146. fs.close()
  147. def build_raw_header(
  148. filename: str, optional_output_filename: str = None, header_data: Optional[RAWHeaderStruct] = None
  149. ):
  150. header_data = header_data or RAWHeaderStruct()
  151. include_file_in_raw_header(filename, header_data, 0)
  152. if optional_output_filename is None:
  153. out_file = filename + ".gen.h"
  154. else:
  155. out_file = optional_output_filename
  156. out_file_base = out_file.replace(".glsl.gen.h", "_shader_glsl")
  157. out_file_base = out_file_base[out_file_base.rfind("/") + 1 :]
  158. out_file_base = out_file_base[out_file_base.rfind("\\") + 1 :]
  159. out_file_ifdef = out_file_base.replace(".", "_").upper()
  160. shader_template = f"""/* WARNING, THIS FILE WAS GENERATED, DO NOT EDIT */
  161. #ifndef {out_file_ifdef}_RAW_H
  162. #define {out_file_ifdef}_RAW_H
  163. static const char {out_file_base}[] = {{
  164. {generate_inline_code(header_data.code, insert_newline=False)}
  165. }};
  166. #endif
  167. """
  168. with open(out_file, "w") as f:
  169. f.write(shader_template)
  170. def build_raw_headers(target, source, env):
  171. for x in source:
  172. build_raw_header(filename=str(x))
  173. if __name__ == "__main__":
  174. subprocess_main(globals())