glsl_builders.py 7.8 KB

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