Skip to content

update to wgpu-native 24.0.0.2 #673

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 48 commits into from
Mar 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
0f1bf87
update headers
Vipitis Feb 20, 2025
eb8581e
try to fix hparser
Vipitis Feb 21, 2025
ce932d1
handle escaped newlines
Vipitis Feb 21, 2025
0fbc6ae
run codegen
Vipitis Feb 21, 2025
f08b44e
fix escaped newlines in lib
Vipitis Feb 21, 2025
770f5a3
add string view in logger
Vipitis Feb 22, 2025
dc89134
simplify limits
Vipitis Feb 22, 2025
903fc4b
fix more callbacks
Vipitis Feb 22, 2025
b008b4d
update surface descriptors
Vipitis Feb 22, 2025
016f70b
update shader module
Vipitis Feb 22, 2025
da524e0
update render pipeline
Vipitis Feb 22, 2025
ded6d3b
fix surface status
Vipitis Feb 22, 2025
b7b52b4
update copy info
Vipitis Feb 22, 2025
6e67484
fix parsing of MAX values
Vipitis Feb 23, 2025
d52775f
update buffer map
Vipitis Feb 23, 2025
3fbb440
avoid magic values
Vipitis Feb 23, 2025
1bf0421
update generate_report
Vipitis Feb 23, 2025
c8548ff
fix limits again
Vipitis Feb 23, 2025
7a83943
fix labels
Vipitis Feb 23, 2025
098c054
update error messages
Vipitis Feb 23, 2025
413ab60
make async unimplemented
Vipitis Feb 24, 2025
6476cca
fix string view null value
Vipitis Feb 24, 2025
274fdb5
enable bundle test
Vipitis Feb 26, 2025
998ff84
avoid pointer references
Vipitis Mar 5, 2025
a958d8f
request native limits
Vipitis Mar 8, 2025
d4b7a85
update mem tests
Vipitis Mar 8, 2025
c86a6b4
cleanup hparser
Vipitis Mar 10, 2025
186741c
handle nested structs
Vipitis Mar 10, 2025
3313ba9
update codegen test
Vipitis Mar 11, 2025
d68253a
apidiff compilation hint
Vipitis Mar 11, 2025
84a3aa6
lint
Vipitis Mar 11, 2025
331b4e9
ruff format
Vipitis Mar 11, 2025
f7f5a15
bump wgpu-native build
Vipitis Mar 11, 2025
78651f0
set callback modes
Vipitis Mar 11, 2025
5f4697d
small tweaks
almarklein Mar 13, 2025
e2a4d2b
tweak codegen of flags so they're not reported missing
almarklein Mar 13, 2025
c15bb4f
Make limit logic a bit more consistent
almarklein Mar 13, 2025
6667a06
tweak new to_c_string_view
almarklein Mar 13, 2025
da03707
tweak to_c_string_view and add from_c_string_view
almarklein Mar 13, 2025
2a7ca0a
Check result of function that return WGPUStatus
almarklein Mar 14, 2025
8f17d43
Need the null-string
almarklein Mar 14, 2025
c179d09
map-read workaround still needed. Update test/comments a bit
almarklein Mar 14, 2025
ec89060
comment on textureview.usage
almarklein Mar 14, 2025
21905cf
Merge branch 'main' into wgpu24
almarklein Mar 14, 2025
3cec914
codegen
almarklein Mar 14, 2025
d47cc9a
the new test failed intermittendly
almarklein Mar 14, 2025
e9cbac6
Use to_c_string_view for labels too
almarklein Mar 14, 2025
632bf24
avoid hardcoded enum value
almarklein Mar 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion codegen/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ def read_file(*fname):
"""Read a file from disk using the relative filename. Line endings are normalized."""
filename = os.path.join(lib_dir, *fname)
with open(filename, "rb") as f:
return f.read().decode().replace("\r\n", "\n").replace("\r", "\n")
return (
f.read()
.decode()
.replace("\r\n", "\n")
.replace("\r", "\n")
.replace("\\\n", "") # Macros escaping a newline
)


class FileCache:
Expand Down
62 changes: 51 additions & 11 deletions codegen/hparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,15 @@ def _parse_from_h(self):
assert name1.startswith("WGPU")
name = name1[4:]
self.enums[name] = enum = {}
for f in code[i2 + 1 : i3].strip().strip(";").split(","):
f = remove_c_comments(f).strip()
code_block = code[i2 + 1 : i3].strip().strip(";")
block = remove_c_comments(code_block).strip()
for f in block.split(","):
if not f:
continue # happens when last item has a comma
continue # no item after last comma
key, _, val = f.partition("=")
# Handle key
key = key.strip()
assert key.startswith("WGPU") and "_" in key
assert key.startswith("WGPU") and "_" in key, f"key={key}"
key = key.split("_", 1)[1]
# Turn value into an int
val = val.strip()
Expand All @@ -120,18 +121,38 @@ def _parse_from_h(self):
else:
enum[key] = int(val)

