From 7bf29f3e4c67b7728bfce2b4732eeed66e7b16c3 Mon Sep 17 00:00:00 2001 From: dhkatz Date: Wed, 28 Oct 2026 18:18:23 -0700 Subject: [PATCH] Clear WS_CLIPCHILDREN on the client's window The client creates its 3D view's window with it, so not adding it wasn't enough: covered entirely by the renderer's window, the client's window got no WM_PAINT and the client still drew about once a second. Co-Authored-By: Claude Opus 5.5 --- adapter/renderer_process.cpp | 7 ++++--- docs/architecture.md | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/adapter/renderer_process.cpp b/adapter/renderer_process.cpp index ee66c03..2f96ebe 100644 --- a/adapter/renderer_process.cpp +++ b/adapter/renderer_process.cpp @@ -89,9 +89,10 @@ std::unique_ptr RendererProcess::start(HWND window, protocol::V .reserved = 0}; // The renderer's window goes on top of the client's 3D view, as a child window. The client's window mustn't clip - // its children (WS_CLIPCHILDREN): the client draws a frame when its window gets WM_PAINT, which a window entirely - // covered by clipped children never gets (it then drew about once a second). The renderer's swap chain is a flip - // model one, which the compositor shows above the client's drawing anyway. + // its children (WS_CLIPCHILDREN, which the client creates it with): the client draws a frame when its window gets + // WM_PAINT, which a window entirely covered by clipped children never gets (it then drew about once a second). The + // renderer's swap chain is a flip model one, which the compositor shows above the client's drawing anyway. + SetWindowLongW(window, GWL_STYLE, GetWindowLongW(window, GWL_STYLE) & ~WS_CLIPCHILDREN); const auto exe = running_copy(module_folder() / L"t5de-renderer.exe"); std::wstring command = std::format(L"\"{}\" --channel {} --parent {}", exe.wstring(), name, GetCurrentProcessId()); STARTUPINFOW startup{.cb = sizeof(STARTUPINFOW)}; diff --git a/docs/architecture.md b/docs/architecture.md index 9be0feb..5127f8e 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -30,7 +30,8 @@ creates (each 3D view, and again on room changes), the adapter: The renderer process draws into a child window it creates inside the client's window: window handles are valid in every process, and Windows allows child windows across processes (Chrome's GPU process works this way). The child is disabled so mouse input goes to the client's window under it. Its swap chain is a flip model one, which the -compositor shows above the client's own drawing. The client's window must not clip its children (`WS_CLIPCHILDREN`): +compositor shows above the client's own drawing. The client's window must not clip its children (`WS_CLIPCHILDREN`, +which the client creates it with, so the adapter clears it): the client draws a frame when its window gets `WM_PAINT` (`SceneWindow.dll` invalidates it, and draws on the paint), and a window entirely covered by clipped children never gets one, so the client fell to about one frame a second. The process exits when the engine is destroyed or the client exits.