Commit Graph
106 Commits
Author SHA1 Message Date
Hakan Seven af0cc68c5a Merge pull request #1006 with block rendering fixes
Invalidate cached world-space block vertices when the base translation changes. Share the regular/mesh partition between full uploads and patches so instanced blocks are uploaded only once. Cover both paths with CPU and GPU regression checks.
2026-09-05 12:30:43 +03:00
PanosandClaude Opus 5 fb64e76f04 fix: let the wire arena serve drawings that contain blocks
Clicking to end a segment froze the UI until the whole drawing had been
re-uploaded. With block geometry cached the remaining cost is the regular
half, and on two drawings it dominates:

    113 k wires   regular=96ms  block=13ms  index=12ms   total 122ms
    198 k wires   regular=85ms  block= 7ms  index=25ms   total 118ms

It is rebuilt from scratch because `PersistentWireArena` -- the only
thing that patches wire buffers in place -- was disabled outright by:

    && !vp_wires.iter().any(|wire| wire.render_instance.is_some())

One block reference was enough; the reproducer has 11 363. The scene
already produces an incremental patch in 0.9 ms and the GPU side could
not use it.

Instanced wires are not something the arena has to handle: they are a
different batch type with their own pipeline. Partition them out, let the
arena own and patch the rest, and keep building `BlockWireGpu` for them.

The handle index is unaffected -- it maps into `vp_wires`, not into arena
slabs, and the patch path already maintains it.

Measured on the 198 k-wire drawing, per drawn segment:

    before   wire 350ms  (shared-fullupload)
    after    wire  12ms  (arena-patch)

with the whole click, including `bump_entities`, going from ~411 ms to
~20 ms.

Verified visually on drawings with and without blocks, through repeated
edits and undo/redo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 09:18:50 +03:00
Hakan Seven 89624f64fc Merge PR #1002: opt-in performance instrumentation 2026-09-04 20:11:02 +03:00
PanosandClaude Opus 5 6747d7125e fix: no GPU allocation path maps, however small the buffer
The previous commit converted the allocations that scale with the
drawing and deliberately left the small fixed-size ones -- uniforms, the
viewcube's static geometry, per-image and per-wipeout quads -- reasoning
that an allocation failure was not a realistic outcome for a few dozen
bytes.

That reasoning was wrong, and the log says so precisely. On the
reproducer the vertex buffers degraded exactly as intended:

  [gpu] uncaptured ... Out of Memory
  [gpu] uncaptured ... In Queue::write_buffer
          Buffer with 'block_wire.vertices' label is invalid
  ... errors #4 #8 #16 #32 #64 #128, session kept alive

and then the process died here:

  thread 'main' panicked at wgpu_core.rs:2253:
  Error in Buffer::get_mapped_range: Validation Error
  Caused by: Buffer with 'block_wire.const' label is invalid

'block_wire.const' is a single uniform struct. Once the device is out of
memory every allocation fails, including a 32-byte one, and
create_buffer_init maps it and panics. Size was never the relevant
property -- whether the path maps is.

So every remaining allocation goes through gpu_upload too: block-wire
const, the viewcube's seven buffers, the blit and clip-boundary buffers,
image instances/params/vertices, mesh material params and surface and the
empty index stub, the texture-hatch uniform, and all five wipeout
buffers. `grep -r create_buffer_init src` now finds only a comment, and
no mapped_at_creation:true remains outside gpu_upload's own docs.

`queue` is threaded through the last holdouts: upload_wipeouts,
upload_clip_boundary, MeshBatchStubs::new and
create_material_bind_group_from_resources.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 16:21:08 +03:00
PanosandClaude Opus 5 0d11421dea fix: free superseded wire buffers before allocating their replacement
The renderer shares one set of wire GPU buffers per wire_content_id
across every pane and viewport slot. Every geometry edit mints a new
content id, so every edit builds a new set -- and the old one stayed in
wire_buffer_cache until the map exceeded sixteen entries.

Sixteen entries is a sane bound for small drawings and fatal for large
ones. The policy counted entries, not bytes. On the reproducer a single
set is most of the card: the VRAM trace shows 572 MiB baseline rising to
1871 MiB on a 2048 MiB GT 1030. The second set has nowhere to go, so the
first edit after opening exhausts VRAM. The perf log shows exactly that
-- two `shared-fullupload` lines, an edit, then the process dies.

Evict on every miss instead of at a count threshold, and do it before
building the replacement rather than after, so the old allocation is
released while the new one is still being sized. Dropping this slot's own
handles first is what lets the retain see the superseded entry as
garbage; a pane still drawing that content keeps its own clone and its
strong count stays above one, so shared geometry is never pulled out
from under a live viewport.

