Skip to content

Commit 5dccb68

Browse files
committed
add dataclass
1 parent 62ab439 commit 5dccb68

File tree

2 files changed

+50
-32
lines changed

2 files changed

+50
-32
lines changed

wgpu_shadertoy/imgui.py

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,36 @@
22
from imgui_bundle import imgui as ig #TODO: rename (git mv) the file instead.
33
from .utils import UniformArray
44
from wgpu.utils.imgui import ImguiWgpuBackend
5+
from dataclasses import dataclass
56

67

78
# could imgui become just another RenderPass after Image? I got to understand backend vs renderer first.
89
# make become part of .passes??
910
# todo: raise error if imgui isn't installed (only if this module is required?)
1011

1112

12-
def parse_constants(code:str, common_code) -> list[tuple[int, str, int|float, str]]:
13+
@dataclass
14+
class ShaderConstant:
15+
renderpass_pass: str #maybe this is a RenderPass pointer?
16+
line_number: int
17+
original_line: str
18+
name: str
19+
value: int | float
20+
shader_dtype: str # float, int, vec2, vec3, bool etc.
21+
22+
def c_type_format(self) -> str:
23+
# based on these for the memoryview cast:
24+
# https://docs.python.org/3/library/struct.html#format-characters
25+
if self.shader_dtype == "float":
26+
return "f"
27+
elif self.shader_dtype == "int":
28+
return "i"
29+
elif self.shader_dtype == "uint":
30+
return "I"
31+
# add more types as needed
32+
return "?"
33+
34+
def parse_constants(code:str, common_code) -> list[ShaderConstant]:
1335
# todo:
1436
# WGSL variants??
1537
# re/tree-sitter/loops and functions?
@@ -22,12 +44,12 @@ def parse_constants(code:str, common_code) -> list[tuple[int, str, int|float, st
2244
# mataches the macro: #define NAME VALUE
2345
# TODO there can be characters in numerical literals, such as x and o for hex and octal representation or e for scientific notation
2446
# technically the macros can also be an expression that is evaluated to be a number... such as # define DOF 10..0/30.0 - so how do we deal with that?
25-
define_pattern = re.compile(r"#define\s+(\w+)\s+([\d.]+)")
47+
define_pattern = re.compile(r"#\s*define\s+(\w+)\s+([\d.]+)") #for numerical literals right now.
2648
if_def_template = r"#(el)?if\s+" #preprocessor ifdef blocks can't become uniforms. replacing these dynamically will be difficult.
2749

2850
constants = []
2951
for li, line in enumerate(code.splitlines()):
30-
match = define_pattern.match(line.rstrip())
52+
match = define_pattern.match(line.strip())
3153
if match:
3254
name, value = match.groups()
3355
if_def_pattern = re.compile(if_def_template + name)
@@ -38,17 +60,26 @@ def parse_constants(code:str, common_code) -> list[tuple[int, str, int|float, st
3860

3961
if "." in value: #value.isdecimal?
4062
# TODO: wgsl needs to be more specific (f32 for example?) - but there is no preprocessor anyways...
41-
dtype = "f" #default float (32bit)
63+
dtype = "float" #default float (32bit)
4264
value = float(value)
43-
elif value.isdecimal: # value.isalum?
44-
dtype = "I" # "big I (32bit)"
65+
elif value.isdecimal(): # value.isnumeric?
66+
dtype = "int" # "big I (32bit)"
4567
value = int(value)
4668
else:
4769
# TODO complexer types?
4870
print(f"can't parse type for constant {name} with value {value}, skipping")
4971
continue
72+
73+
constant = ShaderConstant(
74+
renderpass_pass="image", # TODO: shouldn't be names.
75+
line_number=li,
76+
original_line=line.strip(),
77+
name=name,
78+
value=value,
79+
shader_dtype=dtype
80+
)
5081
# todo: remove lines here? (comment out better)
51-
constants.append((li, name, value, dtype)) # what about line to remove?
82+
constants.append(constant)
5283
print(f"In line {li} found constant: {name} with value: {value} of dtype {dtype}")
5384

5485
# maybe just named tuple instead of dataclass?
@@ -57,23 +88,21 @@ def parse_constants(code:str, common_code) -> list[tuple[int, str, int|float, st
5788
def make_uniform(constants) -> UniformArray:
5889
arr_data = []
5990
for constant in constants:
60-
_, name, value, dtype = constant
61-
arr_data.append(tuple([name, dtype, 1]))
91+
arr_data.append(tuple([constant.name, constant.c_type_format(), 1]))
6292
data = UniformArray(*arr_data)
6393

6494
# init data
6595
for constant in constants:
66-
_, name, value, dtype = constant
67-
data[name] = value
68-
96+
data[constant.name] = constant.value
97+
6998
# TODO:
7099
# is there issues with padding? (maybe solve in the class)
71100
# figure out order due to padding/alignment: https://www.w3.org/TR/WGSL/#alignment-and-size
72101
# return a UniformArray object (cycling import?)
73102
# (does this need to be a class to update the values?)
74103
return data
75104

76-
def construct_imports(constants, constant_binding_idx=10) -> str:
105+
def construct_imports(constants: list[ShaderConstant], constant_binding_idx=10) -> str:
77106
# codegen the import block for this uniform (including binding? - which number?)
78107
# could be part of the UniformArray class maybe?
79108
# to be pasted near the top of the fragment shader code.
@@ -84,17 +113,8 @@ def construct_imports(constants, constant_binding_idx=10) -> str:
84113
var_init_lines = []
85114
var_mapping_lines = []
86115
for const in constants:
87-
_, name, value, dtype = const
88-
# TODO: refactor to dtype map or something more useful -.-
89-
if dtype == "f":
90-
dtype = "float"
91-
elif dtype == "I":
92-
dtype = "int"
93-
else:
94-
# shouldn't happen
95-
continue
96-
var_init_lines.append(f"{dtype} {name};")
97-
var_mapping_lines.append(f"# define {name} const_input.{name}")
116+
var_init_lines.append(f"{const.shader_dtype} {const.name};")
117+
var_mapping_lines.append(f"# define {const.name} const_input.{const.name}")
98118

99119
code_construct = f"""
100120
uniform struct ConstantInput {{
@@ -114,7 +134,7 @@ def update_gui():
114134
pass
115135

116136

117-
def gui(constants, constants_data):
137+
def gui(constants: list[ShaderConstant], constants_data: UniformArray):
118138
ig.new_frame()
119139
ig.set_next_window_pos((0, 0), ig.Cond_.appearing)
120140
ig.set_next_window_size((400, 0), ig.Cond_.appearing)
@@ -126,11 +146,10 @@ def gui(constants, constants_data):
126146

127147
# create the sliders?
128148
for const in constants:
129-
li, name, value, dtype = const
130-
if dtype == "f":
131-
_, constants_data[name] = ig.slider_float(name, constants_data[name], 0, value*2.0)
132-
elif dtype == "I":
133-
_, constants_data[name] = ig.slider_int(name, constants_data[name], 0, value*2)
149+
if const.shader_dtype == "float":
150+
_, constants_data[const.name] = ig.slider_float(const.name, constants_data[const.name], 0, const.value*2.0)
151+
elif const.shader_dtype == "int":
152+
_, constants_data[const.name] = ig.slider_int(const.name, constants_data[const.name], 0, const.value*2)
134153
# TODO: improve min/max for negatives
135154

136155
ig.end()

wgpu_shadertoy/passes.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,7 @@ def construct_code(self) -> tuple[str, str]:
387387
# comment out existing constants
388388
shader_code_lines = self.shader_code.splitlines()
389389
for const in self.main._constants:
390-
line_number = const[0]
391-
shader_code_lines[line_number] = "// " + shader_code_lines[line_number]
390+
shader_code_lines[const.line_number] = "// " + shader_code_lines[const.line_number]
392391
self._shader_code = "\n".join(shader_code_lines)
393392

394393
constant_headers = construct_imports(self.main._constants)

0 commit comments

Comments
 (0)