Skip to content
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

Update wgpu-native to v0.19.1.1 #458

Merged
merged 19 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
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
18 changes: 9 additions & 9 deletions examples/tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import pytest


from testutils import (
from tests.testutils import (
almarklein marked this conversation as resolved.
Show resolved Hide resolved
can_use_wgpu_lib,
wgpu_backend,
is_lavapipe,
Expand All @@ -37,14 +37,6 @@
examples_to_test = find_examples(query="# test_example = true", return_stems=True)


@pytest.mark.parametrize("module", examples_to_run)
def test_examples_run(module, force_offscreen):
"""Run every example marked to see if they can run without error."""
# use runpy so the module is not actually imported (and can be gc'd)
# but also to be able to run the code in the __main__ block
runpy.run_module(f"examples.{module}", run_name="__main__")


@pytest.fixture
def force_offscreen():
"""Force the offscreen canvas to be selected by the auto gui module."""
Expand Down Expand Up @@ -145,6 +137,14 @@ def update_diffs(module, is_similar, img, stored_img):
path.unlink()


@pytest.mark.parametrize("module", examples_to_run)
def test_examples_run(module, force_offscreen):
"""Run every example marked to see if they can run without error."""
# use runpy so the module is not actually imported (and can be gc'd)
# but also to be able to run the code in the __main__ block
runpy.run_module(f"examples.{module}", run_name="__main__")


if __name__ == "__main__":
# Enable tweaking in an IDE by running in an interactive session.
os.environ["WGPU_FORCE_OFFSCREEN"] = "true"
Expand Down
5 changes: 4 additions & 1 deletion tests/test_gui_glfw.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
import time
import weakref
import asyncio
import gc

import wgpu
from pytest import skip
from testutils import run_tests, can_use_glfw, can_use_wgpu_lib
from testutils import run_tests, can_use_glfw, can_use_wgpu_lib, is_pypy
from renderutils import render_to_texture, render_to_screen # noqa


Expand Down Expand Up @@ -83,6 +84,8 @@ async def miniloop():
loop.run_until_complete(miniloop())
assert ref() is not None
del canvas
if is_pypy:
gc.collect() # force garbage collection for pypy
loop.run_until_complete(miniloop())
assert ref() is None

Expand Down
20 changes: 5 additions & 15 deletions tests/test_wgpu_native_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,20 +141,10 @@ def test_parse_shader_error4(caplog):
Caused by:
In wgpuDeviceCreateShaderModule

Shader validation error:
┌─ :1:1
1 │ ╭ fn foobar() {
2 │ │ let m = mat2x2<f32>(0.0, 0.0, 0.0, 0.);
3 │ │ let scales = m[4];
│ │ ^^^^ naga::Expression [9]
│ ╰──────────────────────^ naga::Function [1]
Shader '' parsing error: Index 4 is out of bounds for expression [11]


Function [1] 'foobar' is invalid
Expression [9] is invalid
Type resolution failed
Index 4 is out of bounds for expression [7]
Index 4 is out of bounds for expression [11]
"""

code = dedent(code)
Expand Down Expand Up @@ -185,8 +175,8 @@ def test_validate_shader_error1(caplog):
}
"""

expected1 = """Left: Load { pointer: [3] } of type Matrix { columns: Quad, rows: Quad, width: 4 }"""
expected2 = """Right: Load { pointer: [6] } of type Vector { size: Tri, kind: Float, width: 4 }"""
expected1 = """Left: Load { pointer: [3] } of type Matrix { columns: Quad, rows: Quad, scalar: Scalar { kind: Float, width: 4 } }"""
expected2 = """Right: Load { pointer: [6] } of type Vector { size: Tri, scalar: Scalar { kind: Float, width: 4 } }"""
expected3 = """
Validation Error

Expand Down Expand Up @@ -236,7 +226,7 @@ def test_validate_shader_error2(caplog):
}
"""

expected1 = """Returning Some(Vector { size: Tri, kind: Float, width: 4 }) where Some(Vector { size: Quad, kind: Float, width: 4 }) is expected"""
expected1 = """Returning Some(Vector { size: Tri, scalar: Scalar { kind: Float, width: 4 } }) where Some(Vector { size: Quad, scalar: Scalar { kind: Float, width: 4 } }) is expected"""
expected2 = """
Validation Error

Expand Down
4 changes: 4 additions & 0 deletions tests/test_wgpu_native_query_set.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import wgpu.utils
import gc

from testutils import run_tests, can_use_wgpu_lib
from tests_mem.testutils import is_pypy
from pytest import mark


@mark.skipif(not can_use_wgpu_lib, reason="Needs wgpu lib")
def test_query_set():
if is_pypy:
almarklein marked this conversation as resolved.
Show resolved Hide resolved
gc.collect() # avoid a panic here when using pypy
shader_source = """
@group(0) @binding(0)
var<storage,read> data1: array<f32>;
Expand Down
7 changes: 7 additions & 0 deletions tests_mem/test_gui_offscreen.py → tests_mem/test_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ def test_release_canvas_context(n):
# Check that the canvas objects are really deleted
assert not canvases

# This is a bit weird, but somehow this tests produces a dangling
# CommandBuffer for reasons likely related to the internals of
# wgpu-core. The lines below allocate and release a new
# CommandBuffer, which solves the issue :)
command_encoder = DEVICE.create_command_encoder()
command_encoder.finish()


TEST_FUNCS = [test_release_canvas_context]

Expand Down
10 changes: 9 additions & 1 deletion tests_mem/test_gui_glfw.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
import weakref
import asyncio

import wgpu
import pytest
import testutils # noqa
from testutils import create_and_release, can_use_glfw, can_use_wgpu_lib
from test_gui_offscreen import make_draw_func_for_canvas
from test_gui import make_draw_func_for_canvas


if not can_use_wgpu_lib:
Expand All @@ -22,6 +23,9 @@
pytest.skip("Asyncio loop is running", allow_module_level=True)


DEVICE = wgpu.utils.get_default_device()


async def stub_event_loop():
pass

Expand Down Expand Up @@ -57,6 +61,10 @@ def test_release_canvas_context(n):
# Check that the canvas objects are really deleted
assert not canvases, f"Still {len(canvases)} canvases"

# Help clear dangling CommandBuffer, see test_gui.py
command_encoder = DEVICE.create_command_encoder()
command_encoder.finish()


if __name__ == "__main__":
# testutils.TEST_ITERS = 40 # Uncomment for a mem-usage test run
Expand Down
10 changes: 9 additions & 1 deletion tests_mem/test_gui_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
import gc
import weakref

import wgpu
import pytest
import testutils # noqa
from testutils import create_and_release, can_use_pyside6, can_use_wgpu_lib
from test_gui_offscreen import make_draw_func_for_canvas
from test_gui import make_draw_func_for_canvas


if not can_use_wgpu_lib:
Expand All @@ -17,6 +18,9 @@
pytest.skip("Need pyside6 for this test", allow_module_level=True)


DEVICE = wgpu.utils.get_default_device()


@create_and_release
def test_release_canvas_context(n):
# Test with PySide canvases.
Expand Down Expand Up @@ -51,6 +55,10 @@ def test_release_canvas_context(n):
# Check that the canvas objects are really deleted
assert not canvases

# Help clear dangling CommandBuffer, see test_gui.py
command_encoder = DEVICE.create_command_encoder()
command_encoder.finish()


if __name__ == "__main__":
# testutils.TEST_ITERS = 40 # Uncomment for a mem-usage test run
Expand Down
2 changes: 1 addition & 1 deletion tests_mem/test_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from testutils import can_use_wgpu_lib, create_and_release
from testutils import get_counts, ob_name_from_test_func
from test_objects import TEST_FUNCS as OBJECT_TEST_FUNCS
from test_gui_offscreen import TEST_FUNCS as GUI_TEST_FUNCS
from test_gui import TEST_FUNCS as GUI_TEST_FUNCS


ALL_TEST_FUNCS = OBJECT_TEST_FUNCS + GUI_TEST_FUNCS
Expand Down
19 changes: 9 additions & 10 deletions tests_mem/test_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,11 @@ def test_release_adapter(n):

@create_and_release
def test_release_device(n):
pytest.skip("XFAIL")
# todo: XFAIL: Device object seem not to be cleaned up at wgpu-native.

# Note: the WebGPU spec says:
# [request_device()] is a one-time action: if a device is returned successfully, the adapter becomes invalid.

yield {
"expected_counts_after_create": {"Device": (n, n), "Queue": (n, 0)},
"expected_counts_after_create": {"Device": (n, n), "Queue": (n, n)},
}
adapter = DEVICE.adapter
for i in range(n):
Expand Down Expand Up @@ -82,9 +79,7 @@ def test_release_bind_group_layout(n):
global _bind_group_layout_binding
_bind_group_layout_binding += 1

yield {
"expected_counts_after_create": {"BindGroupLayout": (n, 1)},
}
yield {}

binding_layouts = [
{
Expand Down Expand Up @@ -193,9 +188,13 @@ def test_release_query_set(n):

@create_and_release
def test_release_queue(n):
pytest.skip("XFAIL")
# todo: XFAIL: the device and queue are kinda one, and the former won't release at wgpu-native.
yield {}
# Note: cannot create a queue directly, so we create devices, which gave queue's attached.
yield {
"expected_counts_after_create": {
"Device": (n, n),
"Queue": (n, n),
},
}
adapter = DEVICE.adapter
for i in range(n):
d = adapter.request_device()
Expand Down
4 changes: 2 additions & 2 deletions wgpu/backends/wgpu_native/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@


# The wgpu-native version that we target/expect
__version__ = "0.18.1.3"
__commit_sha__ = "8561b0d8c0b5af7dfb8631d6f924e5418c92f2ce"
__version__ = "0.19.1.1"
__commit_sha__ = "569a2be60d1dc90a660c1c96ffb3722942ada782"
version_info = tuple(map(int, __version__.split(".")))
_check_expected_version(version_info) # produces a warning on mismatch

Expand Down
12 changes: 5 additions & 7 deletions wgpu/backends/wgpu_native/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1701,7 +1701,8 @@ def map(self, mode, offset=0, size=None):
if sync_on_read and map_mode & lib.WGPUMapMode_Read:
if self._mapped_status[2] == 0 and self._usage & flags.BufferUsage.MAP_READ:
encoder = self._device.create_command_encoder()
self._device.queue.submit([encoder.finish()])
command_buffer = encoder.finish()
self._device.queue.submit([command_buffer])

status = 999

Expand Down Expand Up @@ -2000,12 +2001,9 @@ def _destroy(self):

class GPUCommandBuffer(classes.GPUCommandBuffer, GPUObjectBase):
def _destroy(self):
# Since command buffers get destroyed when you submit them, we
# must only release them if they've not been submitted, or we get
# 'Cannot remove a vacant resource'. Got this info from the
# wgpu chat. Also see
# https://docs.rs/wgpu-core/latest/src/wgpu_core/device/mod.rs.html#4180-4194
# --> That's why _internal is set to None in Queue.submit()
# Note that command buffers get destroyed when they are submitted.
# In earlier versions we had to take this into account by setting
# _internal to None. That seems not necessary anymore.
if self._internal is not None and libf is not None:
self._internal, internal = None, self._internal
# H: void f(WGPUCommandBuffer commandBuffer)
Expand Down
50 changes: 30 additions & 20 deletions wgpu/backends/wgpu_native/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ def generate_report():
but in the form of a Python dict.
"""

# H: surfaces: WGPUStorageReport, backendType: WGPUBackendType, vulkan: WGPUHubReport, metal: WGPUHubReport, dx12: WGPUHubReport, dx11: WGPUHubReport, gl: WGPUHubReport
# H: surfaces: WGPURegistryReport, backendType: WGPUBackendType, vulkan: WGPUHubReport, metal: WGPUHubReport, dx12: WGPUHubReport, gl: WGPUHubReport
struct = ffi.new("WGPUGlobalReport *")

# H: void f(WGPUInstance instance, WGPUGlobalReport * report)
Expand All @@ -332,33 +332,35 @@ def generate_report():
report = {}

report["surfaces"] = {
"occupied": struct.surfaces.numOccupied,
"vacant": struct.surfaces.numVacant,
"allocated": struct.surfaces.numAllocated,
"kept": struct.surfaces.numKeptFromUser,
"released": struct.surfaces.numReleasedFromUser,
"error": struct.surfaces.numError,
"element_size": struct.surfaces.elementSize,
}

for backend in ("vulkan", "metal", "dx12", "dx11", "gl"):
for backend in ("vulkan", "metal", "dx12", "gl"):
c_hub_report = getattr(struct, backend)
report[backend] = {}
for key in dir(c_hub_report):
c_storage_report = getattr(c_hub_report, key)
storage_report = {
"occupied": c_storage_report.numOccupied,
"vacant": c_storage_report.numVacant,
"error": c_storage_report.numError,
"element_size": c_storage_report.elementSize,
c_registry_report = getattr(c_hub_report, key)
registry_report = {
"allocated": c_registry_report.numAllocated,
"kept": c_registry_report.numKeptFromUser,
"released": c_registry_report.numReleasedFromUser,
"error": c_registry_report.numError,
"element_size": c_registry_report.elementSize,
}
# if any(x!=0 for x in storage_report.values()):
report[backend][key] = storage_report
# if any(x!=0 for x in registry_report.values()):
report[backend][key] = registry_report

return report


class WgpuNativeCountsDiagnostics(Diagnostics):
def get_subscript(self):
text = ""
text += " * The o, v, e are occupied, vacant and error, respecitively.\n"
text += " * The a, k, r, e are allocated, kept, released, and error, respectively.\n"
text += " * Reported memory does not include buffer/texture data.\n"
return text

Expand All @@ -383,23 +385,30 @@ def get_dict(self):
for report_name in sorted(name_map[name] for name in names + root_names):
result[report_name] = {"count": 0, "mem": 0}

# The field names to add together to obtain a representation for
# the number of objects "allocated" by wgpu-core. In practice,
# wgpu-core can keep objects around for re-use, which is why "allocated"
# and released" are not in this equation.
fields_to_add = ["kept", "error"]

# Establish what backends are active
active_backends = []
for backend in backends:
total = 0
for name in names:
d = native_report[backend][name]
total += d["occupied"] + d["vacant"] + d["error"]
total += sum(d[k] for k in fields_to_add)
if total > 0:
active_backends.append(backend)

# Process names in the root
for name in root_names:
d = native_report[name]
subtotal_count = d["occupied"] + d["vacant"] + d["error"]
subtotal_count = sum(d[k] for k in fields_to_add)
impl = {
"o": d["occupied"],
"v": d["vacant"],
"a": d["allocated"],
"k": d["kept"],
"r": d["released"],
"e": d["error"],
"el_size": d["element_size"],
}
Expand All @@ -416,11 +425,12 @@ def get_dict(self):
implementations = {}
for backend in active_backends:
d = native_report[backend][name]
subtotal_count = d["occupied"] + d["vacant"] + d["error"]
subtotal_count = sum(d[k] for k in fields_to_add)
subtotal_mem = subtotal_count * d["element_size"]
impl = {
"o": d["occupied"],
"v": d["vacant"],
"a": d["allocated"],
"k": d["kept"],
"r": d["released"],
"e": d["error"],
"el_size": d["element_size"],
}
Expand Down
Loading
Loading