The trade is that returning to a previous content id -- undo, mostly --
now rebuilds instead of hitting a warm entry. On a card where the
alternative is dying, that is the right way round.

Adds a PERF line reporting evicted and retained entry counts, so the
policy can be watched rather than assumed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 16:21:08 +03:00
PanosandClaude Opus 5 d0ef752532 fix: an out-of-memory GPU no longer aborts the process
The crash in #358 was not the out-of-memory itself. wgpu reports OOM as
recoverable and the app already installs a handler that logs and keeps
the session alive. What killed the process was what happened next:

  create_buffer_init maps the new buffer unconditionally, and mapping the
  invalid buffer an OOM device returns panics. That panic unwinds through
  iced's main-thread redraw, trips the wgpu-hal swapchain assertion in a
  destructor, and becomes a non-unwinding abort -- no chance to autosave.

Every allocation whose size scales with the drawing now goes through
src/scene/pipeline/gpu_upload.rs, which creates the buffer unmapped and
fills it with Queue::write_buffer. A failed allocation then surfaces as a
validation error on the write, which reaches install_gpu_error_handler
and degrades the frame -- exactly what that handler's comment promised:
"A bad frame must degrade, not end the session."

Converted: block-wire vertices and instances, the wire const storage
buffer, all three wire-arena allocators, face3d vertices and instances,
block-text instances and text vertices, both hatch backends, and the
silhouette buffers. mesh_gpu already allocated this way, which is why the
mesh path never produced this panic; the rest now match it.

No mapped_at_creation:true remains in the renderer. The create_buffer_init
calls left are fixed-size or bounded-small -- uniforms, the viewcube's
static geometry, per-image and per-wipeout quads -- where an allocation
failure is not a realistic outcome.

Two trade-offs worth stating:

  * write_buffer stages through a copy, which mapping at creation avoided.
    Chunking bounds that transient cost, and the measured block-wire
    upload got faster with chunking, not slower.
  * `queue` had to be threaded through the wire, face3d, text and hatch
    builders and their callers. Mechanical, but it widens the diff.

This touches every upload path, so it needs a visual check against a real
drawing before it goes upstream.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 16:21:08 +03:00
PanosandClaude Opus 5 2787dcc1a3 perf: instrument the file-open load path behind PERF
The 52 s silent stretch in a large-DWG open could not be attributed:
OpenTimings covered parse / purge / xref / caches, but the FINALIZING
phase -- prepare_open_geometry, which does the first full Model
tessellation and builds the interaction index -- was unmeasured and
reports only two progress ticks, so it was indistinguishable from parse
time.

Add finalize_ms to OpenTimings and a per-stage PERF breakdown:

  open-phases     parse / purge / xref / caches / finalize
  open-finalize   tessellation vs interaction index
  open-index      index build vs prepare_screen
  wires-build     sort cache, visible filter, block cache, classify,
                  memo hits, parallel tessellate, materialize, sort
  resident-post   scene markers, refedit fade, resident layout
  wire-upload     build_wire_buffers, which runs on the render thread

The wires-build line separates the parallel tessellate from the serial
materialize loop that deep-clones every WireModel while the memo retains
a second copy -- the leading hypothesis for both the stall and the
6.45 GB RSS.

No behaviour change. Every timer is gated on crate::perf::enabled(), so
nothing runs without PERF=1 or the in-app PERF panel. The localized
open-summary format string is left alone; changing it would rekey the
string across every locale, so the full breakdown goes to PERF instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 16:20:30 +03:00
Karim Jerbi 688785a6cf feat(model-space): theme-aware model space canvas and CAD selection/grip sysvars
Model space appearance now follows the active UI theme instead of a
hardcoded dark background. Canvas mode is user-selectable (Match Theme /
Classic CAD Dark / Custom RGB), with matching controls for the paper
sheet and desk surround backgrounds and grid opacity.
Implements industry-standard CAD system variables, all settable via
command name, SETVAR, and the Options dialog:
- COLORTHEME (0/1 dark/light UI scheme, preserves custom palettes)
- COLORSCHEME (interactive named-theme selection)
- BACKGROUND (THEME/CLASSIC/DEFAULT/DESK presets, RGB triplets)
- SELECTIONAREA, SELECTIONAREAOPACITY (translucent marquee fill)
- WINDOWSAREACOLOR/WINDOWAREACOLOR, CROSSINGAREACOLOR (ACI or theme)
- SELECTIONEFFECT, SELECTIONEFFECTCOLOR (GPU highlight pass, driven by
  selection_generation invalidation — no retessellation)
