Skip to content

Commit 8228f71

Browse files
m0dBm0dB
authored andcommitted
moved script back, edited info, edited readme
1 parent 2bab73c commit 8228f71

File tree

2 files changed

+167
-3
lines changed

2 files changed

+167
-3
lines changed

src/rendergraph/shaders/README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,14 @@ The GLSL shaders are extracted programmatically with `QShader` and then used wit
2121
### Qt < 6.6
2222

2323
The GLSL shader have to extracted from the qsb shader bundles to be used by `QOpenGLShader`.
24-
This can be done using the script `generate_shaders_gl.py` in the ../tools directory. To
25-
use this script, make sure that the qsb and spirv commands are in your path. qsb is part of
26-
Qt. spirv is part of the Vulkan SDK and can be downloaded from <https://vulkan.org>
24+
This can be done using the script `rg_generate_shaders_gl.py` in the mixxx/tools directory:
25+
26+
```console
27+
$ ../../../tools/rg_generate_shaders_gl.py --cmake generated_shaders_gl.cmake *.vert *.frag
28+
```
29+
30+
To use this script, make sure that the qsb and spirv commands are in your path. qsb is part
31+
of Qt. spirv is part of the Vulkan SDK and can be downloaded from <https://vulkan.org>
2732

2833
The script also generates the file `generated_shaders_gl.cmake` which sets a cmake
2934
variable containing a list of all GLSL shaders, used by the CMakeLists.txt in this folder.

tools/rg_generate_shaders_gl.py

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Converts a fragment or vertex shader file into a GL shader file.
4+
5+
You can use it like this:
6+
7+
$ ./rg_generate_shaders_gl.py --cmake-output \\
8+
../src/rendergraph/shaders/generated_shaders_gl.cmake \\
9+
../src/rendergraph/shaders/*.{vert,frag}
10+
"""
11+
import argparse
12+
import logging
13+
import os
14+
import pathlib
15+
import re
16+
import shutil
17+
import subprocess
18+
import tempfile
19+
import typing
20+
21+
22+
def find_executable(
23+
executable_name: str, additional_paths: typing.Optional[list[str]] = None
24+
) -> pathlib.Path:
25+
"""Find an executable by name in $PATH and in the additional paths."""
26+
if executable_path := shutil.which(executable_name):
27+
return pathlib.Path(executable_path)
28+
29+
if additional_paths:
30+
if executable_path := shutil.which(
31+
executable_name, path=os.pathsep.join(additional_paths)
32+
):
33+
return pathlib.Path(executable_path)
34+
35+
raise OSError(f"Executable {executable_name!r} not found!")
36+
37+
38+
QSB_EXECUTABLE = find_executable(
39+
"qsb",
40+
additional_paths=[
41+
"/usr/lib/qt6/bin",
42+
"/lib/qt6/bin",
43+
"/usr/local/lib/qt6/bin",
44+
],
45+
)
46+
47+
48+
def parse_shader(input_filepath: pathlib.Path) -> typing.Iterator[str]:
49+
"""Parse a Fragment/Vertex shader file and yield lines for a GL file."""
50+
with tempfile.NamedTemporaryFile() as fp:
51+
subprocess.check_call(
52+
[
53+
QSB_EXECUTABLE,
54+
"--glsl",
55+
"120",
56+
"--output",
57+
fp.name,
58+
input_filepath,
59+
]
60+
)
61+
output = subprocess.check_output(
62+
[QSB_EXECUTABLE, "--dump", fp.name],
63+
encoding="utf-8",
64+
universal_newlines=True,
65+
)
66+
67+
comment_added = False
68+
ok = False
69+
in_shader_block = 0
70+
buffered_blank_line = False
71+
for line in output.splitlines():
72+
if in_shader_block == 2:
73+
if re.match(r"^\*\*", line):
74+
ok = True
75+
else:
76+
if not comment_added and not re.match(r"^#", line):
77+
yield "//// GENERATED - EDITS WILL BE OVERWRITTEN"
78+
comment_added = True
79+
if line:
80+
if buffered_blank_line:
81+
yield ""
82+
buffered_blank_line = False
83+
yield line
84+
else:
85+
buffered_blank_line = True
86+
elif in_shader_block == 1:
87+
if line.rstrip() == "Contents:":
88+
in_shader_block = 2
89+
else:
90+
if line.rstrip() == "Shader 1: GLSL 120 [Standard]":
91+
in_shader_block = 1
92+
if not ok:
93+
raise EOFError("end of file reached before end marker reached")
94+
95+
96+
def get_paths(paths: list[pathlib.Path]) -> typing.Iterator[pathlib.Path]:
97+
for path in paths:
98+
if path.is_dir():
99+
yield from path.glob("*.vert")
100+
yield from path.glob("*.frag")
101+
else:
102+
yield path
103+
104+
105+
def main(argv: typing.Optional[list[str]] = None) -> int:
106+
logging.basicConfig(level=logging.DEBUG, format="%(message)s")
107+
108+
logger = logging.getLogger(__name__)
109+
110+
description, _, epilog = __doc__.strip().partition("\n\n")
111+
parser = argparse.ArgumentParser(
112+
description=description,
113+
epilog=epilog,
114+
formatter_class=argparse.RawDescriptionHelpFormatter,
115+
)
116+
parser.add_argument(
117+
"file",
118+
nargs="+",
119+
type=pathlib.Path,
120+
help="Input files (.vert, .frag) or directory",
121+
)
122+
parser.add_argument(
123+
"--cmake-output",
124+
type=argparse.FileType("w"),
125+
required=True,
126+
help="CMake Output files (.cmake)",
127+
)
128+
args = parser.parse_args(argv)
129+
130+
generated_shaders: list[pathlib.Path] = []
131+
132+
for file in sorted(get_paths(args.file)):
133+
logger.info("Reading file: %s", file)
134+
try:
135+
lines = list(parse_shader(file))
136+
except EOFError as err:
137+
logger.error("Failed to parse %s: %s", file, err)
138+
continue
139+
140+
output_file = file.with_suffix(f"{file.suffix}.gl")
141+
logger.info("Writing file: %s", output_file)
142+
with output_file.open("w") as fp:
143+
for line in lines:
144+
fp.write(f"{line}\n")
145+
146+
generated_shaders.append(output_file)
147+
148+
args.cmake_output.write("set(\n")
149+
args.cmake_output.write(" generated_shaders_gl\n")
150+
for generated_file in generated_shaders:
151+
args.cmake_output.write(f" {generated_file.name}\n")
152+
args.cmake_output.write(")\n")
153+
logger.info("Generated %d shader files.", len(generated_shaders))
154+
155+
return 0
156+
157+
158+
if __name__ == "__main__":
159+
main()

0 commit comments

Comments
 (0)