forked from 1b-agent/opencadstudio-upstream
fix(wasm): make the web build actually run in the browser
With the wasm bundle building, get it rendering in a browser:
- Enable iced `webgl` so wgpu uses WebGL2 where WebGPU is absent
(e.g. Firefox by default), and `fira-sans` so UI text has an
embedded font (the web has no system fonts) — without it every
label was blank.
- Replace `std::time::Instant` with `iced::time::Instant` (web-safe);
`Instant::now()` panics ("time not implemented") on wasm and crashed
at boot via the command line.
- Skip the batched-hatch pipeline on wasm: its bind group needs
vertex-stage storage buffers, which WebGL2 lacks (validation error
on "New drawing"). Fields become Option; upload/draw guard on them.
Hatches don't draw on the web for now; everything else does.
- Fix the trunk feature flag (`data-cargo-no-default-features`) and
ignore the `dist/` build output.
Native build + 75 tests unchanged.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
f2c4a1736a
commit
c49ad887b8
11 files changed
+55
-14
No files matched your search
+1
-1
@@ -41,7 +41,7 @@ pub struct GripHover {
|
||||
pub handle: acadrust::Handle,
|
||||
pub grip_id: usize,
|
||||
pub screen: iced::Point,
|
||||
pub started: std::time::Instant,
|
||||
pub started: iced::time::Instant,
|
||||
}
|
||||
|
||||
/// Open multi-functional-grip popup state.
|
||||
|
||||
+1
-1
@@ -7711,7 +7711,7 @@ impl OpenCADStudio {
|
||||
handle,
|
||||
grip_id,
|
||||
screen: p,
|
||||
started: std::time::Instant::now(),
|
||||
started: iced::time::Instant::now(),
|
||||
});
|
||||
self.grip_popup = None;
|
||||
} else if let Some(h) = self.grip_hover.as_mut() {
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ pub async fn open_path_with_phase(
|
||||
let path2 = path.clone();
|
||||
let phase2 = phase.clone();
|
||||
let (doc, caches) = std::thread::spawn(move || -> Result<_, String> {
|
||||
use std::time::Instant;
|
||||
use iced::time::Instant;
|
||||
phase2.store(PHASE_PARSING, Ordering::Relaxed);
|
||||
let t_parse = Instant::now();
|
||||
let mut doc = load_file(&path2)?;
|
||||
|
||||
+2
-2
@@ -1710,7 +1710,7 @@ impl Scene {
|
||||
Some([cx - w * margin, cy - h * margin, cx + w * margin, cy + h * margin])
|
||||
};
|
||||
let block = self.model_space_block_handle();
|
||||
let t_tess = std::time::Instant::now();
|
||||
let t_tess = iced::time::Instant::now();
|
||||
let arc = Arc::new(self.wires_for_block_culled(block, tess_region, wpp, None, None));
|
||||
self.last_tess_ms.set(t_tess.elapsed().as_secs_f32() * 1000.0);
|
||||
self.last_tess_wires.set(arc.len());
|
||||
@@ -1739,7 +1739,7 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
let layout_block = self.current_layout_block_handle();
|
||||
let t_tess = std::time::Instant::now();
|
||||
let t_tess = iced::time::Instant::now();
|
||||
let mut wires = self.wires_for_block(layout_block);
|
||||
self.last_tess_ms.set(t_tess.elapsed().as_secs_f32() * 1000.0);
|
||||
self.last_tess_wires.set(wires.len());
|
||||
|
||||
@@ -35,8 +35,9 @@ pub struct Pipeline {
|
||||
hatch_pipeline: wgpu::RenderPipeline,
|
||||
/// Phase 4-B — single-draw batched hatch pipeline. Per-instance
|
||||
/// data lives in storage buffers; one draw call covers every
|
||||
/// hatch in the frame.
|
||||
hatch_batched_pipeline: wgpu::RenderPipeline,
|
||||
/// hatch in the frame. `None` on WebGL2 (wasm), which lacks
|
||||
/// vertex-stage storage buffers.
|
||||
hatch_batched_pipeline: Option<wgpu::RenderPipeline>,
|
||||
image_pipeline: wgpu::RenderPipeline,
|
||||
mesh_pipeline: wgpu::RenderPipeline,
|
||||
/// Wireframe variant of the mesh pipeline (LineList topology, same
|
||||
@@ -56,8 +57,8 @@ pub struct Pipeline {
|
||||
uniform_bind_group: wgpu::BindGroup,
|
||||
hatch_bgl1: wgpu::BindGroupLayout,
|
||||
/// Group-1 layout for the batched hatch pipeline (storage buffers
|
||||
/// for instances / boundary / families / dashes).
|
||||
hatch_batched_bgl1: wgpu::BindGroupLayout,
|
||||
/// for instances / boundary / families / dashes). `None` on WebGL2.
|
||||
hatch_batched_bgl1: Option<wgpu::BindGroupLayout>,
|
||||
image_bgl1: wgpu::BindGroupLayout,
|
||||
depth_texture_size: Size<u32>,
|
||||
depth_view: wgpu::TextureView,
|
||||
@@ -355,6 +356,16 @@ impl Pipeline {
|
||||
});
|
||||
|
||||
// ── Hatch batched pipeline (Phase 4-B) ─────────────────────────────
|
||||
// WebGL2 (the wasm fallback backend) has no vertex-stage storage
|
||||
// buffers, which this pipeline's bind group requires. Skip it on wasm
|
||||
// and run without the batched-hatch optimization.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let (hatch_batched_bgl1, hatch_batched_pipeline): (
|
||||
Option<wgpu::BindGroupLayout>,
|
||||
Option<wgpu::RenderPipeline>,
|
||||
) = (None, None);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let (hatch_batched_bgl1, hatch_batched_pipeline) = {
|
||||
let hatch_batched_bgl1 = hatch_batched_gpu::HatchBatchedGpu::bind_group_layout(device);
|
||||
let hatch_batched_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("hatch_batched.pipeline_layout"),
|
||||
@@ -406,6 +417,8 @@ impl Pipeline {
|
||||
multiview: None,
|
||||
cache: None,
|
||||
});
|
||||
(Some(hatch_batched_bgl1), Some(hatch_batched_pipeline))
|
||||
};
|
||||
|
||||
// ── Mesh pipeline ──────────────────────────────────────────────────
|
||||
let mesh_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
@@ -1152,10 +1165,15 @@ impl Pipeline {
|
||||
}
|
||||
|
||||
pub fn upload_hatches(&mut self, device: &wgpu::Device, hatches: &[HatchModel]) {
|
||||
// No batched pipeline (WebGL2) → nothing to upload.
|
||||
let Some(bgl1) = &self.hatch_batched_bgl1 else {
|
||||
self.gpu_hatch_batched = None;
|
||||
return;
|
||||
};
|
||||
let renderable: Vec<HatchModel> =
|
||||
hatches.iter().filter(|h| h.boundary.len() >= 3).cloned().collect();
|
||||
self.gpu_hatch_batched =
|
||||
hatch_batched_gpu::HatchBatchedGpu::build(device, &self.hatch_batched_bgl1, &renderable);
|
||||
hatch_batched_gpu::HatchBatchedGpu::build(device, bgl1, &renderable);
|
||||
}
|
||||
|
||||
pub fn upload_wipeouts(&mut self, device: &wgpu::Device, wipeouts: &[HatchModel]) {
|
||||
@@ -1257,8 +1275,10 @@ impl Pipeline {
|
||||
// by `compute_hatch_lod`). Per-hatch viewport scissor
|
||||
// (paper-space MSPACE) isn't ported to the batched path
|
||||
// yet — follow-up if it shows up as a visual issue.
|
||||
if let Some(batch) = &self.gpu_hatch_batched {
|
||||
pass.set_pipeline(&self.hatch_batched_pipeline);
|
||||
if let (Some(batch), Some(pipeline)) =
|
||||
(&self.gpu_hatch_batched, &self.hatch_batched_pipeline)
|
||||
{
|
||||
pass.set_pipeline(pipeline);
|
||||
pass.set_bind_group(0, &self.uniform_bind_group, &[]);
|
||||
pass.set_bind_group(1, &batch.bind_group, &[]);
|
||||
pass.set_vertex_buffer(0, batch.vertex_buffer.slice(..));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! OpenCADStudio-style command line — bottom panel with input and history
|
||||
|
||||
use std::time::Instant;
|
||||
use iced::time::Instant;
|
||||
|
||||
use crate::app::Message;
|
||||
use iced::widget::{button, column, container, opaque, row, scrollable, text, text_input};
|
||||
|
||||
Reference in new issue
Block a user