# Turn some enums into flags
for line in code.splitlines():
# collect flags
# schme: typedef WGPUFlags WGPUFlagName;
if line.startswith("typedef WGPUFlags "):
parts = line.strip().strip(";").split()
assert len(parts) == 3
name = parts[-1]
if name.endswith("Flags"):
assert name.startswith("WGPU")
name1 = name[4:-1] # xxFlags -> xxFlag
name2 = name[4:-5] # xxFlags -> xx
name = name1 if name1 in self.enums else name2
self.flags[name] = self.enums.pop(name)
assert name.startswith("WGPU")
name = name[4:]
assert not name.endswith("Flags"), "XxxxFlags should not longer exist"
assert name not in self.enums, "flags used to look like enums"
self.flags[name] = {}

# fill flags
# schema: static const WGPUFlagName WGPUFlagName_Value = 0x0000000000000001;
if line.startswith("static const"):
line = remove_c_comments(line).strip()
flag_name = line.removeprefix("static const").lstrip().split()[0]
flag_key, _, val = (
line.removeprefix(f"static const {flag_name}")
.strip()
.rstrip(";")
.partition("=")
)
# Check / normalize flag_name
assert flag_name.startswith("WGPU")
flag_name = flag_name[4:]
assert flag_name in self.flags
# Check / normalize flag_key
assert flag_key.startswith(f"WGPU{flag_name}_")
flag_key = flag_key.partition("_")[2].strip()
self.flags[flag_name][flag_key] = self._parse_val_to_int(val)

# Collect structs. This is relatively easy, since we only need the C code.
# But we don't deal with union structs.
Expand Down Expand Up @@ -229,3 +250,22 @@ def _parse_from_cffi(self):
while alt_name != ffi.getctype(alt_name):
alt_name = ffi.getctype(alt_name)
self.structs[alt_name] = self.structs[name]

def _parse_val_to_int(self, val):
"""
little helper to parse complex values for enums.
"""
val = val.strip(" ()")
if "|" in val:
# recursively handle the "OR" values?
res_val = 0
for sub_val in val.split("|"):
res_val |= self._parse_val_to_int(sub_val)
return res_val
if val.startswith("0x"):
return int(val, 16)
elif "<<" in val:
val1, _, val2 = val.partition("<<")
return int(val1) << int(val2)
else:
return int(val)
8 changes: 4 additions & 4 deletions codegen/tests/test_codegen_rspatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def test_patch_structs():
"""
code2 = patch_wgpu_native_backend(dedent(code1))
assert all(line[4:] in code2 for line in code1 if line.strip())
assert "usage: WGPUBufferUsageFlags/int" in code2
assert "usage: WGPUBufferUsage/int" in code2
assert "size: int" in code2
assert "# FIXME:" not in code2
assert code2 == patch_wgpu_native_backend(code2) # Don't stack comments
Expand All @@ -53,7 +53,7 @@ def test_patch_structs():
"""
code2 = patch_wgpu_native_backend(dedent(code1))
assert all(line[4:] in code2 for line in code1 if line.strip())
assert "usage: WGPUBufferUsageFlags/int" in code2
assert "usage: WGPUBufferUsage/int" in code2
assert "size: int" in code2
assert "# FIXME:" not in code2

Expand All @@ -72,15 +72,15 @@ def test_patch_structs():
# Missing values
code1 = 'struct = new_struct_p("WGPUBufferDescriptor *",label=c_label,size=size,)'
code2 = patch_wgpu_native_backend(dedent(code1))
assert "usage: WGPUBufferUsageFlags/int" in code2
assert "usage: WGPUBufferUsage/int" in code2
assert "# FIXME:" not in code2
assert "usage" in code2 # comment added
assert code2 == patch_wgpu_native_backend(code2) # Don't stack comments

# Too many values
code1 = 'struct = new_struct_p("WGPUBufferDescriptor *",label=c_label,foo=size,)'
code2 = patch_wgpu_native_backend(dedent(code1))
assert "usage: WGPUBufferUsageFlags/int" in code2
assert "usage: WGPUBufferUsage/int" in code2
assert "# FIXME: unknown" in code2
assert code2 == patch_wgpu_native_backend(code2) # Don't stack comments

