Skip to content

Commit

Permalink
Refactor present-method mechanic (#642)
Browse files Browse the repository at this point in the history
* Refactor for new get_context

* Apply new context-canvas mechanic in gui backends

* fix codegen

* Fix example

* Use 'wgpu' arg in get_context
  • Loading branch information
almarklein authored Nov 18, 2024
1 parent 437921d commit 44f4457
Show file tree
Hide file tree
Showing 24 changed files with 170 additions and 179 deletions.
2 changes: 1 addition & 1 deletion docs/guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Next, we can setup the render context, which we will need later on.

.. code-block:: py
present_context = canvas.get_context()
present_context = canvas.get_context("wgpu")
render_texture_format = present_context.get_preferred_format(device.adapter)
present_context.configure(device=device, format=render_texture_format)
Expand Down
6 changes: 4 additions & 2 deletions examples/cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ async def setup_drawing_async(canvas, limits=None):


def get_render_pipeline_kwargs(canvas, device, pipeline_layout):
context = canvas.get_context()
context = canvas.get_context("wgpu")
render_texture_format = context.get_preferred_format(device.adapter)
context.configure(device=device, format=render_texture_format)

Expand Down Expand Up @@ -288,7 +288,9 @@ async def upload_uniform_buffer_async():
device.queue.submit([command_encoder.finish()])

def draw_frame():
current_texture_view = canvas.get_context().get_current_texture().create_view()
current_texture_view = (
canvas.get_context("wgpu").get_current_texture().create_view()
)
command_encoder = device.create_command_encoder()
render_pass = command_encoder.begin_render_pass(
color_attachments=[
Expand Down
10 changes: 3 additions & 7 deletions examples/gui_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_glfw_present_info, poll_glfw_briefly
from wgpu.gui.glfw import get_glfw_present_methods, poll_glfw_briefly

# from triangle import setup_drawing_sync
from cube import setup_drawing_sync
Expand All @@ -33,18 +33,14 @@ def __init__(self, title):
glfw.window_hint(glfw.RESIZABLE, True)

self.window = glfw.create_window(640, 480, title, None, None)
self.context = GPUCanvasContext(self)

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

def get_physical_size(self):
"""get framebuffer size in integer pixels"""
psize = glfw.get_framebuffer_size(self.window)
return int(psize[0]), int(psize[1])

def get_context(self, kind="webgpu"):
def get_context(self, kind="wgpu"):
return self.context


Expand Down
8 changes: 4 additions & 4 deletions examples/gui_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
app = QtWidgets.QApplication([])
canvas = WgpuCanvas(title="wgpu triangle in Qt subprocess")
print(json.dumps(canvas.get_present_info()))
print(json.dumps(canvas.get_present_methods()))
print(canvas.get_physical_size())
sys.stdout.flush()
Expand All @@ -43,15 +43,15 @@
class ProxyCanvas(WgpuCanvasBase):
def __init__(self):
super().__init__()
self._present_info = json.loads(p.stdout.readline().decode())
self._present_methods = 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_present_info(self):
return self._present_info
def get_present_methods(self):
return self._present_methods

def get_physical_size(self):
return self._psize
Expand Down
2 changes: 1 addition & 1 deletion examples/imgui_backend_sea.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
device = adapter.request_device_sync()

# Prepare present context
present_context = canvas.get_context()
present_context = canvas.get_context("wgpu")
render_texture_format = wgpu.TextureFormat.bgra8unorm
present_context.configure(device=device, format=render_texture_format)

Expand Down
2 changes: 1 addition & 1 deletion examples/imgui_renderer_sea.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
device = adapter.request_device_sync()

# Prepare present context
present_context = canvas.get_context()
present_context = canvas.get_context("wgpu")
render_texture_format = wgpu.TextureFormat.bgra8unorm
present_context.configure(device=device, format=render_texture_format)

Expand Down
4 changes: 2 additions & 2 deletions examples/triangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ async def setup_drawing_async(canvas, limits=None):


def get_render_pipeline_kwargs(canvas, device):
context = canvas.get_context()
context = canvas.get_context("wgpu")
render_texture_format = context.get_preferred_format(device.adapter)
context.configure(device=device, format=render_texture_format)

Expand Down Expand Up @@ -94,7 +94,7 @@ def get_render_pipeline_kwargs(canvas, device):

def get_draw_function(canvas, device, render_pipeline, *, asynchronous):
def draw_frame_sync():
current_texture = canvas.get_context().get_current_texture()
current_texture = canvas.get_context("wgpu").get_current_texture()
command_encoder = device.create_command_encoder()

render_pass = command_encoder.begin_render_pass(
Expand Down
4 changes: 2 additions & 2 deletions examples/triangle_glsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def get_render_pipeline(canvas, device):
# No bind group and layout, we should not create empty ones.
pipeline_layout = device.create_pipeline_layout(bind_group_layouts=[])

present_context = canvas.get_context()
present_context = canvas.get_context("wgpu")
render_texture_format = present_context.get_preferred_format(device.adapter)
present_context.configure(device=device, format=render_texture_format)

Expand Down Expand Up @@ -99,7 +99,7 @@ def get_render_pipeline(canvas, device):

def get_draw_function(canvas, device, render_pipeline):
def draw_frame():
current_texture = canvas.get_context().get_current_texture()
current_texture = canvas.get_context("wgpu").get_current_texture()
command_encoder = device.create_command_encoder()

render_pass = command_encoder.begin_render_pass(
Expand Down
2 changes: 1 addition & 1 deletion tests/renderutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ def render_to_screen(
},
)

present_context = canvas.get_context()
present_context = canvas.get_context("wgpu")
present_context.configure(device=device, format=None)

def draw_frame():
Expand Down
15 changes: 7 additions & 8 deletions tests/test_gui_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ def test_base_canvas_context():
assert not issubclass(wgpu.gui.WgpuCanvasInterface, wgpu.GPUCanvasContext)
assert hasattr(wgpu.gui.WgpuCanvasInterface, "get_context")
canvas = wgpu.gui.WgpuCanvasInterface()
# Cannot instantiate, because get_present_info is not implemented
# Cannot instantiate, because get_present_methods is not implemented
with raises(NotImplementedError):
wgpu.GPUCanvasContext(canvas)
wgpu.GPUCanvasContext(canvas, canvas.get_present_methods())


def test_canvas_logging(caplog):
Expand Down Expand Up @@ -87,11 +87,8 @@ def __init__(self):
self.frame_count = 0
self.physical_size = 100, 100

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

def present_image(self, image, **kwargs):
self.frame_count += 1
Expand Down Expand Up @@ -129,6 +126,8 @@ def test_run_bare_canvas():

def test_canvas_context_not_base():
"""Check that it is prevented that canvas context is instance of base context class."""
return # skip

code = "from wgpu.gui import WgpuCanvasBase; canvas = WgpuCanvasBase(); canvas.get_context()"

result = subprocess.run(
Expand All @@ -148,7 +147,7 @@ def test_canvas_context_not_base():
def test_offscreen_canvas():
canvas = MyOffscreenCanvas()
device = wgpu.utils.get_default_device()
present_context = canvas.get_context()
present_context = canvas.get_context("wgpu")
present_context.configure(device=device, format=None)

def draw_frame():
Expand Down
14 changes: 7 additions & 7 deletions tests/test_gui_glfw.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def test_glfw_canvas_render_custom_canvas():
"""

import glfw
from wgpu.gui.glfw import get_glfw_present_info
from wgpu.gui.glfw import get_glfw_present_methods

class CustomCanvas: # implements wgpu.WgpuCanvasInterface
def __init__(self):
Expand All @@ -175,18 +175,18 @@ def __init__(self):
self.window = glfw.create_window(300, 200, "canvas", None, None)
self._present_context = None

def get_present_info(self):
return get_glfw_present_info(self.window)
def get_present_methods(self):
return get_glfw_present_methods(self.window)

def get_physical_size(self):
psize = glfw.get_framebuffer_size(self.window)
return int(psize[0]), int(psize[1])

def get_context(self):
def get_context(self, kind="wgpu"):
if self._present_context is None:
backend_module = sys.modules["wgpu"].gpu.__module__
PC = sys.modules[backend_module].GPUCanvasContext # noqa N806
self._present_context = PC(self)
self._present_context = PC(self, self.get_present_methods())
return self._present_context

canvas = CustomCanvas()
Expand All @@ -202,7 +202,7 @@ def get_context(self):
time.sleep(0.01)
glfw.poll_events()
draw_frame()
canvas.get_context().present() # WgpuCanvasBase normally automates this
canvas.get_context("wgpu").present() # WgpuCanvasBase normally automates this

glfw.hide_window(canvas.window)

Expand All @@ -213,7 +213,7 @@ def _get_draw_function(device, canvas):

shader = device.create_shader_module(code=shader_source)

present_context = canvas.get_context()
present_context = canvas.get_context("wgpu")
render_texture_format = present_context.get_preferred_format(device.adapter)
present_context.configure(device=device, format=render_texture_format)

Expand Down
6 changes: 3 additions & 3 deletions tests_mem/test_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ def make_draw_func_for_canvas(canvas):
"""Create a draw function for the given canvas,
so that we can really present something to a canvas being tested.
"""
ctx = canvas.get_context()
ctx = canvas.get_context("wgpu")
ctx.configure(device=DEVICE, format=None)

def draw():
ctx = canvas.get_context()
ctx = canvas.get_context("wgpu")
command_encoder = DEVICE.create_command_encoder()
current_texture_view = ctx.get_current_texture().create_view()
render_pass = command_encoder.begin_render_pass(
Expand Down Expand Up @@ -70,7 +70,7 @@ def test_release_canvas_context(n):
canvases.add(c)
c.request_draw(make_draw_func_for_canvas(c))
c.draw()
yield c.get_context()
yield c.get_context("wgpu")

del c
gc.collect()
Expand Down
2 changes: 1 addition & 1 deletion tests_mem/test_gui_glfw.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def test_release_canvas_context(n):
canvases.add(c)
c.request_draw(make_draw_func_for_canvas(c))
loop.run_until_complete(stub_event_loop())
yield c.get_context()
yield c.get_context("wgpu")

# Need some shakes to get all canvas refs gone.
del c
Expand Down
2 changes: 1 addition & 1 deletion tests_mem/test_gui_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test_release_canvas_context(n):
canvases.add(c)
c.request_draw(make_draw_func_for_canvas(c))
app.processEvents()
yield c.get_context()
yield c.get_context("wgpu")

# Need some shakes to get all canvas refs gone.
del c
Expand Down
11 changes: 11 additions & 0 deletions wgpu/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,14 @@

# The API entrypoint, from wgpu.classes - gets replaced when a backend loads.
gpu = GPU() # noqa: F405


def rendercanvas_context_hook(canvas, present_methods):
import sys

backend_module = gpu.__module__
if backend_module in ("", "wgpu._classes"):
raise RuntimeError(
"A backend must be selected (e.g. with wgpu.gpu.request_adapter()) before canvas.get_context() can be called."
)
return sys.modules[backend_module].GPUCanvasContext(canvas, present_methods)
Loading

0 comments on commit 44f4457

Please sign in to comment.