diff --git a/wgpu/backends/wgpu_native/_helpers.py b/wgpu/backends/wgpu_native/_helpers.py index 230e87d6..cf26c631 100644 --- a/wgpu/backends/wgpu_native/_helpers.py +++ b/wgpu/backends/wgpu_native/_helpers.py @@ -3,6 +3,7 @@ import sys import ctypes +from queue import deque from ._ffi import ffi, lib from ..._diagnostics import DiagnosticsBase @@ -237,29 +238,38 @@ class ErrorHandler: def __init__(self, logger): self._logger = logger - self._proxy_stack = [] + self._proxy_stack = deque() + self._proxy_messages = {} self._error_message_counts = {} - def capture(self, func): - """Send incoming error messages to the given func instead of logging them.""" - self._proxy_stack.append(func) + def capture(self, name): + """Capture incoming error messages instead of logging them directly.""" + self._proxy_stack.append(name) - def release(self, func): - """Release the given func.""" - f = self._proxy_stack.pop(-1) - if f is not func: + def release(self, name): + """Release the given name, returning the last captured error.""" + n = self._proxy_stack.pop() + if n is not name: + messages = [m for _, m in self._proxy_message.values()] + self._proxy_messages.clear() self._proxy_stack.clear() - self._logger.warning("ErrorHandler capture/release out of sync") + self._logger.error("ErrorHandler capture/release out of sync") + for message in messages: + self.log_error(message) + return self._proxy_messages.pop(name, None) def handle_error(self, error_type: str, message: str): """Handle an error message.""" if self._proxy_stack: - self._proxy_stack[-1](error_type, message) + proxy_name = self._proxy_stack[-1] + if proxy_name in self._proxy_messages: + self.log_error(self._proxy_messages[proxy_name][1]) + self._proxy_messages[proxy_name] = error_type, message else: self.log_error(message) def log_error(self, message): - """Hanle an error message by logging it, bypassing any capturing.""" + """Handle an error message by logging it, bypassing any capturing.""" # Get count for this message. Use a hash that does not use the # digits in the message, because of id's getting renewed on # each draw. @@ -283,7 +293,6 @@ class SafeLibCalls: def __init__(self, lib, error_handler): self._error_handler = error_handler - self._error_message = None self._make_function_copies(lib) def _make_function_copies(self, lib): @@ -293,27 +302,20 @@ def _make_function_copies(self, lib): if callable(ob): setattr(self, name, self._make_proxy_func(name, ob)) - def _handle_error(self, error_type, message): - # If we already had an error, we log the earlier one now - if self._error_message: - self._error_handler.log_error(self._error_message[1]) - # Store new error - self._error_message = (error_type, message) - def _make_proxy_func(self, name, ob): + error_handler = self._error_handler + def proxy_func(*args): # Make the call, with error capturing on - handle_error = self._handle_error - self._error_handler.capture(handle_error) + error_handler.capture(name) try: result = ob(*args) finally: - self._error_handler.release(handle_error) + error_type_msg = error_handler.release(name) # Handle the error. - if self._error_message: - error_type, message = self._error_message - self._error_message = None + if error_type_msg is not None: + error_type, message = error_type_msg cls = ERROR_TYPES.get(error_type, GPUError) wgpu_error = cls(message) # The line below will be the bottom line in the traceback,