From 3ad3373ceeee8d50da620fe3b13109d77b398db2 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 23 Oct 2023 12:52:47 +0200 Subject: [PATCH 1/2] Refactor autogui selection to take imported backends into account better (#384) --- wgpu/gui/auto.py | 100 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 26 deletions(-) diff --git a/wgpu/gui/auto.py b/wgpu/gui/auto.py index 81660db7..5307e3bf 100644 --- a/wgpu/gui/auto.py +++ b/wgpu/gui/auto.py @@ -24,32 +24,80 @@ def is_jupyter(): return False +def _load_backend(backend_name): + """Load a gui backend by name.""" + if backend_name == "glfw": + from . import glfw as module # noqa + elif backend_name == "qt": + from . import qt as module # noqa + elif backend_name == "jupyter": + from . import jupyter as module # noqa + elif backend_name == "wx": + from . import wx as module # noqa + elif backend_name == "offscreen": + from . import offscreen as module # noqa + else: # no-cover + raise ImportError("Unknown wgpu gui backend: '{backend_name}'") + return module + + +def _auto_load_backend(): + # Backends to auto load, ordered by preference. Maps libname -> backend_name + gui_backends = { + "glfw": "glfw", + "PySide6": "qt", + "PyQt6": "qt", + "PySide2": "qt", + "PyQt5": "qt", + } + + # The module that we try to find + module = None + + # Any errors we come accross as we try to import the gui backends + errors = [] + + # Prefer a backend for which the lib is already imported + imported = [libname for libname in gui_backends if libname in sys.modules] + for libname in imported: + try: + module = _load_backend(gui_backends[libname]) + break + except Exception as err: + errors.append(err) + + # If no module found yet, try importing the lib, then import the backend + if not module: + for libname in gui_backends: + try: + importlib.import_module(libname) + except ModuleNotFoundError: + continue + try: + module = _load_backend(gui_backends[libname]) + except Exception as err: + errors.append(err) + + # If still nothing found, raise a useful error + if not module: + msg = "\n".join(str(err) for err in errors) + msg += "\n\n Could not find either glfw or Qt framework." + msg += "\n Install glfw using e.g. ``pip install -U glfw``," + msg += "\n or install a qt framework using e.g. ``pip install -U pyside6``." + if sys.platform.startswith("linux"): + msg += "\n You may also need to run the equivalent of ``apt install libglfw3``." + raise ImportError(msg) from None + + return module + + +# Triage if os.environ.get("WGPU_FORCE_OFFSCREEN") == "true": - from .offscreen import WgpuCanvas, run, call_later # noqa + module = _load_backend("offscreen") elif is_jupyter(): - from .jupyter import WgpuCanvas, run, call_later # noqa + module = _load_backend("jupyter") else: - try: - from .glfw import WgpuCanvas, run, call_later # noqa - except ImportError as glfw_err: - qt_backends = ("PySide6", "PyQt6", "PySide2", "PyQt5") - for backend in qt_backends: - if backend in sys.modules: - break - else: - for libname in qt_backends: - try: - importlib.import_module(libname) - break - except ModuleNotFoundError: - pass - else: - msg = str(glfw_err) - msg += "\n\n Could not find either glfw or Qt framework." - msg += "\n Install glfw using e.g. ``pip install -U glfw``," - msg += "\n or install a qt framework using e.g. ``pip install -U pyside6``." - if sys.platform.startswith("linux"): - msg += "\n You may also need to run the equivalent of ``apt install libglfw3``." - raise ImportError(msg) from None - - from .qt import WgpuCanvas, run, call_later + module = _auto_load_backend() + + +WgpuCanvas, run, call_later = module.WgpuCanvas, module.run, module.call_later From d15104868a0d3d70cfdece6cf51509beabcfd993 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 23 Oct 2023 13:15:55 +0200 Subject: [PATCH 2/2] Provide extra tip for fixing monitor-swap error (#385) * Provide extra tip for fixing monitor-swap error * codegen --------- Co-authored-by: Korijn van Golen --- wgpu/backends/rs.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/wgpu/backends/rs.py b/wgpu/backends/rs.py index 54fa2599..a0b116c8 100644 --- a/wgpu/backends/rs.py +++ b/wgpu/backends/rs.py @@ -431,7 +431,7 @@ def print_storage_report(topic, d): class GPUCanvasContext(base.GPUCanvasContext): def __init__(self, canvas): super().__init__(canvas) - self._surface_size = (-1, -1) + self._surface_size = (-1, -1, -1) self._surface_id = None self._internal = None self._current_texture = None @@ -443,8 +443,15 @@ def get_current_texture(self): ) if self._current_texture is None: self._create_native_swap_chain_if_needed() - # H: WGPUTextureView f(WGPUSwapChain swapChain) - view_id = libf.wgpuSwapChainGetCurrentTextureView(self._internal) + try: + # H: WGPUTextureView f(WGPUSwapChain swapChain) + view_id = libf.wgpuSwapChainGetCurrentTextureView(self._internal) + except Exception as err: + extra_msg = "\nThis may be caused by dragging the window to a monitor with different dpi. " + extra_msg += "Resize window to proceed.\n" + err.args = (err.args[0] + extra_msg,) + err.args[1:] + raise err from None + size = self._surface_size[0], self._surface_size[1], 1 self._current_texture = GPUTextureView( "swap_chain", view_id, self._device, None, size @@ -465,9 +472,11 @@ def present(self): def _create_native_swap_chain_if_needed(self): canvas = self._get_canvas() psize = canvas.get_physical_size() - if psize == self._surface_size: + ref_size = psize[0], psize[1], canvas.get_pixel_ratio() + + if ref_size == self._surface_size: return - self._surface_size = psize + self._surface_size = ref_size if self._surface_id is None: self._surface_id = get_surface_id_from_canvas(canvas)