glsl_builders.py 7.1 KB

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