- SELECTIONPREVIEW (bit 0 = idle rollover, bit 1 = in-command hover)
- GRIPSIZE, GRIPCOLOR, GRIPHOT, GRIPHOVER (grip overlay styling)
2026-09-03 17:44:44 +01:00
Hakan Seven 2cd1d36661 fix(render): match model lineweight preview curve 2026-09-02 23:28:34 +03:00
Hakan Seven 76bdab1e5b feat(display): add model lineweight scale 2026-09-02 23:03:04 +03:00
Hakan Seven edbc4d8ff8 fix(render): correct lineweight display scaling
Remove the global display boost and scale paper-space lineweights with the current sheet zoom across wire, block-wire, and hatch paths.

Fixes #962
2026-09-02 22:32:53 +03:00
ramox81 a706858ff1 Use solid edges for normal selection 2026-09-02 14:38:22 +03:00
Hakan Seven 7ea4f338f6 fix(render): preserve transparency inheritance
Resolve ByBlock alpha independently from color and keep explicit opaque values from falling back to layer transparency.

Pin the matching cadcodec API across desktop, plugin, and web targets.

Refs HakanSeven12/cadcodec#2
2026-08-31 22:48:57 +03:00
Hakan Seven 853e830126 fix(grips): prevent phantom snapping
Keep hidden pre-drag geometry out of snap candidates and avoid empty
scene invalidations. Require stable OTRACK dwell observations and cache
planar-solid visibility during live previews.

Refs #930
2026-08-30 22:26:47 +03:00
Karim Jerbi 6bf84d8f5e feat(ui): show color swatch and layer in selection cycling popup
Improve the selection-cycling list box so overlapping objects can be
identified at a glance:

- Add a color swatch per row showing the object's effective drawn color
- Show the layer name right-aligned next to the element type
- Size the popup to fit its content with consistent padding

Widen render_style_for_viewport visibility from scene-internal to
crate-wide so the view layer resolves the same color the renderer uses.
2026-08-29 12:31:03 +01:00
gianlucafiore a2d4caf2f0 fix(render): improve dashed linetype rendering 2026-08-27 13:51:05 -03:00
Hakan Seven 8c61d89b1e fix(layout): complete paperspace integration
Apply each layout's embedded page setup and plot style consistently across display, PDF, printing, and multi-layout output.\n\nRefs #844
2026-08-26 08:58:44 +03:00
Hakan Seven fd801fac1b fix(hatch): render weighted pattern strokes
Show styled boundaries only while selected.\n\nRefs #821
2026-08-25 22:44:51 +03:00
Hakan Seven e94cbedaa2 fix(save): preserve 3D view and thumbnails
Persist camera, render state, and solid fallback geometry across saves. Generate previews from a clean live viewport and refresh recent-file thumbnails immediately.
2026-08-23 17:59:12 +03:00
Hakan Seven ee89458f8a fix(solid): use kernel geometry
Derive extrusion, bounds, edges, and fill from cadkernel while preserving precise view filtering and stable GPU uploads.
2026-08-21 19:47:03 +03:00
ramox81 17dc0aff39 Complete 2D solid workflow 2026-08-21 10:14:26 +03:00
Hakan Seven 44efb8fd53 feat(frame): complete frame controls
Preserve frame geometry for selection while separating viewport and plot visibility across supported reference entities.

Refs #642
2026-08-20 23:37:02 +03:00
ramox81 aee3397f5d fix(gradient): align properties and editing 2026-08-20 15:33:09 +03:00
gianlucafiore e21d4954a3 fix: respect drawing units in annotation scales 2026-08-12 23:41:22 -03:00
Hakan Seven 5e8221f8f1 perf(render): keep solid meshes resident
Keep all solid geometry GPU-resident during navigation. Use vertex-rate instancing, compact untextured vertices, vertex-cache ordering, and cached silhouette sources without culling geometry.
2026-08-12 16:02:10 +03:00
Hakan Seven 15ab027d70 fix(scene): preserve viewport settings
Reuse full VPort records during camera and tile synchronization so
navigation cannot replace file-defined display state with defaults.

Honor image, ground, IBL, and sun-backed sky parameters in
background rendering.
2026-08-12 13:11:12 +03:00
Hakan Seven 4fd9b98da4 perf(render): instance block geometry
Share source buffers across block placements and render nested content with GPU instance transforms.\n\nChunk silhouettes and compact mesh material data so large drawings stay under device buffer limits and remain responsive.
2026-08-12 02:33:10 +03:00
Hakan Seven dc6dc2256d fix(model): sync solid render interaction 2026-08-11 19:39:01 +03:00
Hakan Seven a1f64d7663 fix(render): restore solid surface rendering
Keep viewport styles from disabling selected shaded modes.\n\nPack mesh shader inputs to stay within WebGL limits.
2026-08-11 15:44:40 +03:00
Karim Jerbi 7ace7fef9e feat(render): per-viewport REDRAW force_rasterize through builders to prepare gate 2026-08-10 23:26:26 +01:00
Hakan SevenandClaude Opus 5 62dfbfe2be fix(paper): give the sheet back its desk and its edge
A paper layout painted its whole canvas in the page colour, so the sheet
had no visible edge — zooming out never reached one, because the surround
was the same colour as the page. Floating viewports then painted
themselves black over it.

