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 to wgpu-native v0.18.1.1 #429

Merged
merged 21 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@ Possible sections in each release:
* Security: in case of vulnerabilities.


### [v0.13.0] - TBD

Changed:

* Update to wgpu-native 0.18.1.1.
* `CanvasContext.get_current_texture()` now returns a `GPUTexture` instead of a `GPUTextureView`.
* `OffscreenCanvas.present()` now receives a `GPUTexture` instead of a `GPUTextureView`,
and this is a new texture on each draw (no re-use).
* `GPUCommandEncoder.begin_render_pass()` binds the lifetime of passed texture views to
the returned render pass object to prevent premature destruction when no reference to
a texture view is kept.

Fixed:

* Dragging a window between windows with different scale factor (with Qt on Windows) no longer
puts the window in an invalid state. A warning is still produced though.
* Fixed that the canonical class name in the `__repr__` of GPU classes was changed in cases where it shouldn't.


### [v0.12.0] - 15-11-2023

This is a big release that contains many improvements, but also multiple API changes.
Expand Down
4 changes: 3 additions & 1 deletion codegen/hparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ def _parse_from_h(self):
name = parts[-1]
if name.endswith("Flags"):
assert name.startswith("WGPU")
name = name[4:-5]
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)

# Collect structs. This is relatively easy, since we only need the C code.
Expand Down
22 changes: 20 additions & 2 deletions codegen/wgpu_native_patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,31 @@ def write_mappings():
pylines.append("}")

# Write a few native-only mappings: int => key
# If possible, resolve to WebGPU names, otherwise use the native name.
pylines.append("enum_int2str = {")
for name in ["BackendType", "AdapterType", "ErrorType", "DeviceLostReason"]:
for name in [
"BackendType",
"AdapterType",
"ErrorType",
"DeviceLostReason",
"TextureFormat",
"TextureDimension",
"PresentMode",
"CompositeAlphaMode",
]:
webgpu_names = {}
if name in idl.enums:
webgpu_names = {
val.replace("-", ""): val for val in idl.enums[name].values()
}
if "unknown" in webgpu_names:
webgpu_names["undefined"] = "unknown"
pylines.append(f' "{name}":' + " {")
for key, val in hp.enums[name].items():
if key == "Force32":
continue
pylines.append(f' {val}: "{key}",')
enum_val = webgpu_names.get(key.lower(), key)
pylines.append(f' {val}: "{enum_val}",')
pylines.append(" },")
pylines.append("}")

Expand Down
2 changes: 1 addition & 1 deletion examples/cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ def draw_frame():
tmp_buffer, 0, uniform_buffer, 0, uniform_data.nbytes
)

