Skip to content

Commit fbc15ed

Browse files
M1 fixes (#195)
* rewrite of CAMetalLayer poiner extraction code to work on Apple aarch64 ABI * Fix wxPython examples somewhat * Fix async triangle example * Only add rubicon as dependency on macOS Co-authored-by: marc <[email protected]>
1 parent ae2356a commit fbc15ed

File tree

3 files changed

+47
-48
lines changed

3 files changed

+47
-48
lines changed

examples/triangle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ async def main_async(canvas):
6262
adapter = await wgpu.request_adapter_async(
6363
canvas=canvas, power_preference="high-performance"
6464
)
65-
device = await adapter.request_device_async(extensions=[], limits={})
65+
device = await adapter.request_device_async(required_limits={})
6666
return _main(canvas, device)
6767

6868

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def finalize_options(self):
2828
else:
2929
pass # don't include binaries; user will have to arrange for the lib
3030

31-
runtime_deps = ["cffi>=1.10"]
31+
runtime_deps = ["cffi>=1.10", "rubicon-objc>=0.4.1; sys_platform == 'darwin'"]
3232

3333

3434
setup(

wgpu/backends/rs_helpers.py

Lines changed: 45 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
from .rs_ffi import ffi, lib
66

7+
if sys.platform.startswith("darwin"):
8+
from rubicon.objc.api import ObjCInstance, ObjCClass
9+
710

811
def get_memoryview_and_address(data):
912
"""Get a memoryview for the given data and its memory address.
@@ -67,57 +70,53 @@ def get_surface_id_from_canvas(canvas):
6770

6871
elif sys.platform.startswith("darwin"): # no-cover
6972
# This is what the triangle example from wgpu-native does:
70-
# #if WGPU_TARGET == WGPU_TARGET_MACOS
71-
# {
72-
# id metal_layer = NULL;
73-
# NSWindow *ns_window = glfwGetCocoaWindow(window);
74-
# [ns_window.contentView setWantsLayer:YES];
75-
# metal_layer = [CAMetalLayer layer];
76-
# [ns_window.contentView setLayer:metal_layer];
77-
# surface = wgpu_create_surface_from_metal_layer(metal_layer);
78-
# }
73+
# if WGPU_TARGET == WGPU_TARGET_MACOS
74+
# {
75+
# id metal_layer = NULL;
76+
# NSWindow *ns_window = glfwGetCocoaWindow(window);
77+
# [ns_window.contentView setWantsLayer:YES];
78+
# metal_layer = [CAMetalLayer layer];
79+
# [ns_window.contentView setLayer:metal_layer];
80+
# surface = wgpu_create_surface_from_metal_layer(metal_layer);
81+
# }
7982
window = ctypes.c_void_p(win_id)
8083

81-
objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("objc"))
82-
objc.objc_getClass.restype = ctypes.c_void_p
83-
objc.sel_registerName.restype = ctypes.c_void_p
84-
objc.objc_msgSend.restype = ctypes.c_void_p
85-
objc.objc_msgSend.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
86-
87-
content_view_sel = objc.sel_registerName(b"contentView")
88-
set_wants_layer_sel = objc.sel_registerName(b"setWantsLayer:")
89-
responds_to_sel_sel = objc.sel_registerName(b"respondsToSelector:")
90-
layer_sel = objc.sel_registerName(b"layer")
91-
set_layer_sel = objc.sel_registerName(b"setLayer:")
92-
93-
# Try some duck typing to see what kind of object the window pointer points to
94-
# Qt doesn't return a NSWindow, but a QNSView instead, which is subclass of NSView.
95-
if objc.objc_msgSend(
96-
window, responds_to_sel_sel, ctypes.c_void_p(content_view_sel)
97-
):
98-
# NSWindow instances respond to contentView selector
99-
content_view = objc.objc_msgSend(window, content_view_sel)
100-
elif objc.objc_msgSend(window, responds_to_sel_sel, ctypes.c_void_p(layer_sel)):
101-
# NSView instances respond to layer selector
102-
# Let's assume that the given window pointer is actually the content view
103-
content_view = window
84+
cw = ObjCInstance(window)
85+
try:
86+
cv = cw.contentView
87+
except AttributeError:
88+
# With wxPython, ObjCInstance is actually already a wxNSView and
89+
# not a NSWindow so no need to get the contentView (which is a
90+
# NSWindow method)
91+
wx_view = ObjCInstance(window)
92+
# Creating a metal layer directly in the wxNSView does not seem to
93+
# work, so instead add a subview with the same bounds that resizes
94+
# with the wxNSView and add a metal layer to that
95+
if not len(wx_view.subviews):
96+
new_view = ObjCClass("NSView").alloc().initWithFrame(wx_view.bounds)
97+
# typedef NS_OPTIONS(NSUInteger, NSAutoresizingMaskOptions) {
98+
# ...
99+
# NSViewWidthSizable = 2,
100+
# NSViewHeightSizable = 16,
101+
# ...
102+
# };
103+
# Make subview resize with superview by combining
104+
# NSViewHeightSizable and NSViewWidthSizable
105+
new_view.setAutoresizingMask(18)
106+
wx_view.setAutoresizesSubviews(True)
107+
wx_view.addSubview(new_view)
108+
cv = wx_view.subviews[0]
109+
110+
if cv.layer and cv.layer.isKindOfClass(ObjCClass("CAMetalLayer")):
111+
# No need to create a metal layer again
112+
metal_layer = cv.layer
104113
else:
105-
# If the code reaches this part, we know that `window` is an
106-
# objective-c object but the type is neither NSView or NSWindow.
107-
raise RuntimeError("Received unidentified objective-c object.")
108-
109-
# [ns_window.contentView setWantsLayer:YES]
110-
objc.objc_msgSend(content_view, set_wants_layer_sel, True)
111-
112-
# metal_layer = [CAMetalLayer layer];
113-
ca_metal_layer_class = objc.objc_getClass(b"CAMetalLayer")
114-
metal_layer = objc.objc_msgSend(ca_metal_layer_class, layer_sel)
115-
116-
# [ns_window.content_view setLayer:metal_layer];
117-
objc.objc_msgSend(content_view, set_layer_sel, ctypes.c_void_p(metal_layer))
114+
metal_layer = ObjCClass("CAMetalLayer").layer()
115+
cv.setLayer(metal_layer)
116+
cv.setWantsLayer(True)
118117

119118
struct = ffi.new("WGPUSurfaceDescriptorFromMetalLayer *")
120-
struct.layer = ffi.cast("void *", metal_layer)
119+
struct.layer = ffi.cast("void *", metal_layer.ptr.value)
121120
struct.chain.sType = lib.WGPUSType_SurfaceDescriptorFromMetalLayer
122121

123122
elif sys.platform.startswith("linux"): # no-cover

0 commit comments

Comments
 (0)