Both come from the same idea, that a viewport's canvas is a colour it can
be handed. What lies behind a viewport depends on what the viewport is:

- The full-canvas sheet shows the desk. `paper_bg_color` is the page, and
  the page is a bounded rectangle `paper_sheet_fill` already draws; using
  it for the canvas too erased the boundary between them. The desk is now
  its own colour, defined once in the scene and read by both the widget
  and the renderer, so the two cannot drift apart.
- A floating viewport sits on that sheet and must paint no canvas at all.
  Handing it a transparent one does not work: the background shader
  returns an opaque colour whatever alpha it is given, so alpha zero came
  out black. Skipping the pass is the only way to ask for see-through, so
  `skip_background` now suppresses both the clear and the pass. It is part
  of the frame signature too — without that, a cached frame would keep
  showing the canvas the flag was just turned off for.

Shading and fog still blend toward what is behind a floating viewport,
which is the page, so the uniform keeps that colour even though nothing
paints it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 00:11:47 +03:00
Hakan Seven c45daeffb6 feat(render): add scene environment pipeline 2026-08-05 23:42:18 +03:00
Hakan Seven b1d55c6a7e feat(view): add visual style previews
- preview render modes on hover without committing
- distinguish 2D and 3D wireframe fill and depth behavior

Refs #483
2026-08-04 21:23:25 +03:00
Hakan Seven 282a5176af fix(groups): highlight full group on hover
Refs #454
2026-08-04 16:40:22 +03:00
Hakan Seven 5662e07303 feat(area): support multi-object selection 2026-08-04 00:01:12 +03:00
Hakan Seven 53cf31f33d feat(web): unify fonts and refine viewcube
Preload one language-aware font resource across the interface, drawing text, and navigation labels.\n\nRender the navigation cube, ring, outlines, and labels through a 4x MSAA target.
2026-08-03 15:28:51 +03:00
Hakan Seven baf9b240a4 feat(annotation): show all scales on highlight 2026-08-02 14:37:12 +03:00
Hakan Seven e03e091b3c feat(annotation): integrate object contexts 2026-08-02 12:00:15 +03:00
Hakan Seven f39481ac49 fix(scene): isolate layout render sources 2026-08-02 03:02:14 +03:00
Hakan Seven 8d5ccdbd25 fix(viewports): share command previews
Closes #548
2026-07-29 23:26:06 +03:00
Hakan Seven 572a662c71 fix(render): show hidden-line silhouettes
Closes #551
2026-07-29 23:14:30 +03:00
Hakan Seven 3897fb2661 fix(viewports): fit extents per pane
Closes #554
2026-07-29 23:03:06 +03:00
Hakan Seven 6ecd67ff6e feat(layer-state): add saved-state editor 2026-07-29 18:39:55 +03:00
Hakan Seven 93b9a840ff refactor(renderer): select paths by GPU limits
Keep storage fast paths on capable adapters and use packed or texture fallbacks only when device limits require them.\n\nCompact mesh-edge varyings to avoid WebGL shader rejection.
2026-07-29 12:49:41 +03:00
Hakan Seven 25166b9508 fix(hatch): preview pattern grip drag 2026-07-28 20:07:10 +03:00
Hakan Seven 00cc17ea55 fix(scene): isolate drawing-space state
Cancel stale interaction state across space changes and keep live previews and zoom extents in their active paper/model context.\n\nRefs #539\nRefs #540\nRefs #541
2026-07-28 18:51:20 +03:00
Hakan Seven 05b044f367 perf(scene): make geometry edits incremental
Avoid full document, cache, and GPU rebuilds for entity deltas so add, delete, history, selection, and live polyline edits stay responsive on large drawings.
2026-07-28 17:16:27 +03:00
Hakan Seven 2ac61bf99a fix(ui): theme remaining interface text 2026-07-28 12:29:31 +03:00
Hakan Seven 4887a53d66 perf(render): batch and cull mesh instances
Share repeated block geometry across GPU instances.\n\nPatch changed mesh chunks and cull large batches with indirect draws.
2026-07-27 01:11:21 +03:00
Hakan Seven 6fb68d1c7c feat(scene): integrate DWG object data 2026-07-26 23:38:38 +03:00