Files
solvecraft/docs/render-workspace.md
0xlillith 1b55174743 Render: isolate lens saves and retain bounded previews
Save only lens parameters on existing CPU/GPU jobs while keeping unrelated
draft settings dirty. Retain pinned checkpoints through preview and status,
and release host-only snapshots outside the status lock.

Use the admitted PNG bound for registered-executor checkpoint slots. Preserve
executor work credits, final publication and regrade ownership. Add regression
coverage and align help, translations and capability documentation.
2026-11-05 04:22:03 +01:00

689 lines
46 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Integrated Render workspace (R3)
The native image panel offers CPU raster for schematic output, optional CPU ray tracing and
[GPU path tracing](render-path.md) as explicit image methods. All three methods can be edited and saved offline. Check and Start are enabled
only when the selected method has an installed supporting host; an unavailable method never falls back.
R4a builds on this viewport contract with a separately advertised CPU product-image
executor and Check/Start/Cancel/Export controls. See [render jobs](render-jobs.md) for
the asynchronous protocol, image limits, captured revisions and PNG export. The legacy
`ui.render` snapshot remains a separate route.
Choose **Render** in the workspace switcher after finishing a modelling operation.
The current integrated GPU renderer shows a separate product scene. Return to Design
restores its camera, projection preference and existing viewport drawing policy.
New native setups select CPU ray tracing with progressive sampling when the installed
host supports it. Existing saved final settings keep their interpretation.
Preferences > Graphics > **Use previous Render image defaults** restores the previous manual workflow.
A first entry with an active saved shot restores its authored camera when that camera
is more than the untouched default. An uncomposed setup still fits the current product.
Leaving Render remembers its transient camera separately from Design, so returning
preserves framing and a saved lens stays paired with its perspective projection.
Clicking a shot in the tree or Shots strip activates its numerical settings and
complete saved camera, including an active shot when its camera differs. Unsaved
edits retain Save/Discard/Keep. The standalone editor-selection command only focuses
its editor; numerical reloads, saves and Undo do not navigate. The compatibility preference keeps
the former camera-neutral thumbnail and entry-refit behaviour.
## Live image feedback
Render automatically computes a separate live image after visual edits settle for
200 ms. Appearance, lighting, camera, visibility and image settings invalidate the
capture; selecting an editor alone does not. The selected ray-traced method is retained.
The stage bar identifies the live resolution and accepted/current sample count and
offers Pause/Resume (`view.render_live {paused: true|false}`). Live resolution follows
the active output frame inside the free product canvas, with a longest edge of
512 pixels. Common output ratios use exact integer multiples; otherwise the live
image uses the nearest bounded pixel dimensions and picking uses that captured
aspect. Final output size and saved sampling settings are unchanged. Invalid
authored dimensions/sampling are refused before constructing the small live copy. An older image is marked as updating and cannot
be used to pick geometry against a different projection.
Automatic images belong to an isolated scratch session. They do not enter retained
results or replace the completed image selected for Copy, Export or Regrade. An
explicit Render action displays its progressing image. If live capture or rendering
is still releasing resources, the captured final request waits for cleanup; a changed
source cancels that pending request instead of silently rendering different settings.
CPU progressive and GPU jobs publish each complete nonfinal pass. Adaptive GPU
feedback distinguishes pilot passes from production iterations; each production
pixel has its own budget, and pilots are excluded from the final image. Fast
passes can coalesce between screen repaints. Unsupported combinations are not silently
converted to fixed sampling. Unsupported CPU optical combinations also retain their
settings and show their reason. These remain open parts of complete CPU/GPU parity.
## Retained comparisons and all-shot rendering
The result viewer retains up to eight completed images per document tab, outside
the design and undo history. `render.compare {left, right}` accepts `"live"` or a
zero-based retained-result index (oldest first); the sides must differ. It resolves
indices to job identities so later index shifts cannot retarget an open comparison.
`render.compare {off:true}` leaves comparison. The shared segments toggle the two
sources, Swap exchanges them, and the shared divider stays within 5–95%. Both sides
share image pan/zoom; the live product uses the captured camera and current design.
Dragging the divider cannot navigate either camera. Copy, export and regrade use
the compared result on the right (or the left when the right is Live). That selection
remains stable while new results arrive; evicting a source ends its comparison.
The Output footer's **All shots (n)** runs `render.batch`. The command snapshots up
to 32 saved shot cameras and names and uses one common image setup. The UI supplies
the current draft as `request` (the existing `render.start` parameters); a headless
`render.batch {}` uses the first saved setup in name order. Each queued shot keeps its
own captured camera while all other options remain the same. Each admission runs
through `render.start`, one job at a time. No shots, an active render/queue or stale
geometry refuses without editing the design or history.
`render.status {}` advances an explicitly started queue and returns
`{queue:{running,waiting,job,shot,error}}`; a status call with a job also includes
that queue metadata. The UI ticks advance queues in active and background tabs.
`render.cancel {}` cancels the active batch job and drops all waiting shots; an
explicit job ID retains the single-job cancellation API. Opening a stage, editor
or queue readout never starts a batch. The Shots strip reports running and waiting
counts, and completed shots enter the same retained-result list.
## Stage bar and floating docks
Render has no ribbon. A shared 88 pt two-row stage bar sits 8 pt to the right of
the 104 × 88 pt workspace card, with matching top and bottom edges. The icon sits
above the Render label and Workspace caption. The header is 36 pt; the remaining
52 pt contains the tools, centred vertically below a 1 pt divider. Numbered **Look**, **Light**, **Frame** and **Output** chips
run `view.render_stage {stage: look|light|frame|output}`. The selection belongs to the
open document tab's view state; it is not saved in the design. Opening a stage never
applies or saves settings or submits a final image job. It focuses the existing Inspector
page, while every Inspector page and registered Render command remains reachable.
The headless command validates and echoes the request without changing the document.
Render, Cancel and the shared command search (S) occupy the top row. The Pick control's
icon-text Body/Face/Component segments, Paint/Pick Up actions and direct-light
preview information chip occupy Look's second row with 8 pt side padding. Changing the
light coordinate frame remains disabled with its reason in this checkout. Frame
uses the existing explicit camera command for projection and the registered Save Shot
action opens the shared naming confirmation; Output shows the current
size/format and directs edits to the Output Inspector. No completion ticks are inferred.
The Browser and Inspector float in both shell layouts and share a top edge, 8 pt
below the bar. Their default outer widths are 266 pt and 305 pt, with shared resize
bounds. The Browser keeps the upper left card (up to ten compact scene rows); its
stage panel fills the remaining height below it, ending 8 pt above the 48 pt Shots
strip. Both cards share width and side preferences. Narrow windows shorten the
Browser to reserve library space, then omit cards that cannot fit safely.
Look hosts Appearances: search with the actual catalog count, In Design / Library /
Recent / favorites scopes, horizontally scrolling category chips and a three-column
sphere grid or list. Resources used in the design or matching a selected look's
finish family or category appear first. Clicking a look applies through
`appearance.assign` to selected bodies, faces or components; with no target it
only inspects. Favorites and recent history live in user preferences. Explicit
catalog navigation opens the corresponding stage library.
Look also offers **Surfaces and references**. Surfaces/References segments and search
lead to authored surface rows with availability badges. Selecting a surface loads
its bounded source-map preview; Target, Plane and Repeat precede Use uniform,
Use GPU maps and Save maps. GPU actions remain faded with a reason on other methods.
Physical profiles retain their properties and sources; Prepare creation plan opens
a read-only review with acknowledgement and Copy plan. It creates no material.
Light hosts **Studios and environments**: a Scope dropdown, search and three-column
tiles for Studios, Rigs, Environments and read-only Specifications. Mine remains
visible with the reason that personal studio saving is unavailable. Clicking selects;
Apply prepares a review without changing the method or saved settings.
The shared `ui_kit::ReviewAndApply` dialog presents a resource card, Replaces/Kept,
variant controls and explicit acknowledgement before applying. Studio, rig fit,
environment, ground/stage, physical plan, fixed quality budget and uniform/spatial
maps use this composition. Canonical Draft, Balanced and High quality chips instead
apply their listed fixed GPU budget as one reversible numerical edit. Sampling shows
size, samples, depth, seed and noise reduction; the chip hover lists all three budgets.
Other image settings and the saved/live cameras are kept. Library specifications
retain the acknowledged review. A captured review expires when the document, revision,
shot, method or settings change. Rig fitting offers centre, extent, captured bounds
and emitters-only projection. Quality review reports projected dimensions, samples,
depth and omissions; `render.quality.prepare` remains reachable through command
activation. S5 can call `render_workspace::review_resource(app,
"render.quality.prepare", id)` for its selected specification.
Programmatic commands continue to execute without opening dialogs.
Remove shot uses the existing shared `Confirm`; remove stage and saved optics now
also use it. Resource removal confirmations expire after a document revision change.
Confirmed reviews run `render.preset.apply`, `render.library.studio.apply` or
`render.library.environment.apply`, preserving validation and undo. Studios require an explicitly selected
GPU path setup; environments require CPU or GPU ray tracing. Frame shows a faded Frame presets placeholder with its Shot editor reason. Output hosts the Size presets card described below. Stage navigation itself never applies a resource.
The 110 pt compass uses the free canvas beside the Inspector; the navigation bar
keeps its shared handler. The Shots strip's independent `view.shots_strip`
preference is unchanged.
`view.render_focus {on: true}` hides the Browser, Inspector and Shots strip and
releases their canvas reservations. Tab toggles focus while Render is active and
no text field has focus; the **Exit focus** chip sits at the Browser's remembered
side below the stage bar. The workspace card, stage bar, compass and view bar
remain available. Focus belongs to the window, is never saved in a design or
preferences, and retains the docks' sides, widths and folded state.
Below 1100 pt, or with `view.layout {mode: compact}`, the docks use their shared
minimum width without overwriting the remembered width. Below 800 pt the Browser
folds into a chip. Its floating card opens through
`view.render_browser {open: true}` and closes through **Fold Browser**; it shields
navigation beneath the card without reserving a canvas column. Neither command
edits or evaluates the design, moves the camera or creates an undo step.
## Image methods and controls
**CPU raster** produces the existing preview PNG with approximate transparency.
**CPU ray tracing** requires a native worker and the `raytrace-cpu` build feature.
Fresh native setups use **CPU progressive** with sample and seed controls. The
normal UI presents progressive sampling as the active mode; a loaded fixed
setup keeps its existing 1, 4 or 16 spatial samples until the user explicitly
adopts progressive sampling. The compatibility preference also exposes the old
Single image / Progressive selector. Sample and image limits follow the selected
[CPU resource profile](render-cpu-resource-profiles.md). Both routes use
Neutral / Warm and cool / Key and rim lighting, cast shadows and exposure from
−16 to +16 EV. The UI proposes a smaller size only as an explicit action. Transparent
background exports straight-alpha edge coverage for both sampling modes; body and
face opacity still require 100%. Bloom, backdrop and photographic effects require
opaque output and remain explicit refusals. Additional collapsible sections expose body reflections,
captured HDR or constant environments, directional lights and point lights. Reflection
bindings require explicit confirmation after a document change; reusing a name cannot
redirect them to another body. Check and the current job display their separate HDR
capture metadata. See [CPU workspace controls](render-cpu-workspace.md) for units,
limits and the immutable capture contract. The common Glass editor supports stable
saved targets. Without Indirect lighting, CPU Glass uses the certified sheet/box
subset. With Indirect lighting, [general Glass](render-cpu-general-glass.md) adds
certified closed meshes under its geometry, transmission and scattering restrictions.
The Lights editor exposes point/spot and rectangular lights. Emission, surface effects
and anisotropy retain their bounded API contracts. Indirect lighting has its own
Sampling controls and validates saved or temporary optical bindings against the
selected transport; unsupported combinations refuse without removing the bindings.
**GPU path tracing** preserves asynchronous shader preparation and its timing display.
Its independent image device requires Vulkan; the live viewport may use another backend.
When no compatible Vulkan device is available, GPU settings remain editable and
saveable, while Check and Start explain why GPU execution is unavailable.
Sampling > Path tracing exposes progressive sample counts from 1 to 4096,
maximum path depth (1–16) and random seed. Exposure (−32 to +32 EV) lives in Shot. One setup
exposure field controls the selected image method. Switching methods retains their
local numeric drafts while submitting and saving only the active method. Reloading
or choosing another setup replaces those drafts with the saved numeric intent.
Automatic ray-traced live images use the captured image exposure, environment, lens and
supported material effects at interactive resolution. The integrated direct-light preview
is the fallback while no live image is available, or for Raster and the compatibility workflow.
Effects > Tone exposes Reinhard / ACES fitted tone mapping. Sampling > Path tracing
owns optional edge-aware noise reduction for progressive and final display images;
Shot owns thin-lens depth of field. Positive aperture
requires a perspective product camera. Denoising preserves raw accumulation but can
soften detail; glass and sharp specular first hits bypass its spatial filter.
The Environment Browser row opens the Environment editor. The legacy Environment
and Background pages use that same editor, with five sections in order: Source,
Orientation and strength, Backdrop, Ground and stage, and Atmosphere (GPU).
Source offers None, Artistic sky, HDR file and embedded HDR for GPU, or None,
Constant and HDR for CPU. Change opens the Environments scope of Studios and
environments. Embedded HDR shows its name, kind and saved-in-design status; sky
exposes zenith, horizon and ground linear colours. CPU HDR has lighting samples.
Radiance `.hdr` input is bounded to 16 MiB, 2048 × 1024, 2:1, and linear
sRGB/D65 primaries; unspecified primaries require the explicit interpretation checkbox.
EXR is not supported. Rotation and intensity affect illumination, while the saved
background remains the camera backplate. Selecting a file runs the bounded public
`render.hdr.inspect` command; a typed path has an explicit **Check HDR file** action.
The picker belongs to the originating document revision, setup, method and input
selection. A cancelled or stale answer never replaces a newer selection.
Rotate and Intensity use the existing environment command; the rotation ring is
their shortcut. Backdrop offers World, Colour, Gradient and Clear. Gradient uses
the existing GPU camera backdrop (vertical, horizontal or radial, two colours,
centre and radius). It never lights, casts shadows or reflects, and is refused
with transparent output. World visibility and blur remain faded because no saved
renderer setting backs them. Unsupported method choices stay faded with a reason.
Ground and stage fits a saved ground, floor and wall, or polygonal cyclorama.
Replacing a stage requires the explicit consent checkbox; removing it uses the
shared **Remove the saved stage?** confirmation. The fields expose extent margin,
clearance gap and linear reflectance, followed by the saved panel count.
Atmosphere edits the bounded participating medium: enable, world bounds in mm,
extinction, scattering albedo and phase. The transitional Aging page's airborne
section calls this same function. CPU and raster show the controls faded with
the GPU requirement. These controls use existing render state and commands;
opening the editor never changes the design or renderer.
The panel distinguishes a pending file, a checked file and **HDR saved inside this
design**. It shows dimensions, declared colour primaries and intensity; the source
digest is available in a collapsed details section. Check, Start and Save use the
checked file's expected SHA-256, rejecting changed bytes until an explicit recheck.
An already captured image retains its immutable source independently of later edits.
Custom studio lighting exposes constant linear-RGB environment radiance, a finite-radius sun,
and up to four one-sided rectangular emitters. World-space centers and orthogonal
half-edge vectors specify their geometry in millimetres; radiance is linear RGB.
An environment map replaces the constant environment term. The path integrator uses
the published emitter/BSDF MIS contract.
### Appearance editor and body optics
Picking a face, body or component opens Appearance. Its kind chips, look card and
Applies to scope lead to Edit: colour, finish, metallic, roughness, opacity and IOR.
The look card's source tag opens the inheritance chain. Reset restores the picked
target's values from when it was opened; Save to library uses the existing design
library export flow.
Glass and absorption (CPU/GPU), Clear coat (CPU/GPU), built-in Maps (CPU/GPU), Saved with this shot,
and Reflections (CPU) follow in that order and start collapsed. Unsupported
methods remain visible with an explanation; opening a section never changes the
renderer. Optical bindings currently target one whole visible body, so face,
component and multiple-body picks show the same controls disabled with a reason.
The Browser command has no Wear selection kind yet, so no Open Wear link is shown.
The old Appearance page retains explicit target selection and the library/reference
and wear controls awaiting their own editors. Its body optics use the same sections
as the picked-body editor. Scene ordinal, exact name, stable target and document
identity are checked; a document revision change requires Revalidate this body.
Six original glass recipes use linear transmittance over a reference distance to
compute absorption per millimetre via -ln(T) / distance. Glass exposes boundary,
IOR, thickness, RGB absorption, straight shadow transmission and closed-solid
interior scattering. Thickness is disabled for closed glass, which uses geometric
distance. Glass cannot share a body with coat or maps. Full topology, orientation,
overlap and optical-boundary checks use the selected producer. CPU without
Indirect lighting keeps its strict sheet/axis-aligned-box subset. Explicit CPU
Path supports certified general closed meshes, nested media and selected-medium
scattering; GPU has its own certificate and nested-mode policy. The common
controls never bypass those geometry or combination checks. See
[renderer parity](render-parity.md) for historical qualification and this change's Glass follow-up.
Clear coat retains weight, roughness and IOR in common CPU/GPU saved bindings.
Built-in surfaces can save base-colour, normal, metallic and roughness maps for
both methods, with stable target identity and a definition-local frame. External
PNG/JPEG picker maps remain GPU-only temporary inputs; portable external-map
import is separate unqualified work. The map-size, encoded-byte, repeat and
assignment limits still apply. File-picker completion checks the original
document, revision and body identity.
Local edits remain prospective on the document tab until saved. Save these optics
uses render.optics.bind; replacing different saved Glass requires the explicit
Replace this body's saved optics checkbox. Save coat to shot uses the separate
stable coat binding. Built-in surface bindings are also saved; external image
file mappings remain temporary. Remove saved opens the
shared Remove saved optics? confirmation before render.optics.unbind; stale
confirmations cannot affect a changed document. Saved authored surface bindings
remain manageable in Saved with this shot. Reflections retains this body's
Off/Mirror/Rough CPU binding and explicit target confirmation.
CPU uses the common IOR/Beer controls, while transport choices remain explicit.
Without Path, general boundaries, scattering and transmitted shadows refuse.
With Path, certified general Glass and thin transmitted shadows are supported;
closed transmitted shadows and external Atmosphere with Glass still refuse.
Coat/maps cannot share a Glass target, but unrelated opaque targets keep their
supported effects. Check and Start each use fresh captured intent. Strict
preparation performs the full general-Glass certificate and carries its proof
into that prepared job's renderer. A separate Render click prepares a fresh
capture; it does not reuse an earlier Check proof.
Saved target A and prospective target B merge as stable assignments; checked file
HDR capture preserves them on CPU and GPU without an ordinal-override detour.
Unsupported combinations refuse and no implicit Save occurs. See
[PR #11087](https://github.com/tangency-labs/solidcraft/pull/11087) for this
follow-up's exact source and qualification. Historical CI7 and opaque native
results remain separate.
Check and Start build the same request from the current product camera, size and
selected method, including when Render Result is open. Every click revalidates host
availability and scene readiness. The engine checks resource admission at Start;
an earlier successful Check does not reserve a queue slot or memory.
## Product scene
`engine::product_scene::extract` consumes authoritative evaluated geometry, reusing
the document's world/occurrence placement and mesh Arcs. Body, component, material,
face and imported appearances retain their existing precedence and opacity. Source
geometry is never transformed a second time or altered to serve a renderer.
The shared L1 `ProductScene`/`ProductMesh` contract contains world-space meshes,
resolved sRGB colours, opacity and face appearance overrides. Face indices are the
existing mesh-to-B-rep mapping, resolved through document appearance references;
they are not new persistent names or triangle-derived face identities.
There are no grid/axis/edge lines, sketches, construction objects, section cuts,
analysis shading, active-component fading, canvases or decals in the product scene. Browser visibility still controls which bodies appear.
The existing Design viewport and CPU snapshot routes keep their current policies.
Extraction refuses a stale live-drag model. The explicit model-reading `render.scene {}`
command follows the normal engine evaluation boundary, then returns bounded metadata:
body names, triangle counts, resolved colours/opacity, face-override counts and bounds.
It does not return mesh payloads, change the document or add an undo step. Product
extraction admits at most 16,384 visible bodies.
## Image result canvas
The image panel switches between **Live Preview** (the product camera) and
**Render Result** (the current CPU checkpoint or last completed PNG). Clicking the
small image preview also opens the result on the main canvas. Check, Start, Cancel and Export
remain available in the same panel. An empty result explains how to start a job;
it never substitutes the live preview for an exported image.
**Fit Image** frames the complete image and follows window resizing. **100%**
maps one image pixel to one physical display pixel, including on high-DPI screens.
Pan, wheel, trackpad and Space + drag use the shared Navigation preferences with
orbit disabled. Ctrl + arrow keys pan the image; Fit/Home frame it. Image navigation
does not move the product or Design camera and does not change the saved setup.
The canvas shows the image dimensions, executor, full job identity and captured
model revision. A visible warning identifies an earlier captured revision when the
document has changed. A CPU progressive checkpoint is explicitly provisional and
shows its samples per pixel and sequence. Its texture is cached by job identity
and sequence, separately from the last completed image. **Export final PNG** keeps
using that completed image even while a checkpoint is shown. Cancelling or failing
the active job clears its preview and returns to the retained final image.
Transparency is shown over a checker
background; display scaling and navigation never modify the PNG bytes.
Canvas selection, framing and pan/zoom are local to each open document tab. Switching
tabs never displays another document's texture. A new image starts fitted; later
checkpoints and transitions to final/cancelled state preserve pan/zoom. Final
publication is checked directly after the preview read so a stale status reply
cannot briefly select an older frame and reset the next frame's zoom. The canvas
itself adds no document fields; CPU sampling intent is saved with the job below.
## Settings and product camera
The right panel stages a named setup's camera, background, output and active image
method. **Save Setup** records the complete numerical `RenderSetup.job` and the current
product `Camera` through `render.settings.set`, with one undo step. A new checked HDR
uses `render.settings.capture_hdr` to save both source bytes and setup in that same
atomic step. Editing the name
is Save As; choosing an existing name from the picker loads that setup. **Reload Saved
Settings** replaces the numeric draft with the saved values. Drafts remain separate
for each open document, and loading a setup preserves completed images and result framing.
| Active method | Saved numeric intent |
| --- | --- |
| CPU raster | Transparency, with zero exposure |
| CPU ray tracing | Transparency, fixed AA or progressive SPP/seed, resource profile, optional opaque path depth, guided noise reduction, physical lens, tone map/finishing/backdrop and raw EXR retention, certified saved optical bindings, preset or custom directional lights, point/spot and area lights with samples/seed, shadows, constant RGB environment or embedded HDR with rotation/intensity/samples, setup exposure |
| GPU path tracing | Transparency, samples/depth/seed, tone map, denoise, lens, RGB studio lighting/sun/area lights, procedural sky or embedded HDR with rotation/intensity/explicit colour assumption, setup exposure |
CPU path lighting can combine with certified optical bindings under the
[general Glass restrictions](render-cpu-general-glass.md). Incompatible saved
intent still refuses before HDR IO or scene capture. Opening a GPU
setup retains non-power-of-two sample counts,
RGB environment values, exact sky gradients and radians. Explicit empty light lists,
black environments and absent settings remain distinct. The only exposure authority
is `RenderSetup.exposure_ev`: CPU requests lower it to their raytrace options with zero
outer exposure; GPU jobs use the same value in their job options. Choosing raster
explicitly selects its zero-exposure response.
Saving uses pure numeric validation and does not require an installed renderer or a
current evaluated scene. New HDR capture reads and validates the bounded source through
the engine; failure leaves the document, resource table and local file selection intact.
After success, the draft changes to the embedded resource and contains no source path.
Saving that setup again, Save As, Reload and design reopen use the included bytes, even
when the original file has been removed. CPU/GPU setups may share the same immutable
payload. The engine limits each source to 16 MiB and the design to 64 resources/64 MiB.
Check and Start use the same complete typed setup and then perform normal host, scene
and capture admission. Temporary bindings can still use an embedded HDR through the
runtime resource selector. Saved stable Glass, clear-coat and built-in surface
bindings travel with complete CPU/GPU jobs. Temporary runtime body bindings,
including disabled assignments and external GPU image maps, still cannot be saved
by full-job Save; use the explicit portable binding actions for supported inputs.
The panel explains those limitations and refuses unsupported Save before changing
the document. Temporary inputs can still execute through the transient request route. Older
setups with nondefault legacy environment rotation/intensity are also retained and
refused for full-job saving until explicitly converted; their values are never erased.
**Restore Saved Camera** runs `render.settings.apply {name}`, preserving target,
distance, yaw, pitch, roll, FOV and up axis as view state. It neither edits the document
nor settles a stale model. The UI honours its up axis in Render; the Design camera
is restored when returning. The camera moves exactly as in Design: the navigation preset,
Orbit around, zoom to the cursor, the SpaceMouse, the middle double-click, F6 and the axis
view or navigation cube all work and
**Fit Product** frames visible product bodies without including sketches or selection.
In the normal workflow, **Fit Product**, entry fit, product drawing, navigation,
live capture and picking always use the active output frame inside the free canvas.
Selecting Shot, Sampling, Effects or Output changes neither its camera nor aspect;
a portrait output therefore stays portrait in every editor. Entry fit waits for
the saved setup to load. The previous Frame-only projection policy remains behind
**Use previous Render image defaults**. `render.camera.fit` takes the picture's `aspect`
(width over height, 0.000001 to 1000000, default 1) and refuses a fit whose camera would be
out of range, leaving the current camera as it was.
Preview drafts do not make the document dirty until saved. Unknown backend preferences
and unsupported modes remain saved intent: the strict resolver reports the unsupported
viewport request. **Use Integrated Preview** is an explicit draft change, never an
automatic fallback. The method selector describes the desired image execution independently of the
installed host. It does not claim support for a saved final-mode or external-engine
preference. The integrated schematic fallback keeps its existing drawing policy;
its normal-workflow framing still follows the active output aspect.
## Host capabilities and resources
`render.backends` always retains the built-in `cpu-raster` preview/PNG image executor.
An installed CPU image host adds `cpu-product`; a native host with `raytrace-cpu`
also advertises `cpu-raytrace`, with its bounded PBR/HDRI/refraction profile. Discovery
does not select that method automatically or enable native workers in the browser.
Only an installed UI GPU host adds `gpu-viewport`, advertising **Preview / Viewport**
with no image formats or HDRI/refraction/progressive features. The installed
[opaque PBR consumer](render-opaque-pbr.md) adds the `pbr` feature. The host reports
effective limits from its created device and depth attachment: at most 4096 pixels
per dimension and 4,194,304 pixels, within the 256 MiB pixel scratch admission policy.
The estimate includes the selected MSAA colour/depth cost; it excludes scene geometry,
driver overhead and cached source meshes. It is not a measurement of total GPU memory.
The preview never refuses a frame because of the screen size. When the viewport's physical
size is past those limits (a maximised 4K or 5K window is 8–15 million pixels), the preview
draws at the largest size of the same aspect the executor admits (`RenderLimits::fit`, both
axes scaled by one factor, scratch included) and filters it up into the viewport. A viewport
within the limits draws at full size.
PNG images keep the saved-setup limits above. A setup past them is refused with the limits and
the largest size at its aspect (for example `4096 × 4096` → `2048 × 2048`), and the image panel
offers to use that size.
Pure capability metadata lives in the session/host, outside persistence and history,
surviving document replacement, scratch sessions and document-tab switches. No wgpu
types enter the engine or document crates. A host without an installed GPU/depth buffer
reports that product viewport preview is unavailable, rather than silently using the
opaque CPU snapshot path to approximate transparent appearances.
GPU product batches are separate from Design batches while sharing the installed
device and pipelines. Model/visibility/appearance changes update the product scene;
camera movement updates uniforms without rebuilding vertices. Selection and section
state do not change the product buffers. On desktop the preview takes up to eight of
the device's largest buffers of vertex geometry (2 GiB with the usual 256 MiB buffers,
never more than 2 GiB); uploads are split into buffers within that size. The web keeps
a 128 MiB budget. The budget is checked before allocation, and a refusal names the
scene's geometry size, the limit and how to get under it (hide bodies in Design). The
upload itself runs under its own GPU error scopes: an allocation the device refuses is
shown in the preview instead of drawing, and nothing else in the app changes how GPU
errors are handled. Messages sit on a theme panel card so they stay readable on any
background colour. The separate CPU image job keeps its own memory and work limits.
Malformed/non-finite triangles and invalid opacity fail.
Reinstalling the host invalidates product uploads. This is not a general device-loss,
render-job or cancellation protocol; those ownership mechanisms belong to R4.
Transparency uses the existing raster far/near alpha-blending approximation, without
refraction or HDR illumination. Opaque PBR uses a separate surface batch in the
same installed renderer. R3 itself added no export job; R4a supplies CPU product
PNG jobs. There is no result gallery or Bevy dependency. Saved setups were added
in format 21, alongside opt-in optical parameters on existing looks.
`ui.render` remains the existing Design snapshot API, using its installed renderer;
`ui.screenshot` captures
the actual integrated Render workspace. Product-image export uses `render.start`
and `render.export`, rather than the Design snapshot API.
## Reproduce
Run the original procedural `examples/render-studio.json` through the engine/CLI and
open its saved design in the app. Select Render, adjust its background or camera and
save a setup. The fixture uses original boxes/cylinders and simple colour/opacity
assignments; no commercial CAD files, artwork or materials are used. Actual Windows
Vulkan/MSAA evidence is attached to [#673](https://github.com/bherbruck/solidcraft/issues/673).
## Explicit Render camera and the Design view contract
`render.camera.set {camera}` applies a complete camera snapshot as transient view
state. It uses the saved-render camera's strict decoder and finite mm/radian
limits, preserves its explicit roll and up axis, and does not evaluate geometry,
change a saved shot, modify the design or add an undo step. Omitted roll/up use
zero/Z defaults; all other camera fields are required. Unknown fields, invalid
axes, nonfinite values and out-of-bounds numbers refuse atomically.
The Render inspector uses this command for numerical edits and before Frame
Subject. Design's `view.apply` retains upstream's independent policy: it accepts
finite roll (clamped to +/-pi) and refuses an explicit up axis. Render does not
reintroduce the former fork-specific extension to that Design command. Saving
or restoring a shot still uses the existing render-settings commands.
The camera pitch label is explicitly **Camera pitch**, so the legacy English
label adapter does not replace mechanical screw pitch translations. Material
axis conventions have stable catalog IDs; the numerical/wood conventions and
engineering data remain unchanged.
The legacy English-label adapter gives explicit catalog IDs precedence and, for
an ambiguous source label, retains a translation already present in the chosen
locale before falling back to English. Adding an untranslated Render alias no
longer hides existing Library, Appearance or Checkpoint translations. Contextual
Render labels still use their own stable IDs rather than borrowing a different
feature's meaning.
## Picking in the product view
The Look stage bar's second-row **Pick** control selects Body, Face or Component granularity,
using Design’s selection filter state through the `select.filter` command. Component
picks use the Browser’s exact occurrence path, including nested and repeated
placements; a body at the design root retains its body identity. Face picks use the
existing mesh-to-B-rep mapping and connected-surface selection rules.
Hover highlights the target; clicking selects it. Clicking empty space clears the
selection. Shift and Ctrl/Cmd follow Design’s add/toggle behavior and the Shift
add-only preference. Browser row selections and product picks share the same
selection commands and state, retained across Design/Render switches. Picking works
with the Shots strip shown or hidden. Render picking never starts a geometry edit.
Selection and hover use Design’s highlight drawing and theme tokens in a separate
GPU overlay on the live product view. The product scene and its upload stay unchanged.
CPU image jobs, exported PNGs, retained images and the Render Result canvas never
contain this overlay; the Pick control is disabled on the result canvas.
Look's tools row contains **Paint** (B) and **Pick Up** (I). A library click applies
to the current selection or starts painting when nothing is selected; dragging
previews the look until release. Esc or Done ends painting or sampling. Copy Look
and Paste Look use Ctrl/Cmd+Alt+C and Ctrl/Cmd+Alt+V.
The selection pill uses shared kit chrome and follows the projected selection.
Its actions open the look chain, Replace All, toggle a library favourite and hide
the selection. The name opens the selection menu. Assign, Replace All, Copy Look
and Remove are available there and in the Appearance Inspector; commands also
remain available to search and scripts. Replace All asks for the replacement in a
shared confirmation and applies one undoable command. A changed document cancels
that pending request. Hover uses the shared tooltip with the object and look names.
## Browser scene focus
The existing Camera Inspector page shows a standalone Shot editor when the Browser selection
is a saved shot. Opening it leaves the live camera and saved design unchanged. Camera fields
use `render.camera.set`; Update shot saves that camera and Revert restores the saved camera.
Name, output dimensions and exposure use the existing undoable shot/settings commands.
Distance is shown as a multiple of the visible subject's bounding-box diagonal (one millimetre
for an empty scene). The lens chip uses the camera's vertical FOV and a 24 mm sensor height.
Frame's floating panel offers five aspect presets and Custom, plus composition guides.
Clicking a preset saves its output dimensions in one undo step; Custom preserves dimensions.
`view.render_frame` independently validates and echoes transient aspect/guide preferences;
headless calls never edit a design. Guides are per-document UI state, never saved in design JSON.
Composition guides and their size/lens chips appear only in Frame or the Shot editor.
Their transient/selected-shot aspect is independent of the active output camera;
hiding or changing those guides never changes normal-workflow product framing,
live pixels or picking. Image jobs and the Result canvas omit guides. Retained
results and comparison images continue to use their captured dimensions.
The shared Browser lists Environment (with its source), Lights, saved Shots,
Objects and Output (with image dimensions and format). Shots **+** opens the
existing Shots manager; Lights **+**, Solo and Mute remain faded with an explicit
reason until named light commands exist. Existing light entries describe the
current numerical image setup, without inventing a saved rig.
`ViewState.selection` exposes `RenderSelection` for Nothing, Object (the existing
engine selection), Environment, Light, Shot and Output. It belongs to each document
view and is never saved in a design. `view.render_select` validates semantic focus
requests without opening dialogs or editing/evaluating a design. Object picks and
Browser selections keep the existing selection commands. Selecting Output opens
the Output editor; selecting Environment opens its editor. The legacy Environment
and Background entry points share the Environment editor.
Look expands Objects, Light expands Lights and Environment, and Frame expands
Shots. Output leaves expansion unchanged. Stages preserve selection and never
lock rows or prevent manual expansion.
## Appearance editor
Look shows one Appearance editor with scope, current look, the four-step look
chain and Edit controls. There is no Inspector page strip. Applies to chooses the
selected target or every exact user of the look; mixed values remain inert until
edited. Edit contains colour/hex, finish and metallic/roughness/opacity sliders.
Advanced reuses the existing surface library, optical bindings, glass, clear coat,
texture-map, reflection and aging controls. The separate Textures disclosure is
marked Soon. The shared footer exports the design library through `library.export`
and resets the current target's override through `appearance.assign`.
`view.render_inspector {visible}` controls transient per-document visibility;
selecting a stage reopens the editor. Output selection routes to its dedicated editor;
Environment selection routes to its editor. Other semantic selection focus routing
remains deferred.
## Output editor
The Output stage offers size, format, quality and a Check summary in the stage bar's
second row. Its lower-left Size presets card offers Preview (1280 × 720), Full HD,
4K, Square and Custom. Presets outside the selected executor's limits stay visible
with a disabled explanation showing the selected method's dimension/pixel limits and
largest fitting size. The default raster limit is 4,194,304 pixels, so 4K exceeds it;
methods with larger resource profiles can admit 4K. Custom retains the current size
and reveals its fields through `render.output.size`. Numerical size edits and preset
clicks patch width and height with `render.settings.set`, as one undo step. Opening
the panel never applies a preset or starts a job.
Selecting Output in the Browser shows a short editor with Renderer, Image,
Destination and Render sections. Sampling, Effects and Check are child rows under
Output, each with a summary and its own editor. Sampling owns Quality, GPU Path
tracing, Adaptive, Glass transport and CPU ray tracing. Method-specific controls
stay visible and faded with their reason on other methods. Effects owns Tone,
Creative looks, Lens effects, Finishing and Regrade, in that order; its controls
support CPU ray tracing and GPU path tracing. Effect edits patch the saved job with `render.settings.set`;
opening an editor never saves or switches methods. Check owns the preflight notes,
This request, Renderers and collapsed Under the hood diagnostics. The legacy
Effects, Output and Diagnostics pages call the same editors.
The stage bar's Check chip and Output's note count open Check. Check now runs the
read-only `render.preflight` without admitting a job. Notes offer links to their
owning editor. Copy report and diagnostic JSON use `render.preflight.copy` and the
platform clipboard queue; headless calls only validate and echo bounded text.
GPU Draft, Balanced and High apply a fresh canonical fixed budget in one undo step.
Sampling shows resolution, samples, path depth, seed and noise reduction; hovering
the chips lists all three budgets. Imported library specifications keep their
acknowledged review. The saved camera and other effects stay
intact; an incompatible combination leaves the draft unchanged. The highlight
follows the actual budget fields and returns to Custom after an edit. These GPU
budgets remain disabled on CPU and Raster. CPU keeps its own sample controls;
Raster remains the schematic method. EXR export is
available only for the selected completed image with an actual retained master.
Destination selection uses the completed-image export picker. The pinned Render
Image / All shots footer starts the active setup or queues all saved shots through
`render.batch`; opening the editor never starts a job.
Render and Cancel stay in the stage bar. Active jobs also show a single-row progress
pill with Cancel. The result viewer retains Copy, export, close, Fit/100% and shared
pan/zoom. R starts the active setup; Escape closes the image without cancelling its job.
Preferences › Graphics offers “Use previous Render preview geometry budget” (`render_previous_geometry_budget`). It restores the fixed limit (512 MiB on 64-bit desktop, 128 MiB elsewhere); by default, desktop previews use the device-based budget. Changes apply to the next preview frame.
CPU Glass uses the common body editor and stable saved optical targets. Fixed
jobs retain the strict sheet/axis-aligned-box subset and refuse scattering or
transmitted shadows. Explicit Indirect lighting selects the certified general
Glass path, including its conditional thin-shadow and scattering contracts.
Reopened controls preserve the actual saved numbers. Edits are prospective;
Save/replacement/Remove own document changes. Unchanged hydration and Discard do
not save or start a final job. The multi-target merge in this change does not
imply an automatic Save. See [general Glass](render-cpu-general-glass.md) and
[the qualification record](render-parity.md#qualification).