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

Refactor canvas context to allow presenting as image #586

Merged
merged 27 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
49afc76
Refactor canvas context to allow presening as image
almarklein Sep 16, 2024
e042000
some cleanup
almarklein Sep 16, 2024
671cd4a
codegen
almarklein Sep 16, 2024
1e816bc
Fix flicker
almarklein Sep 17, 2024
65b4848
cleaner
almarklein Sep 17, 2024
a4d5651
fix error on exit
almarklein Sep 17, 2024
8b6eef8
looked into qt image draw performance a bit
almarklein Sep 17, 2024
70e77d4
Fix/workaround for Qt on Wayland
almarklein Sep 17, 2024
039b61c
Fix glfw
almarklein Sep 17, 2024
0db867d
Give wx same treatment as qt
almarklein Sep 17, 2024
dc1c24b
Show warning when using offscreen rendering in qt and wx
almarklein Sep 17, 2024
28fa1a8
docs
almarklein Sep 17, 2024
fc85d36
Merge branch 'main' into canvascontext
almarklein Sep 17, 2024
2c923dd
Update offscreen canvases. No more need for WgpuOfscreenCanvasBase
almarklein Sep 17, 2024
23f3e06
Update notebook
almarklein Sep 17, 2024
40e7071
docs
almarklein Sep 17, 2024
9e63ae3
Merge branch 'main' into canvascontext
almarklein Sep 17, 2024
d7d2db5
minor tweaks
almarklein Sep 17, 2024
c8bb1bf
update tests
almarklein Sep 17, 2024
fa5b77c
Fix memtest
almarklein Sep 18, 2024
8e3d56a
remove debug text overlay
almarklein Sep 18, 2024
588a8df
Bit of docstrings
almarklein Sep 18, 2024
2e4d125
explaine purpose of canvas context
almarklein Sep 18, 2024
02c9600
Rename surface_info -> present_info
almarklein Sep 18, 2024
4685a0b
draw_to_screen -> present_method
almarklein Sep 18, 2024
6b624a8
flake
almarklein Sep 18, 2024
7e001d5
Merge branch 'main' into canvascontext
almarklein Sep 20, 2024
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
1 change: 0 additions & 1 deletion docs/gui.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ The Canvas base classes
~WgpuCanvasInterface
~WgpuCanvasBase
~WgpuAutoGui
~WgpuOffscreenCanvasBase


For each supported GUI toolkit there is a module that implements a ``WgpuCanvas`` class,
Expand Down
6 changes: 3 additions & 3 deletions examples/triangle_glfw_direct.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import glfw

from wgpu.backends.wgpu_native import GPUCanvasContext
from wgpu.gui.glfw import get_surface_info, get_physical_size
from wgpu.gui.glfw import get_glfw_present_info, get_physical_size
from wgpu.utils.device import get_default_device


Expand All @@ -29,9 +29,9 @@ class GlfwCanvas:
def __init__(self, window):
self._window = window

def get_surface_info(self):
def get_present_info(self):
"""get window and display id, includes some triage to deal with OS differences"""
return get_surface_info(self._window)
return get_glfw_present_info(self._window)

def get_physical_size(self):
"""get framebuffer size in integer pixels"""
Expand Down
8 changes: 4 additions & 4 deletions examples/triangle_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
app = QtWidgets.QApplication([])
canvas = WgpuCanvas(title="wgpu triangle in Qt subprocess")

print(json.dumps(canvas.get_surface_info()))
print(json.dumps(canvas.get_present_info()))
print(canvas.get_physical_size())
sys.stdout.flush()

Expand All @@ -42,15 +42,15 @@
class ProxyCanvas(WgpuCanvasBase):
def __init__(self):
super().__init__()
self._surface_info = json.loads(p.stdout.readline().decode())
self._present_info = json.loads(p.stdout.readline().decode())
self._psize = tuple(
int(x) for x in p.stdout.readline().decode().strip().strip("()").split(",")
)
print(self._psize)
time.sleep(0.2)

