glsl_builders.py 7.3 KB

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