glsl_builders.py 7.2 KB

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