def get_surface_info(self):
return self._surface_info
def get_present_info(self):
return self._present_info

def get_physical_size(self):
return self._psize
Expand Down
129 changes: 109 additions & 20 deletions examples/wgpu-examples.ipynb

Large diffs are not rendered by default.

47 changes: 18 additions & 29 deletions tests/test_gui_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import numpy as np
import wgpu.gui # noqa
from testutils import run_tests, can_use_wgpu_lib, is_pypy
from pytest import mark
from pytest import mark, raises


class TheTestCanvas(wgpu.gui.WgpuCanvasBase):
Expand Down Expand Up @@ -37,10 +37,10 @@ def spam_method(self):
def test_base_canvas_context():
assert not issubclass(wgpu.gui.WgpuCanvasInterface, wgpu.GPUCanvasContext)
assert hasattr(wgpu.gui.WgpuCanvasInterface, "get_context")
# Provides good default already
canvas = wgpu.gui.WgpuCanvasInterface()
ctx = wgpu.GPUCanvasContext(canvas)
assert ctx.get_preferred_format(None) == "bgra8unorm-srgb"
# Cannot instantiate, because get_present_info is not implemented
with raises(NotImplementedError):
wgpu.GPUCanvasContext(canvas)


def test_canvas_logging(caplog):
Expand Down Expand Up @@ -80,12 +80,22 @@ def test_canvas_logging(caplog):
assert text.count("division by zero") == 4


class MyOffscreenCanvas(wgpu.gui.WgpuOffscreenCanvasBase):
class MyOffscreenCanvas(wgpu.gui.WgpuCanvasBase):
def __init__(self):
super().__init__()
self.textures = []
self.frame_count = 0
self.physical_size = 100, 100

def get_present_info(self):
return {
"method": "image",
"formats": ["rgba8unorm-srgb"],
}

def present_image(self, image, **kwargs):
self.frame_count += 1
self.array = np.frombuffer(image, np.uint8).reshape(image.shape)

def get_pixel_ratio(self):
return 1

Expand All @@ -99,26 +109,6 @@ def _request_draw(self):
# Note: this would normally schedule a call in a later event loop iteration
self._draw_frame_and_present()

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,
"mip_level": 0,
"origin": (0, 0, 0),
},
{
"offset": 0,
"bytes_per_row": bytes_per_pixel * size[0],
"rows_per_image": size[1],
},
size,
)
self.array = np.frombuffer(data, np.uint8).reshape(size[1], size[0], 4)


@mark.skipif(not can_use_wgpu_lib, reason="Needs wgpu lib")
def test_run_bare_canvas():
Expand Down Expand Up @@ -181,7 +171,7 @@ def draw_frame():
render_pass.end()
device.queue.submit([command_encoder.finish()])

assert len(canvas.textures) == 0
assert canvas.frame_count == 0

# Draw 1
canvas.request_draw(draw_frame)
Expand Down Expand Up @@ -214,8 +204,7 @@ def draw_frame():
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
assert canvas.frame_count == 4


def test_autogui_mixin():
Expand Down
2 changes: 1 addition & 1 deletion tests/test_gui_glfw.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def __init__(self):
self.window = glfw.create_window(300, 200, "canvas", None, None)
self._present_context = None

def get_surface_info(self):
def get_present_info(self):
if sys.platform.startswith("win"):
return {
"platform": "windows",
Expand Down
2 changes: 1 addition & 1 deletion tests_mem/test_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def make_draw_func_for_canvas(canvas):
so that we can really present something to a canvas being tested.
"""
ctx = canvas.get_context()
ctx.configure(device=DEVICE, format="bgra8unorm-srgb")
ctx.configure(device=DEVICE, format=None)

def draw():
ctx = canvas.get_context()
Expand Down
4 changes: 3 additions & 1 deletion tests_mem/test_gui_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ def test_release_canvas_context(n):
if app is None:
app = PySide6.QtWidgets.QApplication([""])

yield {}
yield {
"ignore": {"CommandBuffer"},
}

canvases = weakref.WeakSet()

Expand Down
Loading
Loading