Table of Contents

Rendering Canvas on Web Service Application

Overview

The web application's 3D canvas is rendered on the server and streamed to the browser over a SignalR hub connection at /renderingHub. Disp/RenderingHub.cs serves that hub, Program.cs maps it, and the singleton in Disp/RenderingService.cs owns one native display engine per hub connection. The browser paints the frames that arrive and forwards the user's input back.

Core Component

  • Location: wwwroot-src/src/components/RenderingCanvas.vue
  • Purpose: owns one hub connection, forwards input to the server engine, and paints the frames the server streams back

Its hubUrl prop defaults to /renderingHub, and it exposes connect, disconnect, the eight view commands and clearCache — the surface through which wwwroot-src/src/components/RenderingCanvasToolBar.vue drives the canvas instance it is bound to.

Connection Management

SignalR Hub Connection

Each RenderingCanvas instance opens its own hub connection and therefore owns one connection ID; the server keeps one display engine per connection ID, so several canvases can be live at the same time. On initialization the hub sends the connection ID back to the caller as CanvasInitialized, and the component re-emits it as serverInitialized. It is re-emitted after every reconnect, because the server disposes the engine on disconnect and a reconnect gets a new ID — the parent page answers by registering its content against that new ID.

The connection ID is the index for every canvas operation. Disp/RenderingService.cs keys the engine dictionary, the last-input time, the last lossless-frame hash and the negotiated frame format by it, and Disp/RenderingHub.cs keys its own sketch-view cache the same way.

Engine Lifetime

The engine's life is the connection's: GetOrCreateEngine creates it when the canvas initializes, and RemoveEngine — called from OnDisconnectedAsync — disposes it and raises EngineRemoved. An HTTP endpoint therefore resolves an engine through the non-creating GetEngine; creating one for an ID that has already disconnected would orphan an engine no disconnect can ever clean up. Disp/StlPreviewService.cs subscribes to EngineRemoved to release the per-connection native topology the engine itself does not own.

Frame Encoding

The frame encoding is negotiated at initialization. InitializeCanvasV2(width, height, formats) with jpeg in the accepted list selects SkiaSharp JPEG frames delivered on ImageUpdateV2 at an adaptive quality — interactive frames compress harder and a still frame refines once — and adding png lets that refine be encoded lossless. A browser without createImageBitmap, or a server that does not carry the V2 method, falls back to InitializeCanvas and the gzip-RGBA frames delivered on ImageUpdate.

Connection ID Naming Convention

One value carries two names on the frontend:

  • renderingConnectionId — the parameter name used throughout the API wrappers under wwwroot-src/src/api, and the route-parameter name on most display controllers
  • renderingConnId — how the Execution page holds it (wwwroot-src/src/pages/ExecutionPage.vue), filled from the canvas's @server-initialized and passed to the typed wrappers in wwwroot-src/src/api/execution.ts, whose parameter is renderingConnectionId

Data Flow Architecture

Frontend Responsibilities

The component owns the connection and its reconnect schedule, forwards pointer, wheel, key, touch, resize and visibility events to the hub, and paints each frame as it arrives. A canvas the layout has hidden keeps its connection open and tells the engine to pause rendering instead, so a hidden canvas costs nothing while a simulation runs.

Backend Integration

Rendering and frame encoding happen on the server; a controller decides only what the engine shows, by resolving the engine from the connection ID and assigning its displayee. That is why any feature can put its own content on a canvas without touching the transport.

Example: Execution Controller

  • File: Execution/ExecutionController.cs
  • Method: InitializeExecution (POST /api/Execution/initialize/{connectionId})
  • Purpose: resolves the engine with RenderingService.GetEngine(connectionId) and assigns the Execution displayee to it

Six further controllers bind content the same way — the STL preview, the Tool House, the General Setup equipment, the machine tool, the Mech Builder general mechanism and the legacy HardNcEnv controller — each with its own route, all reaching the engine through the same connection ID. The first five bind a displayee of their own; the last binds the shared Execution displayee, and it has had no caller since the Legacy Controller page was removed on 2026-09-11 — the route is still mounted, but no canvas in the SPA initializes against it.

Key Points

  • Every canvas data-stream operation is indexed by the connection ID.
  • Rendering and frame encoding happen on the server; the frontend owns the connection, the input events and the painting of received frames.
  • The engine is created and destroyed with the hub connection, so anything else that holds per-connection resources releases them from EngineRemoved.

Source Code Path

See HiNC App Anatomy for git repository links.

Web Service Source Code Path

  • Disp/RenderingHub.cs — the hub: creates the per-connection engine, encodes and pumps frames, handles mouse, wheel, key, touch, resize and visibility, and disposes the engine on disconnect.
  • Disp/RenderingService.cs — the singleton holding the per-connection maps, the playback render throttle and the EngineRemoved event.
  • Disp/StlPreviewService.cs — the EngineRemoved subscriber that frees the File Explorer preview's native topology.
  • Program.cs — registers SignalR and maps the hub at /renderingHub.
  • Execution/ExecutionController.cs — the worked example above: InitializeExecution assigns the Execution displayee to the engine named by the connection ID.
  • Disp/StlPreviewController.cs — binds an STL preview to a canvas (api/stl-preview).
  • Mech/ToolHouseDisplayController.cs — the Tool House canvas binding (api/mech/tool-house-display).
  • Mech/EquipmentSetupDisplayController.cs — the General Setup canvas binding (api/mech/equipment-setup-display).
  • Mech/MachineToolDisplayController.cs — the machine-tool canvas binding (api/mech/machine-tool/display).
  • Mech/MechBuilder/GeneralMechanismDisplayController.cs — the Mech Builder canvas binding (api/general-mechanism/display).
  • Controller/ControllerController.cs — the legacy controller surface's canvas binding (initialize-display/{connectionId}), which assigns the shared Execution displayee; still mounted, with no SPA caller since the Legacy Controller page was removed.

Web Page Application Source Code Path

  • wwwroot-src/src/components/RenderingCanvas.vue — the client half: one hub connection per instance, encoding negotiation, frame painting, input forwarding and the reconnect loop.
  • wwwroot-src/src/components/RenderingCanvasToolBar.vue — the view-control bar bound to one canvas instance through its exposed methods.
  • wwwroot-src/src/pages/ExecutionPage.vue — mounts the main canvas, stores the ID as renderingConnId and initializes the Execution content from @server-initialized.
  • wwwroot-src/src/api/execution.ts — the typed wrappers whose renderingConnectionId parameter addresses that canvas.
  • wwwroot-src/src/components/execution/StepVolumePanel.vue — a second canvas on the Execution page, with its own connection and its own engine.
  • wwwroot-src/src/components/mech/EquipmentSetupPanel.vue — the General Setup page's canvas column.
  • wwwroot-src/src/components/toolhouse/ToolHouseSetupPanel.vue — the Tool House page's canvas column.
  • wwwroot-src/src/components/StlPreviewPane.vue — the File Explorer STL preview canvas.

See Also