current_texture_view = present_context.get_current_texture()
current_texture_view = present_context.get_current_texture().create_view()
render_pass = command_encoder.begin_render_pass(
color_attachments=[
{
Expand Down
4 changes: 2 additions & 2 deletions examples/triangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,13 @@ def _main(canvas, device):
)

def draw_frame():
current_texture_view = present_context.get_current_texture()
current_texture = present_context.get_current_texture()
command_encoder = device.create_command_encoder()

render_pass = command_encoder.begin_render_pass(
color_attachments=[
{
"view": current_texture_view,
"view": current_texture.create_view(),
"resolve_target": None,
"clear_value": (0, 0, 0, 1),
"load_op": wgpu.LoadOp.clear,
Expand Down
2 changes: 1 addition & 1 deletion tests/renderutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ def render_to_screen(
present_context.configure(device=device, format=None)

def draw_frame():
current_texture_view = present_context.get_current_texture()
current_texture_view = present_context.get_current_texture().create_view()
command_encoder = device.create_command_encoder()

ca = color_attachment or {
Expand Down
51 changes: 44 additions & 7 deletions tests/test_gui_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,26 +79,32 @@ def test_canvas_logging(caplog):


class MyOffscreenCanvas(wgpu.gui.WgpuOffscreenCanvas):
def __init__(self):
super().__init__()
self.textures = []
self.physical_size = 100, 100

def get_pixel_ratio(self):
return 1

def get_logical_size(self):
return self.get_physical_size()

def get_physical_size(self):
return 100, 100
return self.physical_size

def _request_draw(self):
# Note: this would normaly schedule a call in a later event loop iteration
self._draw_frame_and_present()

def present(self, texture_view):
device = texture_view._device
size = texture_view.size
def present(self, texture):
self.textures.append(texture)
device = texture._device
size = texture.size
bytes_per_pixel = 4
data = device.queue.read_texture(
{
"texture": texture_view.texture,
"texture": texture,
"mip_level": 0,
"origin": (0, 0, 0),
},
Expand All @@ -119,9 +125,8 @@ def test_offscreen_canvas():
present_context = canvas.get_context()
present_context.configure(device=device, format=None)

@canvas.request_draw
def draw_frame():
current_texture_view = present_context.get_current_texture()
current_texture_view = present_context.get_current_texture().create_view()
command_encoder = device.create_command_encoder()
render_pass = command_encoder.begin_render_pass(
color_attachments=[
Expand All @@ -137,10 +142,42 @@ def draw_frame():
render_pass.end()
device.queue.submit([command_encoder.finish()])

assert len(canvas.textures) == 0

# Draw 1
canvas.request_draw(draw_frame)
assert canvas.array.shape == (100, 100, 4)
assert np.all(canvas.array[:, :, 0] == 0)
assert np.all(canvas.array[:, :, 1] == 255)

# Draw 2
canvas.request_draw(draw_frame)
assert canvas.array.shape == (100, 100, 4)
assert np.all(canvas.array[:, :, 0] == 0)
assert np.all(canvas.array[:, :, 1] == 255)

# Change resolution
canvas.physical_size = 120, 100

# Draw 3
canvas.request_draw(draw_frame)
assert canvas.array.shape == (100, 120, 4)
assert np.all(canvas.array[:, :, 0] == 0)
assert np.all(canvas.array[:, :, 1] == 255)

# Change resolution
canvas.physical_size = 120, 140

# Draw 4
canvas.request_draw(draw_frame)
assert canvas.array.shape == (140, 120, 4)
assert np.all(canvas.array[:, :, 0] == 0)
assert np.all(canvas.array[:, :, 1] == 255)

# We now have four unique texture objects
assert len(canvas.textures) == 4
assert len(set(canvas.textures)) == 4


def test_autogui_mixin():
c = wgpu.gui.WgpuAutoGui()
Expand Down
4 changes: 2 additions & 2 deletions tests/test_gui_glfw.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
Test the canvas, and parts of the rendering that involves a canvas,
like the swap chain.
like the canvas context and surface texture.
"""

import os
Expand Down Expand Up @@ -269,7 +269,7 @@ def _get_draw_function(device, canvas):
)

def draw_frame():
current_texture_view = present_context.get_current_texture()
current_texture_view = present_context.get_current_texture().create_view()
command_encoder = device.create_command_encoder()
assert current_texture_view.size
ca = {
Expand Down
3 changes: 2 additions & 1 deletion tests_mem/test_gui_glfw.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ def test_release_canvas_context(n):
loop.run_until_complete(stub_event_loop())
gc.collect()
loop.run_until_complete(stub_event_loop())
gc.collect()

# Check that the canvas objects are really deleted
assert not canvases
assert not canvases, f"Still {len(canvases)} canvases"


if __name__ == "__main__":
Expand Down
13 changes: 6 additions & 7 deletions tests_mem/test_gui_offscreen.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def make_draw_func_for_canvas(canvas):
def draw():
ctx = canvas.get_context()
command_encoder = DEVICE.create_command_encoder()
current_texture_view = ctx.get_current_texture()
current_texture_view = ctx.get_current_texture().create_view()
render_pass = command_encoder.begin_render_pass(
color_attachments=[
{
Expand All @@ -42,7 +42,6 @@ def draw():
)
render_pass.end()
DEVICE.queue.submit([command_encoder.finish()])
ctx.present()

return draw

Expand All @@ -51,17 +50,16 @@ def draw():
def test_release_canvas_context(n):
# Test with offscreen canvases. A context is created, but not a wgpu-native surface.

# Note: the offscreen canvas keeps the render-texture-view alive, since it
# is used to e.g. download the resulting image. That's why we also see
# Textures and TextureViews in the counts.
# Note: the offscreen canvas keeps the render-texture alive, since it
# is used to e.g. download the resulting image, and who knows how the
# user want to use the result. The context does drop its ref to the
# textures, which is why we don't see textures in the measurements.

from wgpu.gui.offscreen import WgpuCanvas

yield {
"expected_counts_after_create": {
"CanvasContext": (n, 0),
"Texture": (n, n),
"TextureView": (n, n),
},
}

Expand All @@ -77,6 +75,7 @@ def test_release_canvas_context(n):
gc.collect()
if is_pypy:
gc.collect() # Need a bit more on pypy :)
gc.collect()

# Check that the canvas objects are really deleted
assert not canvases
Expand Down
50 changes: 25 additions & 25 deletions wgpu/_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,38 +174,35 @@ def configure(
):
"""Configures the presentation context for the associated canvas.
Destroys any textures produced with a previous configuration.
This clears the drawing buffer to transparent black.

Arguments:
device (WgpuDevice): The GPU device object.
format (enums.TextureFormat): The texture format, e.g. "bgra8unorm-srgb".
Default uses the preferred_format.
device (WgpuDevice): The GPU device object to create compatible textures for.
format (enums.TextureFormat): The format that textures returned by
``get_current_texture()`` will have. Must be one of the supported context
formats. An often used format is "bgra8unorm-srgb".
usage (flags.TextureUsage): Default ``TextureUsage.OUTPUT_ATTACHMENT``.
color_space (PredefinedColorSpace): Default "srgb".
alpha_mode (enums.CanvasAlphaMode): Default opaque.
view_formats (List[enums.TextureFormat]): The formats that views created
from textures returned by ``get_current_texture()`` may use.
color_space (PredefinedColorSpace): The color space that values written
into textures returned by ``get_current_texture()`` should be displayed with.
Default "srgb".
alpha_mode (enums.CanvasAlphaMode): Determines the effect that alpha values
will have on the content of textures returned by ``get_current_texture()``
when read, displayed, or used as an image source. Default "opaque".
"""
self.unconfigure()
self._device = device
self._format = format or self.get_preferred_format(device.adapter)
self._usage = usage or flags.TextureUsage.RENDER_ATTACHMENT
self._color_space = color_space
self._alpha_mode = alpha_mode
raise NotImplementedError()

# IDL: undefined unconfigure();
def unconfigure(self):
"""Removes the presentation context configuration.
Destroys any textures produced while configured."""
self._device = None
self._format = None
self._usage = None
self._color_space = None
self._alpha_mode = None
raise NotImplementedError()

# IDL: GPUTexture getCurrentTexture();
def get_current_texture(self):
"""Get the `GPUTexture` that will be composited to the canvas
by the context next.

NOTE: for the time being, this could return a `GPUTextureView` instead.
"""Get the `GPUTexture` that will be composited to the canvas next.
This method should be called exactly once during each draw event.
"""
raise NotImplementedError()

Expand All @@ -219,7 +216,7 @@ def present(self):

@apidiff.add("Better place to define the preferred format")
def get_preferred_format(self, adapter):
"""Get the preferred swap chain format."""
"""Get the preferred surface texture format."""
return "bgra8unorm-srgb" # seems to be a good default

def __del__(self):
Expand Down Expand Up @@ -2067,10 +2064,13 @@ def _seed_object_counts():

def generic_repr(self):
try:
module_name = "wgpu"
if "backends." in self.__module__:
backend_name = self.__module__.split("backends")[-1].split(".")[1]
module_name = f"wgpu.backends.{backend_name}"
module_name = self.__module__
if module_name.startswith("wgpu"):
if module_name == "wgpu._classes":
module_name = "wgpu"
elif "backends." in module_name:
backend_name = self.__module__.split("backends")[-1].split(".")[1]
module_name = f"wgpu.backends.{backend_name}"
object_str = "object"
if isinstance(self, GPUObjectBase):
object_str = f"object '{self.label}'"
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.17.2.1"
__commit_sha__ = "44d18911dc598104a9d611f8b6128e2620a5f145"
__version__ = "0.18.1.1"
__commit_sha__ = "118848fa951af384922d703315d3b245ed1adadf"
version_info = tuple(map(int, __version__.split(".")))
_check_expected_version(version_info) # produces a warning on mismatch

Expand Down
Loading
Loading