v0.17.0
Features
Async upload jobs
Long-running uploads can run on a background thread without freezing the viewport. The renderer owns a job runner that workers report to via a channel; the main thread drains completions once per frame from prepare_scene. Each call returns a JobId immediately; consumers poll upload_status(JobId) or attach a completion callback, then take the resulting handle with upload_result_*(JobId). The synchronous upload_* entries keep their signatures and are now thin wrappers that submit a job and wait.
Async entry points are wired up for every upload that previously blocked the main thread:
- Environment maps (
begin_upload_environment_map). Irradiance convolution, GGX prefilter, and BRDF LUT generation run on a worker. The GPU compute path no longer blocks ondevice.poll. The BRDF LUT is cached after its first generation. - Mesh data (
begin_upload_mesh_data). Tangent computation, vertex repack, and normal-line generation move to a worker. - Skin weights (
begin_upload_skin_weights). - Textures and normal maps (
begin_upload_texture,begin_upload_normal_map). - Gaussian splats (
begin_upload_gaussian_splats) and overlay textures (begin_upload_overlay_texture). - Volume meshes:
begin_upload_volume_mesh_data,begin_upload_clipped_volume_mesh_data,begin_upload_sparse_volume_grid_data,begin_upload_projected_tet_mesh. - Volumes:
begin_upload_volumeandbegin_upload_volume_for_mc. - The four curve types:
begin_upload_polyline,_streamtube,_tube,_ribbon.
Three scene-item categories also gain a pre-upload + per-frame reference workflow (own an id, submit a lightweight ref item each frame): point clouds (PointCloudId), glyph sets (GlyphSetId), tensor glyph sets (TensorGlyphSetId), and two sprite variants for non-particle use (SpriteSetId for static billboards, SpriteInstanceSetId for entity sprites). SceneFrame gains matching *_refs fields. GlyphUniform and TensorGlyphUniform now carry a per-frame model matrix at offset 0; existing per-frame consumers see identical output.
Public types: JobId, UploadStatus, ProgressHandle, ResultSlot. New methods on ViewportGpuResources and ViewportRenderer: process_uploads, upload_status, uploads_pending, all_uploads_complete, on_upload_complete. ViewportRenderer::rebuild_camera_bind_groups is now public so consumers driving begin_upload_environment_map can rebuild bind groups themselves once the job lands.
Showcase 51 (eframe-showcase) demonstrates the system end to end with a sync vs async toggle, per-asset progress bars, and an in-flight count read from the same public API a consumer would use.
Item-type plugins
Plugins can ship a new kind of scene item without forking the lib. New categories register through an ItemTypePlugin trait and submit their per-frame data via SceneFrame::submit_plugin_items. The lib handles picking, selection outline, frustum cull, clip volumes, shadow casting, and OIT transparency for plugin items the same way it handles built-ins. Published WGSL helpers for lighting, transparency, and clipping keep plugin shaders in sync with the renderer.
Plugin-facing job API
ItemTypePlugin implementations can submit background work through the same runner the built-in uploads use. ItemFrameContext gains a jobs: Jobs<'a> field with submit_cpu<T, F>, status, and take<T: 'static>. The plugin trait surface is otherwise unchanged: a job submitted in frame N is consumable in frame N+1. A worked example lives at viewport-lib-terrain/examples/eframe_plugin_jobs.rs.
Improvements
- GPU cull service is now multi-batch.
CullSubmissioncarries per-batch metadata, atomic counter, and indirect-draw buffers alongside the instance AABB list; one call dispatches the cull compute for every batch. Per-mesh draw parameters live inBatchMeta, published fromplugin_api::cull.submit_cull_single_meshandsubmit_cull_shadow_single_meshkeep the simple call shape for one-mesh-N-instances plugins. The four-method dispatch surface onCullResourcescollapses to one. - Scene-graph lights gain built-in glyphs and picking.
scene::build_light_glyphs(&scene, &selection)returns aGlyphItemper light (sphere for point, arrow for spot/directional) plus aPolylineIteminfluence-volume wireframe for any selected light. - 8-light cap removed. The fixed
array<Light, 8>uniform is replaced by a per-frame storage buffer sized toMAX_SCENE_LIGHTS(currently 512). When the union ofEffectsFrame::lighting.lightsandSceneFrame::lightsexceeds the cap, the shadow-casting directional stays at index 0 and the rest are ranked byLightSource::importance * proximity_weight. upload_environment_mapis much faster. The CPU irradiance, GGX prefilter, and BRDF LUT loops now run in parallel viarayon::par_chunks_mut. A GPU compute path runs the three convolutions as compute dispatches when the device exposes Rgba16Float storage write, dropping a multi-hundred-millisecond cost to a few milliseconds.
Bug fixes
- Per-object draw path no longer collapses shared-mesh instances. The path used for two-sided, matcap, param_vis, scalar attribute, override, and wireframe items wrote every item's
ObjectUniforminto one buffer perMeshId, so when N scene nodes shared a mesh only the last write's transform survived. The renderer now maintains a per-scene-item pool of object uniform buffers and bind groups, indexed by position in the scene-items list, growing lazily with a cache key. - Excluded-item filter gaps across LDR, HDR, and OIT: the LDR filter now includes
matcap_id, the HDR filter andhas_transparentpredicate now includeparam_vis, and the non-instancing HDR check plus per-object OIT loop now acceptmaterial.is_blend()items at opacity 1.0. Previously matcap-only, param_vis-only, or fully opaque blend items could be silently invisible. - OIT instanced pipeline: fix init-order trap where the pipeline was never created when the first frame had an empty scene. Instanced transparent geometry added on later frames is now drawn correctly.
- Residual Y-up fixes. Hemisphere ambient now uses
world_normal.zinmesh.wgsl,mesh_oit.wgsl, and the two instanced variants; shadow up-vector fallbacks useVec3::Xwhen the light direction is collinear with Z;build_glyph_arrowis rebuilt along +Z, soGlyphItemarrows and directional/spot light glyphs point along the supplied vector; placeholder pick-hit normals for the curve types areVec3::Z. - Equirectangular IBL convention switched from Y-up to Z-up to match the rest of the library.
OverlayImageItem.alphanow actually fades the image. The pre-multiplied alpha blending was leaving RGB at full intensity. Same fix incidentally corrects soft-edge PNGs.
Removed
- Legacy async texture API:
upload_texture_async,PendingTextureId,is_upload_ready,promote_texture, and the bespoke staging-buffer pool that backed them. Usebegin_upload_texture+upload_result_textureinstead. Seedocs/migration-guides/upload-job-system.md.