Expand Down
81 changes: 50 additions & 31 deletions codegen/wgpu_native_patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import re
from collections import defaultdict
from itertools import chain

from codegen.utils import print, format_code, Patcher, to_snake_case
from codegen.hparser import get_h_parser
Expand Down Expand Up @@ -290,42 +291,47 @@ def apply(self, code):
class StructPatcher(Patcher):
def apply(self, code):
self._init(code)
hp = get_h_parser()

count = 0
line_index = -1
brace_depth = 0

for line, i in self.iter_lines():
# detect struct definition start (only when not already inside a struct)
if "new_struct_p(" in line or "new_struct(" in line:
if line.lstrip().startswith("def "):
continue # Implementation
if "_new_struct" in line:
continue # Implementation
if "new_struct_p()" in line or "new_struct()" in line:
continue # Comments or docs
line_index = i
j = line.index("new_struct")
line = line[j:] # start brace searching from right pos
brace_depth = 0

if line_index >= 0:
for c in line:
if c == "#":
break
elif c == "(":
brace_depth += 1
elif c == ")":
brace_depth -= 1
assert brace_depth >= 0
if brace_depth == 0:
self._validate_struct(hp, line_index, i)
count += 1
line_index = -1
break
# call a function to find the end and validate so we don't skip over nested structure in this loop
self._find_struct_end(line, i)
count += 1

print(f"Validated {count} C structs")

def _find_struct_end(self, first_line, start_line_index):
"""
If a struct start is found, this function will find the end of the struct and then validate it
"""
brace_depth = 0
hp = get_h_parser()
for line, i in chain(
[(first_line, 0)], self.iter_lines(start_line=start_line_index + 1)
):
for c in line:
if c == "#":
break
elif c == "(":
brace_depth += 1
elif c == ")":
brace_depth -= 1
assert brace_depth >= 0
if brace_depth == 0:
# here we have found the end of this struct so now we can validate it.
self._validate_struct(hp, start_line_index, i)
return # we assume it always works?

def _validate_struct(self, hp, i1, i2):
"""Validate a specific struct usage."""

Expand Down Expand Up @@ -381,19 +387,32 @@ def _validate_struct(self, hp, i1, i2):
self.insert_line(i1, indent + "# H: " + fields)

# Check keys
brace_count = 0
keys_found = []
for j in range(2, len(lines) - 1):
line = lines[j]
key = line.split("=")[0].strip()
if key.startswith("# not used:"):
key = key.split(":")[1].split("=")[0].strip()
elif key.startswith("#"):
continue
keys_found.append(key)
if key not in struct:
msg = f"unknown C struct field {struct_name}.{key}"
self.insert_line(i1 + j, f"{indent}# FIXME: {msg}")
print(f"ERROR: {msg}")
if brace_count <= 0:
# ready to find a new key candidate
key = line.split("=")[0].strip()
if key.startswith("# not used:"):
key = key.split(":")[1].split("=")[0].strip()
elif key.startswith("#"):
continue
# (still) inside a multi-line structure
for c in line.strip():
if c == "#":
break # we can ignore this line past the comment in terms of nested structures
elif c == "(":
brace_count += 1
elif c == ")":
brace_count -= 1
if brace_count == 0:
# here we finished through the multi line structure
keys_found.append(key)
if key not in struct:
msg = f"unknown C struct field {struct_name}.{key}"
self.insert_line(i1 + j, f"{indent}# FIXME: {msg}")
print(f"ERROR: {msg}")

# Insert comments for unused keys
more_lines = []
Expand Down
34 changes: 29 additions & 5 deletions tests/test_set_constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,39 @@ def test_normal_push_constants():
assert all(result == expected_result)


