glsl_builders.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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(filename: str, header_data: Optional[RDHeaderStruct] = None) -> None:
  89. header_data = header_data or RDHeaderStruct()
  90. include_file_in_rd_header(filename, header_data, 0)
  91. out_file = filename + ".gen.h"
  92. out_file_base = out_file
  93. out_file_base = out_file_base[out_file_base.rfind("/") + 1 :]
  94. out_file_base = out_file_base[out_file_base.rfind("\\") + 1 :]
  95. out_file_ifdef = out_file_base.replace(".", "_").upper()
  96. out_file_class = out_file_base.replace(".glsl.gen.h", "").title().replace("_", "").replace(".", "") + "ShaderRD"
  97. if header_data.compute_lines:
  98. body_parts = [
  99. "static const char _compute_code[] = {\n%s\n\t\t};" % generate_inline_code(header_data.compute_lines),
  100. f'setup(nullptr, nullptr, _compute_code, "{out_file_class}");',
  101. ]
  102. else:
  103. body_parts = [
  104. "static const char _vertex_code[] = {\n%s\n\t\t};" % generate_inline_code(header_data.vertex_lines),
  105. "static const char _fragment_code[] = {\n%s\n\t\t};" % generate_inline_code(header_data.fragment_lines),
  106. f'setup(_vertex_code, _fragment_code, nullptr, "{out_file_class}");',
  107. ]
  108. body_content = "\n\t\t".join(body_parts)
  109. # Intended curly brackets are doubled so f-string doesn't eat them up.
  110. shader_template = f"""/* WARNING, THIS FILE WAS GENERATED, DO NOT EDIT */
  111. #ifndef {out_file_ifdef}_RD
  112. #define {out_file_ifdef}_RD
  113. #include "servers/rendering/renderer_rd/shader_rd.h"
  114. class {out_file_class} : public ShaderRD {{
  115. public:
  116. {out_file_class}() {{
  117. {body_content}
  118. }}
  119. }};
  120. #endif
  121. """
  122. with open(out_file, "w") as fd:
  123. fd.write(shader_template)
  124. def build_rd_headers(target, source, env):
  125. for x in source:
  126. build_rd_header(str(x))
  127. class RAWHeaderStruct:
  128. def __init__(self):
  129. self.code = ""
  130. def include_file_in_raw_header(filename: str, header_data: RAWHeaderStruct, depth: int) -> None:
  131. fs = open(filename, "r")
  132. line = fs.readline()
  133. while line:
  134. while line.find("#include ") != -1:
  135. includeline = line.replace("#include ", "").strip()[1:-1]
  136. included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline)
  137. include_file_in_raw_header(included_file, header_data, depth + 1)
  138. line = fs.readline()
  139. header_data.code += line
  140. line = fs.readline()
  141. fs.close()
  142. def build_raw_header(filename: str, header_data: Optional[RAWHeaderStruct] = None):
  143. header_data = header_data or RAWHeaderStruct()
  144. include_file_in_raw_header(filename, header_data, 0)
  145. out_file = filename + ".gen.h"
  146. out_file_base = out_file.replace(".glsl.gen.h", "_shader_glsl")
  147. out_file_base = out_file_base[out_file_base.rfind("/") + 1 :]
  148. out_file_base = out_file_base[out_file_base.rfind("\\") + 1 :]
  149. out_file_ifdef = out_file_base.replace(".", "_").upper()
  150. shader_template = f"""/* WARNING, THIS FILE WAS GENERATED, DO NOT EDIT */
  151. #ifndef {out_file_ifdef}_RAW_H
  152. #define {out_file_ifdef}_RAW_H
  153. static const char {out_file_base}[] = {{
  154. {generate_inline_code(header_data.code, insert_newline=False)}
  155. }};
  156. #endif
  157. """
  158. with open(out_file, "w") as f:
  159. f.write(shader_template)
  160. def build_raw_headers(target, source, env):
  161. for x in source:
  162. build_raw_header(str(x))
  163. if __name__ == "__main__":
  164. subprocess_main(globals())