Render: integrated workspace, preview image jobs and opaque PBR (#1046)
Lands the Render workspace (R3), bounded preview-image jobs with cancellation and atomic PNG export (R4a), and opaque PBR looks as one change. - Render workspace: an overlay-free product preview on the installed GPU device. Returning to Design restores its camera. render.scene and render.settings.apply are added. - Image jobs: render.preflight/start/status/cancel/export run bounded CPU preview jobs. Export captures the job, overwrite choice and document, and checks the caller's file scope before writing. - Opaque PBR: looks can carry metal fraction, roughness and dielectric IOR. The product viewport and preview PNG use them; geometry and mass are unchanged. The v1 catalog export refuses PBR rather than dropping it. - Render setups and PBR looks are saved under design format 21. Labels are in en/de/ja/ko/zh/ru. Supersedes #706 and #800. Refs #620, #673, #756, #1004. Contributed by @0xlillith. Co-authored-by: lillith <35337584+0xlillith@users.noreply.github.com>
This commit is contained in:
1 parent
e4ef6cfe29
commit
a261aaa841
70 files changed
+5860
-101
No files matched your search
@@ -0,0 +1,119 @@
|
||||
# Product-image jobs (R4a)
|
||||
|
||||
R4a adds bounded CPU preview images to the Render workspace. The installed `builtin` /
|
||||
`cpu-product` executor supports Preview, Image and PNG. It does not promise refraction,
|
||||
HDRI, exposure, final mode or EXR; those requests fail explicitly. The subsequent
|
||||
[opaque PBR consumer](render-opaque-pbr.md) adds opt-in direct-lit surfaces through
|
||||
the same host. Legacy scalar looks and the Design snapshot renderer retain their
|
||||
existing shading.
|
||||
|
||||
## One command path
|
||||
|
||||
`render.preflight` and `render.start` take `setup` (a saved name) OR `settings` (a
|
||||
RenderSetup object; omitted fields use its documented defaults), plus optional
|
||||
`transparent_background` and `replace` booleans, both false by default. Without either
|
||||
setup/settings, the current requested camera is used, or the camera is fitted to the
|
||||
product. Saving a setup is independent of starting a job. Unknown fields are errors.
|
||||
|
||||
Both commands reject stale geometry without settling a live modelling gesture. Finish
|
||||
the gesture or use the explicit `render.scene` boundary first. Capture uses R3's
|
||||
authoritative visible meshes, placements and appearances, without selection, edges,
|
||||
axes, grids, sections or analysis overlays. A cold capture can prepare world geometry
|
||||
and meshes synchronously; it is not a promise of bounded CAD evaluation latency.
|
||||
Preflight validates capabilities and estimates admission, but does not reserve a queue
|
||||
slot or the host memory pool. Start checks those resources again.
|
||||
|
||||
Start returns a typed `job: {session, serial, generation}`. Keep all three fields when
|
||||
calling `render.status`, `render.cancel` or `render.export`. IDs are never accepted by
|
||||
another document session, scratch copy, or a newly opened/recovered document. Status
|
||||
does no rendering and never waits for completion. It reports queued, preparing,
|
||||
running, encoding, cancelling, cancelled, completed or failed, with phase-local
|
||||
`done/total`, structured job errors and completed frame metadata. Progress restarts
|
||||
when a phase changes; it is not a time estimate.
|
||||
|
||||
The captured `scene_generation` is the session revision at start. Later edits, undo,
|
||||
camera changes and tab switches do not mutate the snapshot. An image continues to
|
||||
describe that captured scene; the UI labels a revision mismatch conservatively (view
|
||||
cache invalidation can also advance a revision). Runtime state is outside the document,
|
||||
undo, checkpoints and the design format. Closing/replacing a session cancels its work;
|
||||
the worker cannot publish a cancelled frame. Direct host-side document replacement
|
||||
preserves the installed image service while allocating fresh job ownership; old IDs
|
||||
and completed images never cross into the new document. `replace: true` cancels retained active
|
||||
and pending jobs before submitting the new request. A cancelled generation cannot
|
||||
publish late. A successful older generation never replaces a newer successful result.
|
||||
|
||||
Each document retains at most two live jobs, four recent terminal status records and
|
||||
one last successful image. Cancellation/failure preserves that image. A new successful
|
||||
image releases the previous image; old terminal metadata can still report completion,
|
||||
but it does not promise that its bytes remain exportable. Export requires an explicit
|
||||
retained completed job ID; it never silently exports a different/newer job.
|
||||
|
||||
## Native and browser execution
|
||||
|
||||
Each application installs one CPU host, shared by its documents. Native execution uses
|
||||
one worker and at most two queued inputs across documents; it runs without UI polling.
|
||||
The browser uses the same incremental task, pumped by UI frames, without threads.
|
||||
Preparation, stable transparency sorting, clearing, pixel tests and downsampling check
|
||||
cancellation between small units. A step admits at most 16384 units; PNG encoding
|
||||
additionally yields after four rows. The UI pumps 1024 units per frame. These are work
|
||||
bounds, not hard real-time guarantees: allocation, codec compression and OS scheduling
|
||||
still have platform-dependent latency.
|
||||
|
||||
Admission caps output at 4096 per dimension and 4 million pixels; pixel scratch is
|
||||
estimated at 48 bytes per output pixel (2x supersampling). Other limits: 262144
|
||||
triangles/face overrides/edge polylines, 128 MiB retained source geometry, 384 MiB total
|
||||
estimated memory per job, 512 MiB shared host admission including retained PNGs, and
|
||||
128 million projected pixel tests. The projected-work limit is checked during bounded
|
||||
preparation and may fail an admitted job. Encoded PNG bytes have an additional
|
||||
`8 * output_pixels + 65536` cap. Allocator and codec internals are estimates, not an
|
||||
operating-system memory quota. Limits fail with errors rather than allocating unbounded
|
||||
buffers. Completed frames keep their memory reservation while bytes are retained.
|
||||
|
||||
The CPU path shades in linear light, blends sorted triangle alpha approximately, then
|
||||
encodes 8-bit sRGB with straight alpha and an sRGB PNG chunk. Transparent shared edges
|
||||
use a consistent fill rule. Intersecting transparent surfaces and triangle sorting
|
||||
remain raster approximations. This is not a glass/refraction renderer. Native GPU
|
||||
preview and CPU export can differ in shading and transparency.
|
||||
|
||||
PNG uses the already-transitive Rust `png` crate (MIT OR Apache-2.0), now an explicit
|
||||
render dependency. No external render engine or third-party art is introduced.
|
||||
|
||||
## Export and automation
|
||||
|
||||
`render.export` takes `job`, `path` ending in `.png`, and `overwrite` (default false).
|
||||
The destination is checked against the caller's existing file-access scope before
|
||||
reading the retained frame or calling either native writer. Disallowed folders and
|
||||
traversal are refused in both overwrite modes. Native no-clobber export writes/syncs a temporary beside the target and publishes it
|
||||
with an atomic hard link; an existing destination wins even in a race. Filesystems
|
||||
without that operation return an error. Explicit overwrite uses the existing atomic
|
||||
writer without reading or backing up the previous target. Failed writes preserve the completed
|
||||
image and CAD state. Browser export uses the existing virtual file/download service;
|
||||
no filesystem paths or base64 payloads are persisted in the design.
|
||||
|
||||
The Render panel provides Check Render Request, Start Render, phase progress,
|
||||
Cancel Render and Export PNG. Replace existing file is an explicit opt-in. A failed or
|
||||
cancelled new render leaves the previous successful export available.
|
||||
|
||||
Native PNG export in the combined integration uses the document-bound asynchronous
|
||||
picker from #1114. The requested job ID and overwrite choice are captured before the
|
||||
dialog opens: cancellation writes nothing, closing the originating document drops
|
||||
the answer, and an evicted image fails instead of substituting a newer result. A tab
|
||||
switch does not redirect the export to another document.
|
||||
|
||||
Controls and phase messages use stable offline catalog IDs in English, Japanese, Chinese,
|
||||
German and Korean; command IDs and scenario handles remain unchanged.
|
||||
|
||||
```sh
|
||||
solidcraft-cli render examples/render-studio.json --out product.png --width 800 --height 600
|
||||
solidcraft-cli render design.solidcraft --setup Studio --out product.png --transparent
|
||||
```
|
||||
|
||||
The CLI follows preflight/start/native wait/export, with a default 120-second wait
|
||||
limit (`--timeout 1..3600`). Timeout requests cancellation. `--overwrite` is required
|
||||
to replace an existing target. A persistent headless MCP session installs the same
|
||||
host: use the ordinary `execute` tool with the five commands and returned job ID.
|
||||
Closing the CLI/MCP process ends its session; a one-shot `exec render.start` is not a
|
||||
durable background service. In a browser, leave the app running so frames can pump work.
|
||||
|
||||
Validation evidence and responsiveness measurements are recorded in the R4a issue/PR;
|
||||
passing native tests or a wasm compile does not establish browser visual performance.
|
||||
Reference in new issue
Block a user