def test_render_bundle_push_constants_fails():
def test_render_bundle_push_constants():
device, pipeline, render_pass_descriptor = setup_pipeline()
encoder = device.create_render_bundle_encoder(
vertex_call_buffer = device.create_buffer(size=COUNT * 4, usage="STORAGE|COPY_SRC")
bind_group = device.create_bind_group(
layout=pipeline.get_bind_group_layout(0),
entries=[
{"binding": 0, "resource": {"buffer": vertex_call_buffer}},
],
)
bundle_encoder = device.create_render_bundle_encoder(
color_formats=[TextureFormat.rgba8unorm],
)
encoder.set_pipeline(pipeline)
bundle_encoder.set_pipeline(pipeline)
bundle_encoder.set_bind_group(0, bind_group)
buffer = np.random.randint(0, 1_000_000, size=(2 * COUNT), dtype=np.uint32)
with pytest.raises(RuntimeError):
set_push_constants(encoder, "VERTEX", 0, COUNT * 4, buffer)
set_push_constants(bundle_encoder, "VERTEX", 0, COUNT * 4, buffer)
set_push_constants(
bundle_encoder, "FRAGMENT", COUNT * 4, COUNT * 4, buffer, COUNT * 4
)
bundle_encoder.draw(COUNT)
render_bundle = bundle_encoder.finish()

# a different encoder to execute the bundle
encoder = device.create_command_encoder()
this_pass = encoder.begin_render_pass(**render_pass_descriptor)
this_pass.execute_bundles([render_bundle])
this_pass.end()
device.queue.submit([encoder.finish()])

info_view = device.queue.read_buffer(vertex_call_buffer)
result = np.frombuffer(info_view, dtype=np.uint32)
expected_result = buffer[0:COUNT] + buffer[COUNT:]
assert all(result == expected_result)


def test_bad_set_push_constants():
Expand Down
11 changes: 8 additions & 3 deletions tests/test_wgpu_native_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,9 +549,14 @@ def test_create_buffer_with_data(size):


@pytest.mark.skip
def test_show_bug_wgpu_native_305_still_not_fixed():
# When this bug is fixed, we can remove READ_NOSYNC, and just tread "READ" as if
# it were READ_NOSYNC. No need to handle the command buffer.
def test_that_passes_when_bug_wgpu_native_305_is_fixed():
# See https://github.com/gfx-rs/wgpu-native/issues/305
# If the bug is still there, this test will fail. Therefore its skipped.
# If this test passes (on multiple machines) the bug is likely fixed.
# When this bug is fixed, we can remove the command-buffer trick in _api.py,
# and enable this test to detect regressions.

# Update
device = wgpu.utils.get_default_device()
data1 = b"abcdefghijkl"

Expand Down
25 changes: 14 additions & 11 deletions tests/test_wgpu_native_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ def test_parse_shader_error2(caplog):
Caused by:
In wgpuDeviceCreateShaderModule

Shader '' parsing error: expected ',', found ';'
Shader '' parsing error: expected `,`, found ";"
┌─ wgsl:2:38
2 │ @location(0) texcoord : vec2<f32>;
│ ^ expected ','
│ ^ expected `,`


expected ',', found ';'
expected `,`, found ";"
"""

code = dedent(code)
Expand Down Expand Up @@ -103,16 +103,14 @@ def test_parse_shader_error3(caplog):
Caused by:
In wgpuDeviceCreateShaderModule

Shader '' parsing error: unknown scalar type: 'f3'
Shader '' parsing error: unknown type: `f3`
┌─ wgsl:3:39
3 │ @builtin(position) position: vec4<f3>,
│ ^^ unknown scalar type
= note: Valid scalar types are f32, f64, i32, u32, bool
│ ^^ unknown type


unknown scalar type: 'f3'
unknown type: `f3`
"""

code = dedent(code)
Expand Down Expand Up @@ -183,16 +181,19 @@ def test_validate_shader_error1(caplog):
Caused by:
In wgpuDeviceCreateShaderModule

Shader validation error:
Shader validation error: Entry point vs_main at Vertex is invalid
┌─ :10:20
10 │ out.position = matrics * out.position;
│ ^^^^^^^^^^^^^^^^^^^^^^ naga::Expression [7]
= Expression [7] is invalid
= Operation Multiply can't work with [4] (of type Matrix { columns: Quad, rows: Quad, scalar: Scalar { kind: Float, width: 4 } }) and [6] (of type Vector { size: Tri, scalar: Scalar { kind: Float, width: 4 } })


Entry point vs_main at Vertex is invalid
Expression [7] is invalid
Operation Multiply can't work with [4] and [6]
Operation Multiply can't work with [4] (of type Matrix { columns: Quad, rows: Quad, scalar: Scalar { kind: Float, width: 4 } }) and [6] (of type Vector { size: Tri, scalar: Scalar { kind: Float, width: 4 } })
"""

code = dedent(code)
Expand Down Expand Up @@ -233,11 +234,13 @@ def test_validate_shader_error2(caplog):
Caused by:
In wgpuDeviceCreateShaderModule

Shader validation error:
Shader validation error: Entry point fs_main at Vertex is invalid
┌─ :9:16
9 │ return vec3<f32>(1.0, 0.0, 1.0);
│ ^^^^^^^^^^^^^^^^^^^^^^^^ naga::Expression [8]
= The `return` value Some([8]) does not match the function return value


Entry point fs_main at Vertex is invalid
Expand Down
Loading