GraphicApplication: Vulkan renderer, native windows, input and frame diagnostics #9

Merged
ilusha merged 5 commits from GraphicApplication into master 2026-10-29 10:38:29 +03:00

5 commits

Author SHA1 Message Date
0e2ed18f3d GraphicApplication: application loop, libraries, and replace Graphics
Some checks failed
Windows / build (pull_request) Has been cancelled
CMake on a single platform / build (pull_request) Has been cancelled
CMake / build (pull_request) Has been cancelled
application/ holds the frame loop. Application owns the profiler, the GPU
device, the window, canvas and debug UI, and drives an
ApplicationDelegate (wantsFrame, processFrame, drawFrame) instead of being
a base class. It is configured with ApplicationOptions (window size,
identity, platform context, resource directory, debug interface) and has an
explicit start/step/finish lifecycle driven by run(). Each frame it paints
the decorations around the delegate's drawing; ImGui renders last.

SessionRecorder records an Application's raw input through the window tap
and its frame profile, and saves them with the environment as one
directory for bug reports and replays.

Each src/ directory is its own library, linked only to what its headers
use: GraphicApplicationInputLayer, -Diagnostics, -Gpu, -BackendContract,
-Backend (the platform for the build target), -Window, -Canvas,
-Decorations, -DebugUI, and GraphicApplication on top. Only Window can see
the backend. graphic_application_resources() copies the shared fonts next
to an executable.

The old Graphics module is removed. WidgetApplication becomes a delegate
that owns its Application; widgets receive an InputView whose origin and
button visibility the update manager chooses, instead of mutating shared
input state. The Widgets debug overlay (DebugManager and its global
gDebugWidget) and the per-widget debug state it read are removed.
Consumers use typed text styles and image handles. The color wheel moves
from Canvas into the color picker widget; Math gains Vec2::angle() and
Vec2::rotated() for it. Duplicate per-module font copies are removed.

The port's MODULES_BUILD_* gates are replaced by ANDROID checks, and the
generic app id is set directly until the root desktop-entry helpers land.
GraphicApplication/.clang-format inherits the root style and requires
braces on every control-statement body.
2026-10-29 10:28:35 +03:00
92de3007e9 GraphicApplication: NanoVG canvas, window decorations and ImGui debug UI
canvas/ is a generic drawing interface on one NanoVG Vulkan context per
window surface. NanoVG reads the recording command buffer from the
GpuFrame handed to drawBegin(). It offers only generic primitives: shapes,
gradients, paths with holes, composite operations, typed text styles
(TextStyle, TextAlign, FontFace) and typed ImageHandles for files, pixels
and registered render targets. Origin and clipping are scoped: SavedState,
Translation and Placement restore them, and every primitive honours the
origin.

Text uses bundled faces (regular, bold, mono, math) with symbol,
special-character and emoji fallbacks, and a Cyrillic cut of the matching
weight last in each face's chain; tests check every bundled face loads and
covers the characters found in UI sources and the Russian alphabet.
rsc/FONTS.md records where each font comes from.

decorations/ owns client-side decorations as one concept: WindowDecorations
paints the base, rounded corners, outline and shadow of the same
WindowFrame the backend uses for edge hit-testing.

debug_ui/ owns the process ImGui and ImPlot contexts. DebugGUI renders
through the ImGui Vulkan backend with its own platform layer fed from
InputState, or runs headless so code that calls ImGui directly still works
when the interface is hidden.
2026-10-29 05:24:06 +03:00
711793ffd7 GraphicApplication: GPU device, window surfaces, window and platform backends
gpu/ replaces the OpenGL path and is split by lifetime:
- GpuDevice is the instance, device, graphics queue, command pool, one-shot
  uploads and render-target registry, shared by every window.
- WindowSurface is one window's surface, swapchain, render pass,
  depth-stencil and frames in flight. beginFrame() returns a GpuFrame
  (command buffer, frame slot, extent), the narrow view renderers record
  into. Render-finished semaphores are per swapchain image, Vulkan results
  are checked and failures throw GpuError.

backends/contract/ is the only thing a backend and the window share.
PlatformWindow is what a backend implements; it knows no coalescer or GPU
object. PlatformWindowHost is all a backend may call back: rawInput(),
onContentResized(), onSurfaceAvailable() and onSurfaceLost(), so the window
drives the GPU surface. Desktop-only manipulation is the optional
DesktopWindowControls interface. A backend also defines its surface
extensions, its resource directory and createPlatformWindow(), which
receives a typed PlatformContext.

- backends/wayland/ keeps xdg configure handling to itself and acknowledges
  it when the frame loop applies it. The seat is split per device
  (keyboard, pointer, tablet, text input, clipboard); clipboard contents are
  read asynchronously on the event loop. evdev key codes and the touchpad
  pinch converter live here. Resources resolve next to the executable.
- backends/android/ runs on native_app_glue and finishes the activity when
  the application returns or fails.

window/ owns the platform window, its WindowSurface, the coalescer and the
input queue, and exposes roles: WindowEventLoop, WindowInput (state,
injection, raw tap, delivery policies, the one place input is muted),
WindowControls (with desktop() for the optional controls) and
WindowServices. Window's public header names no backend.
2026-10-29 05:24:06 +03:00
d3fc5be922 GraphicApplication: diagnostics for the frame loop
diagnostics/ holds what the application loop uses to observe itself:

- Profiler samples section durations and counters per frame (wall and CPU
  time, mean/median/p95/p99). It is an explicit instance with no global;
  ProfileScope takes the profiler it reports to. GPU time is reported as a
  section like any other measurement.
- UiTrail is the stack of profile scopes open on the frame-loop thread. The
  profiler owns it and ProfileScope pushes onto it, so a hang report names
  the scope that stalled.
- HangWatchdog writes a report when a non-idle phase stalls: open scope
  trail, input count, thread stacks sampled through Callstack, and a
  blocked/busy verdict. Debugger pauses are not counted. Applications add
  their own state to reports through report file sources.
2026-10-29 05:24:06 +03:00
348daff9f2 GraphicApplication: platform-independent input layer
Starts the GraphicApplication module, which replaces Graphics. As in
Sketch2D, each library lives in src/<name>/ with its own public/ and
private/; only public/ is exported and headers are included by name.

input_layer/ is the boundary between platform backends and input consumers.
InputEvent is the one declaration of every event: a std::variant of small
structs (pointer sample, move, explicit PointerLeft, button, scroll, scroll
end, key, text, focus, gesture). InputSink::post() is the only entry point,
so the coalescer, the per-frame state and recordings all derive from the
same type instead of re-listing event kinds.

Keys and pointer buttons are separate enums (Key, PointerButton) and
separate events; KeyAction covers press, release and repeat.

InputCoalescer batches posted events per frame under delivery policies and
forwards every raw event to an optional tap. InputQueue collects delivered
events, releases held buttons and keys when the pointer or focus is lost,
and produces a read-only InputState: the ordered event log plus the pointer,
button, key, scroll and pressure state derived from it. InputView is a
consumer's view with its own pointer origin and optionally hidden buttons,
so callers never mutate shared state.

InputRecording serializes every event kind (text, key repeat and gestures
included) through one format table per variant alternative, and replays
into any InputSink. Two-finger touch gestures are tracked here too. The
layer depends on no window system and is unit-tested directly.
2026-10-29 05:24:06 +03:00