http://bugs.winehq.org/show_bug.cgi?id=60263 Bug ID: 60263 Summary: winemac.drv: cross-process child window Metal swapchains not implemented (Steam client renders black); the CAContext/CALayerHost route already exists and is only reached from vulkan.c Product: Wine Version: 11.16 Hardware: x86-64 OS: MacOS Status: UNCONFIRMED Severity: normal Priority: P2 Component: winemac.drv Assignee: wine-bugs@list.winehq.org Reporter: winebugs@protonmail.ch Target Milestone: --- winemac.drv contains this FIXME: Cross-process child window Metal swapchains are not implemented This is the whole reason Steam's client renders as a black window under Wine on macOS, and the route to fix it already exists in the tree — it is simply never reached from the Metal side. ## What happens Steam's CEF GPU process asks for a Metal view for a child window whose root lives in the *browser* process. `get_win_data()` returns NULL by construction for a foreign HWND — `win_datas` is a process-local CFDictionary — and the caller aborts on the null view by design. Chromium restarts the GPU process, it dies identically, and after ~6 attempts it gives up. Black window. Two things make this hard to spot: - The `CreateSwapChain` cross-process guard never fires for this window, because `GetWindowThreadProcessId()` says the *child* belongs to us. The Win32 process check and winemac's realization check disagree, and the path trusts the first. - It is backend-independent: forcing `--use-angle=swiftshader` (no Metal, no GPU driver at all) produces the same crash count and a byte-identical black capture. ## The route already in the tree `macdrv_client_surface_acquire_metal_swapchain()` builds a `CAContext`-backed offscreen swapchain and posts `WM_MACDRV_CREATE_REMOTE_LAYER` to the window's owning process, which hosts it via `CALayerHost`. That is exactly the cross-process case. In stock Wine only `vulkan.c` reaches it. Wiring the Metal side to fall back to that route when `get_win_data()` returns NULL makes Steam's client render completely, out-of-process, with text, no injected switches and 0 GPU crashes. ## Working implementation, for reference A working version of both halves is public here, under MIT: https://github.com/macgameport/cities-skylines-2-macos/blob/main/scripts/win... Notes from getting it working, in case they save someone time: - **Do not add a member to `macdrv_functions_t`** to expose the entry point — that struct is `C_ASSERT`-ed at a fixed size, so a client built against a larger struct would read past the end of an older winemac's and call whatever followed. A standalone exported symbol degrades safely. - **Convert Win32 pixels to Cocoa points at the CALayerHost entry points.** `CALayer.frame` is in points; unconverted frames are 2x too large on retina and the content is pushed down and right. - **Stack the hosted layers by Win32 paint order, not insertion order.** CEF recreates a swapchain on every resize, so insertion order puts whichever browser was recreated last on top. Steam runs two sibling browsers and the client goes black. - **An empty child rect is not "no rect".** CEF keeps an inactive browser in the z-order at 0x0; treating that as "no rect supplied" and stretching the layer over the view blacks the window. - **Retire a superseded layer on CREATE, not on destroy**, or the child is unhosted between the two and the window blinks through during a resize. ## Disclosure This was investigated with heavy AI assistance, so I am filing a report rather than a patch, and the implementation above is offered only as a reference someone may or may not want to look at. I am also aware of the relicensing discussion in 3Shain/dxmt#152 and am not asking for anything to be taken as-is. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.