qt: comment cleanup after the wayland subsurface stack

Updates stale comments left over from intermediate phases of the
subsurface present work:

- GhosttySurface.h class doc: was describing the QPainter blit
  era. Rewrite to describe the actual present path
  (wl_subsurface + dmabuf, sibling subsurfaces for splits,
  legacy mmap fallback).
- GhosttySurface.cpp Show handler: drop the "Phase 2 (current):
  scaffolding only" narrative — long since shipped.
- GhosttySurface.h m_subsurfacePresenter doc: reflect that it's
  null when compositor lacks required globals, not "Phase 2
  keeps working because nothing depends on it".
- SubsurfacePresenter.cpp set_position comment: drop the
  "Phase 4 will keep this in sync" placeholder — splits are
  wired now and setPosition is called immediately after
  tryCreate.
- Host.h: list VK_EXT_image_drm_format_modifier as a required
  extension (was missing) and note that instance() primes the
  Wayland dmabuf modifier registry on the calling thread.

No behavior changes.

Co-Authored-By: claude-flow <ruv@ruv.net>
pull/12846/head
Nathan 2026-05-25 13:40:25 -05:00
parent f686fa50e8
commit 55f4abbc02
4 changed files with 41 additions and 27 deletions

View File

@ -435,13 +435,9 @@ bool GhosttySurface::event(QEvent *e) {
// and we can safely look up the Wayland parent wl_surface.
// Lazy-init the subsurface presenter once and keep it for the
// widget's lifetime — tying it to Show/Hide would churn the
// wl_subsurface on every tab switch.
//
// Phase 2 (current): scaffolding only. The presenter creates a
// wl_subsurface but never attaches a buffer; the existing
// `presentVulkanDmabuf` + `paintEvent` QPainter path is the
// one producing pixels. Phase 3 will route frames through the
// subsurface and retire the QPainter blit.
// wl_subsurface on every tab switch. Re-creation on real
// native-surface lifecycle changes is handled by the
// QEvent::PlatformSurface branch above.
if (!m_subsurfacePresenter) {
// Use the TOP-LEVEL QWindow's wl_surface as the parent for
// our subsurface — NOT this widget's own QWindow. Each pane

View File

@ -39,14 +39,26 @@ class OverlayScrollbar;
// One Ghostty terminal pane.
//
// libghostty's OpenGL renderer draws the terminal into an offscreen
// framebuffer owned by a private QOpenGLContext (there is no on-screen
// GL surface). Each frame is read back into a QImage and painted with
// QPainter. That keeps this an ordinary translucent QWidget, so it
// embeds in the QTabWidget / QSplitter tree and its transparent
// background composites to the desktop exactly like the rest of the
// widget chrome — avoiding QOpenGLWidget (composites opaque on Wayland)
// and an embedded QOpenGLWindow (does not present when embedded).
// Terminal pixels reach the screen via a wl_subsurface attached to
// the top-level QWindow's wl_surface (see wayland::SubsurfacePresenter).
// libghostty's renderer (Vulkan or OpenGL, picked at compile time
// via GHASTTY_USE_VULKAN) hands us a dmabuf fd per frame; we wrap
// it in a wl_buffer via zwp_linux_dmabuf_v1 and the compositor
// scans it out directly — no readback, no QPainter blit for the
// terminal area. Each pane in a split is a sibling subsurface
// under the same top-level wl_surface, positioned at its offset
// within the top-level via setPosition.
//
// This QWidget itself keeps WA_TranslucentBackground so the
// terminal area of the parent surface is transparent (the
// subsurface below shows through) and chrome (SearchBar,
// overlays, scrollbar) painted in paintEvent stays visible on top.
//
// Legacy fallback: if the compositor lacks the required Wayland
// globals (linux-dmabuf-v1, viewporter, subcompositor) or the
// renderer reports image_backed=false (NVIDIA Vulkan's
// legacy_copy path on this branch), the frame goes through a
// mmap+memcpy+QImage+QPainter::drawImage path instead.
class GhosttySurface : public QWidget {
Q_OBJECT
@ -364,10 +376,10 @@ private:
QString m_pwd;
// Wayland subsurface for the GPU-direct present path. Lazily
// created on first `QEvent::Show` once the native QWindow exists;
// null until then, null forever if creation fails (Phase 2 keeps
// working in that case because nothing yet depends on it). Phase 3
// will use this to attach dmabuf-backed `wl_buffer`s.
// created on first `QEvent::Show` once the top-level QWindow
// exists; null if the compositor lacks the required globals
// (linux-dmabuf-v1, viewporter, subcompositor), in which case
// the legacy mmap+memcpy+QImage+QPainter path renders pixels.
std::unique_ptr<wayland::SubsurfacePresenter> m_subsurfacePresenter;
// Per-surface latch for the first-dmabuf log breadcrumb so each
// pane / split prints its own line on first frame.

View File

@ -8,10 +8,16 @@
//
// The host is process-singleton (one Vulkan instance + device shared
// across every `GhosttySurface`), constructed lazily on first use
// via `instance()`. If Vulkan isn't available (no loader, no
// suitable physical device with `VK_KHR_external_memory_fd` +
// `VK_EXT_external_memory_dma_buf`), construction fails gracefully
// and the caller falls back to the OpenGL path.
// via `instance()`. Requires a physical device that supports
// VK_KHR_external_memory_fd, VK_EXT_external_memory_dma_buf, and
// VK_EXT_image_drm_format_modifier — all three are needed for the
// dmabuf-as-importable-image export path libghostty's Vulkan
// renderer uses to hand frames back to the host.
//
// On first use Host::instance() also primes the process-wide
// Wayland dmabuf modifier registry (see SubsurfacePresenter) on
// the calling thread, so the renderer-thread `get_supported_modifiers`
// callback can read it without further synchronization.
#pragma once

View File

@ -272,10 +272,10 @@ SubsurfacePresenter::tryCreate(QWindow *topLevel) {
// dependency but couldn't deliver lockstep resize because the
// two surfaces commit independently in that mode.
// Subsurface covers the parent at the origin. Phase 4 will keep
// this in sync on splits/tabs/etc.; for now the GhosttySurface
// forces WA_NativeWindow so its QWindow IS the terminal's native
// wayland surface and (0,0) is correct.
// Initial subsurface position: (0,0) in parent-surface coords.
// GhosttySurface immediately calls setPosition after tryCreate
// returns with the pane's real offset within the top-level (and
// updates it on every moveEvent / resizeEvent).
wl_subsurface_set_position(sub, 0, 0);
// Stack the subsurface BELOW the parent so Qt's child widgets