@bornengine/engine 0.4.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +231 -0
  3. package/native/android/Cargo.lock +1848 -0
  4. package/native/android/Cargo.toml +24 -0
  5. package/native/android/src/lib.rs +702 -0
  6. package/native/ios/Cargo.lock +1690 -0
  7. package/native/ios/Cargo.toml +32 -0
  8. package/native/ios/src/lib.rs +1267 -0
  9. package/native/linux/Cargo.lock +3279 -0
  10. package/native/linux/Cargo.toml +29 -0
  11. package/native/linux/src/lib.rs +1331 -0
  12. package/native/macos/Cargo.lock +3310 -0
  13. package/native/macos/Cargo.toml +46 -0
  14. package/native/macos/src/lib.rs +1302 -0
  15. package/native/shared/Cargo.lock +1899 -0
  16. package/native/shared/Cargo.toml +62 -0
  17. package/native/shared/assets/default_font.ttf +0 -0
  18. package/native/shared/build.rs +270 -0
  19. package/native/shared/shaders/common/clouds.wgsl +122 -0
  20. package/native/shared/shaders/common/fog.wgsl +16 -0
  21. package/native/shared/shaders/common/foliage_wind.wgsl +98 -0
  22. package/native/shared/shaders/common/imposter.wgsl +112 -0
  23. package/native/shared/shaders/common/pbr.wgsl +186 -0
  24. package/native/shared/shaders/common/shadows.wgsl +186 -0
  25. package/native/shared/shaders/common/sky.wgsl +8 -0
  26. package/native/shared/shaders/common/tonemap.wgsl +25 -0
  27. package/native/shared/shaders/impulse_field.wgsl +57 -0
  28. package/native/shared/shaders/material_abi.wgsl +383 -0
  29. package/native/shared/shaders/materials/test_minimal.wgsl +42 -0
  30. package/native/shared/src/anim_mixer.rs +61 -0
  31. package/native/shared/src/attach.rs +263 -0
  32. package/native/shared/src/audio/decode.rs +123 -0
  33. package/native/shared/src/audio/mod.rs +863 -0
  34. package/native/shared/src/audio/render.rs +892 -0
  35. package/native/shared/src/audio/spsc.rs +156 -0
  36. package/native/shared/src/audio/stream.rs +226 -0
  37. package/native/shared/src/custom_shaders.rs +104 -0
  38. package/native/shared/src/decals.rs +245 -0
  39. package/native/shared/src/drs.rs +211 -0
  40. package/native/shared/src/engine.rs +261 -0
  41. package/native/shared/src/ffi.rs +116 -0
  42. package/native/shared/src/ffi_core/assets.rs +388 -0
  43. package/native/shared/src/ffi_core/audio_ffi.rs +184 -0
  44. package/native/shared/src/ffi_core/draw.rs +334 -0
  45. package/native/shared/src/ffi_core/game_loop.rs +577 -0
  46. package/native/shared/src/ffi_core/input.rs +234 -0
  47. package/native/shared/src/ffi_core/mod.rs +127 -0
  48. package/native/shared/src/ffi_core/models.rs +1154 -0
  49. package/native/shared/src/ffi_core/ragdoll_ffi.rs +261 -0
  50. package/native/shared/src/ffi_core/scene.rs +626 -0
  51. package/native/shared/src/ffi_core/vfx.rs +212 -0
  52. package/native/shared/src/ffi_core/visual.rs +691 -0
  53. package/native/shared/src/frame_callbacks.rs +122 -0
  54. package/native/shared/src/geometry.rs +236 -0
  55. package/native/shared/src/handles.rs +182 -0
  56. package/native/shared/src/input.rs +448 -0
  57. package/native/shared/src/jolt_sys.rs +822 -0
  58. package/native/shared/src/lib.rs +55 -0
  59. package/native/shared/src/models.rs +1093 -0
  60. package/native/shared/src/models_gltf.rs +1280 -0
  61. package/native/shared/src/particles.rs +391 -0
  62. package/native/shared/src/physics_jolt.rs +1908 -0
  63. package/native/shared/src/picking.rs +298 -0
  64. package/native/shared/src/postfx.rs +345 -0
  65. package/native/shared/src/profiler.rs +492 -0
  66. package/native/shared/src/ragdoll.rs +474 -0
  67. package/native/shared/src/renderer/atmosphere_lut.rs +573 -0
  68. package/native/shared/src/renderer/brdf_lut.rs +154 -0
  69. package/native/shared/src/renderer/draw2d.rs +143 -0
  70. package/native/shared/src/renderer/formats.rs +822 -0
  71. package/native/shared/src/renderer/froxel.rs +421 -0
  72. package/native/shared/src/renderer/gi_bake.rs +653 -0
  73. package/native/shared/src/renderer/graph.rs +462 -0
  74. package/native/shared/src/renderer/hiz.rs +269 -0
  75. package/native/shared/src/renderer/hot_reload.rs +390 -0
  76. package/native/shared/src/renderer/impulse_field.rs +456 -0
  77. package/native/shared/src/renderer/lighting.rs +154 -0
  78. package/native/shared/src/renderer/material_instancing.rs +171 -0
  79. package/native/shared/src/renderer/material_pipeline.rs +700 -0
  80. package/native/shared/src/renderer/material_system.rs +1996 -0
  81. package/native/shared/src/renderer/material_system_tests.rs +601 -0
  82. package/native/shared/src/renderer/material_system_wasm.rs +41 -0
  83. package/native/shared/src/renderer/mod.rs +12556 -0
  84. package/native/shared/src/renderer/model_draw.rs +641 -0
  85. package/native/shared/src/renderer/occlusion.rs +429 -0
  86. package/native/shared/src/renderer/planar_pass.rs +593 -0
  87. package/native/shared/src/renderer/planar_reflection.rs +499 -0
  88. package/native/shared/src/renderer/post_pass.rs +249 -0
  89. package/native/shared/src/renderer/postfx_chain.rs +728 -0
  90. package/native/shared/src/renderer/pt_pass.rs +577 -0
  91. package/native/shared/src/renderer/scene_pass.rs +607 -0
  92. package/native/shared/src/renderer/shader_include.rs +205 -0
  93. package/native/shared/src/renderer/shader_library.rs +135 -0
  94. package/native/shared/src/renderer/shaders/ao.rs +570 -0
  95. package/native/shared/src/renderer/shaders/core.rs +1243 -0
  96. package/native/shared/src/renderer/shaders/env.rs +907 -0
  97. package/native/shared/src/renderer/shaders/gi.rs +810 -0
  98. package/native/shared/src/renderer/shaders/mod.rs +19 -0
  99. package/native/shared/src/renderer/shaders/post.rs +1558 -0
  100. package/native/shared/src/renderer/shaders/pt.rs +1859 -0
  101. package/native/shared/src/renderer/shaders/ssgi.rs +1586 -0
  102. package/native/shared/src/renderer/shadow_pass.rs +731 -0
  103. package/native/shared/src/renderer/ssgi_pass.rs +392 -0
  104. package/native/shared/src/renderer/ssr_pass.rs +188 -0
  105. package/native/shared/src/renderer/texture_store.rs +473 -0
  106. package/native/shared/src/renderer/transient.rs +591 -0
  107. package/native/shared/src/renderer/types.rs +941 -0
  108. package/native/shared/src/renderer/util.rs +152 -0
  109. package/native/shared/src/scene.rs +1362 -0
  110. package/native/shared/src/sdf_cache.rs +274 -0
  111. package/native/shared/src/shadows.rs +1036 -0
  112. package/native/shared/src/staging.rs +102 -0
  113. package/native/shared/src/string_header.rs +266 -0
  114. package/native/shared/src/text_renderer.rs +502 -0
  115. package/native/shared/src/textures.rs +197 -0
  116. package/native/tvos/Cargo.lock +1693 -0
  117. package/native/tvos/Cargo.toml +36 -0
  118. package/native/tvos/metal-patched/Cargo.toml +178 -0
  119. package/native/tvos/metal-patched/LICENSE-APACHE +201 -0
  120. package/native/tvos/metal-patched/LICENSE-MIT +25 -0
  121. package/native/tvos/metal-patched/src/acceleration_structure.rs +667 -0
  122. package/native/tvos/metal-patched/src/acceleration_structure_pass.rs +108 -0
  123. package/native/tvos/metal-patched/src/argument.rs +366 -0
  124. package/native/tvos/metal-patched/src/blitpass.rs +102 -0
  125. package/native/tvos/metal-patched/src/buffer.rs +71 -0
  126. package/native/tvos/metal-patched/src/capturedescriptor.rs +76 -0
  127. package/native/tvos/metal-patched/src/capturemanager.rs +113 -0
  128. package/native/tvos/metal-patched/src/commandbuffer.rs +192 -0
  129. package/native/tvos/metal-patched/src/commandqueue.rs +44 -0
  130. package/native/tvos/metal-patched/src/computepass.rs +107 -0
  131. package/native/tvos/metal-patched/src/constants.rs +152 -0
  132. package/native/tvos/metal-patched/src/counters.rs +119 -0
  133. package/native/tvos/metal-patched/src/depthstencil.rs +190 -0
  134. package/native/tvos/metal-patched/src/device.rs +2134 -0
  135. package/native/tvos/metal-patched/src/drawable.rs +39 -0
  136. package/native/tvos/metal-patched/src/encoder.rs +2041 -0
  137. package/native/tvos/metal-patched/src/heap.rs +281 -0
  138. package/native/tvos/metal-patched/src/indirect_encoder.rs +344 -0
  139. package/native/tvos/metal-patched/src/lib.rs +657 -0
  140. package/native/tvos/metal-patched/src/library.rs +902 -0
  141. package/native/tvos/metal-patched/src/mps.rs +575 -0
  142. package/native/tvos/metal-patched/src/pipeline/compute.rs +475 -0
  143. package/native/tvos/metal-patched/src/pipeline/mod.rs +71 -0
  144. package/native/tvos/metal-patched/src/pipeline/render.rs +762 -0
  145. package/native/tvos/metal-patched/src/renderpass.rs +443 -0
  146. package/native/tvos/metal-patched/src/resource.rs +182 -0
  147. package/native/tvos/metal-patched/src/sampler.rs +165 -0
  148. package/native/tvos/metal-patched/src/sync.rs +178 -0
  149. package/native/tvos/metal-patched/src/texture.rs +352 -0
  150. package/native/tvos/metal-patched/src/types.rs +90 -0
  151. package/native/tvos/metal-patched/src/vertexdescriptor.rs +250 -0
  152. package/native/tvos/src/audio_backend.rs +197 -0
  153. package/native/tvos/src/lib.rs +1891 -0
  154. package/native/visionos/Cargo.lock +1693 -0
  155. package/native/visionos/Cargo.toml +40 -0
  156. package/native/visionos/src/audio_backend.rs +197 -0
  157. package/native/visionos/src/lib.rs +1887 -0
  158. package/native/watchos/Cargo.lock +16 -0
  159. package/native/watchos/Cargo.toml +19 -0
  160. package/native/watchos/shaders/bloom_postfx.metal +99 -0
  161. package/native/watchos/src/BloomWatchApp.swift +1267 -0
  162. package/native/watchos/src/BloomWatchAudio.swift +179 -0
  163. package/native/watchos/src/audio.rs +55 -0
  164. package/native/watchos/src/draw_list.rs +229 -0
  165. package/native/watchos/src/ffi_stubs.rs +915 -0
  166. package/native/watchos/src/ffi_stubs_manual.rs +35 -0
  167. package/native/watchos/src/lib.rs +1124 -0
  168. package/native/watchos/src/models.rs +746 -0
  169. package/native/watchos/src/postfx.rs +95 -0
  170. package/native/watchos/src/scene.rs +534 -0
  171. package/native/watchos/src/textures.rs +184 -0
  172. package/native/web/Cargo.lock +1657 -0
  173. package/native/web/Cargo.toml +43 -0
  174. package/native/web/bloom_glue.js +695 -0
  175. package/native/web/build.sh +131 -0
  176. package/native/web/index.html +35 -0
  177. package/native/web/jolt_bridge.js +1519 -0
  178. package/native/web/src/input_ffi.rs +286 -0
  179. package/native/web/src/lib.rs +1796 -0
  180. package/native/web/src/material_ffi.rs +710 -0
  181. package/native/web/src/parity_ffi.rs +343 -0
  182. package/native/web/src/physics_ffi.rs +643 -0
  183. package/native/web/src/ragdoll_ffi.rs +250 -0
  184. package/native/web/src/render_settings.rs +98 -0
  185. package/native/windows/Cargo.lock +1815 -0
  186. package/native/windows/Cargo.toml +68 -0
  187. package/native/windows/src/lib.rs +1486 -0
  188. package/package.json +4279 -0
  189. package/src/audio/index.ts +315 -0
  190. package/src/core/colors.ts +63 -0
  191. package/src/core/index.ts +1206 -0
  192. package/src/core/keys.ts +63 -0
  193. package/src/core/types.ts +104 -0
  194. package/src/index.ts +171 -0
  195. package/src/math/index.ts +516 -0
  196. package/src/mobile/index.ts +294 -0
  197. package/src/models/index.ts +1258 -0
  198. package/src/physics/index.ts +1134 -0
  199. package/src/scene/index.ts +698 -0
  200. package/src/shapes/index.ts +120 -0
  201. package/src/text/index.ts +48 -0
  202. package/src/textures/index.ts +187 -0
  203. package/src/vfx/index.ts +191 -0
  204. package/src/world/index.ts +24 -0
  205. package/src/world/loader.ts +423 -0
  206. package/src/world/prefab.ts +217 -0
  207. package/src/world/render.ts +172 -0
  208. package/src/world/saver.ts +108 -0
  209. package/src/world/serialize.ts +301 -0
  210. package/src/world/terrain.ts +355 -0
  211. package/src/world/types.ts +160 -0
  212. package/src/world/validate.ts +319 -0
  213. package/src/world/version.ts +114 -0
@@ -0,0 +1,343 @@
1
+ //! EN-063 — web FFI parity for full-3D games (driven by the shooter port).
2
+ //!
3
+ //! Every function here mirrors an existing native implementation in
4
+ //! `bloom-shared/src/ffi_core/`; the shared engine methods they call compile
5
+ //! unchanged on wasm32. The groups:
6
+ //!
7
+ //! - Mesh / instance-buffer / texture-array **scratch** entry points. The
8
+ //! TS wrappers never pass array pointers (Perry 0.5.x rejects `number[]`
9
+ //! into i64 params); they push scalars through the mesh scratch and then
10
+ //! call the `_scratch` builder. Without these exports every generated
11
+ //! mesh (water ribbon, grass blades, splat map) silently came back 0 on
12
+ //! web while the same game worked native.
13
+ //! - `bloom_stage_model_bytes` — the synchronous web half of the staged
14
+ //! model loader. JS glue fetches the GLB and calls this; `stageModels`'
15
+ //! worker-thread decode does not exist on wasm, but the contract (stage
16
+ //! returns a ticket, commit returns a model) is preserved.
17
+ //! - Env-HDR from bytes, splat impulses, scene GI-only flags, profiler
18
+ //! text — small native features 3D games actually call.
19
+ //! - Host stubs (`present_mode`, `take_screenshot`) that must exist so the
20
+ //! calls are cheap no-ops rather than auto-stubbed `undefined` returns.
21
+
22
+ use crate::engine;
23
+ use std::cell::RefCell;
24
+ use wasm_bindgen::prelude::*;
25
+
26
+ // ============================================================
27
+ // Mesh scratch (mirrors ffi_core/models.rs)
28
+ // ============================================================
29
+
30
+ #[wasm_bindgen]
31
+ pub fn bloom_mesh_scratch_reset() {
32
+ engine().models.mesh_scratch_reset();
33
+ }
34
+
35
+ #[wasm_bindgen]
36
+ pub fn bloom_mesh_scratch_push_f32(v: f64) {
37
+ engine().models.mesh_scratch_push_f32(v as f32);
38
+ }
39
+
40
+ #[wasm_bindgen]
41
+ pub fn bloom_mesh_scratch_push_u32(v: f64) {
42
+ engine().models.mesh_scratch_push_u32(v as u32);
43
+ }
44
+
45
+ #[wasm_bindgen]
46
+ pub fn bloom_create_mesh_scratch(vertex_count: f64, index_count: f64) -> f64 {
47
+ engine()
48
+ .models
49
+ .create_mesh_from_scratch(vertex_count as u32, index_count as u32)
50
+ }
51
+
52
+ #[wasm_bindgen]
53
+ pub fn bloom_gen_mesh_spline_ribbon_scratch(point_count: f64, width_count: f64) -> f64 {
54
+ let n = point_count as usize;
55
+ let wn = width_count as usize;
56
+ let (points, widths) = {
57
+ let scratch = engine().models.scratch_floats();
58
+ if n < 2 || wn == 0 || scratch.len() < n * 3 + wn {
59
+ return 0.0;
60
+ }
61
+ (
62
+ scratch[..n * 3].to_vec(),
63
+ scratch[n * 3..n * 3 + wn].to_vec(),
64
+ )
65
+ };
66
+ engine().models.gen_mesh_spline_ribbon(&points, &widths)
67
+ }
68
+
69
+ #[wasm_bindgen]
70
+ pub fn bloom_create_instance_buffer_scratch(instance_count: f64) -> f64 {
71
+ let eng = engine();
72
+ let count = instance_count as u32;
73
+ let need = (count as usize) * 9;
74
+ if count == 0 || eng.models.scratch_f32.len() < need {
75
+ return 0.0;
76
+ }
77
+ let data: Vec<f32> = eng.models.scratch_f32[..need].to_vec();
78
+ eng.models.mesh_scratch_reset();
79
+ eng.renderer.create_instance_buffer(&data, count) as f64
80
+ }
81
+
82
+ /// Params arrive via the mesh scratch (reset + push_f32 × N) — the idiom the
83
+ /// TS `setMaterialParams` wrapper uses everywhere (Perry 0.5.x rejects JS
84
+ /// arrays in pointer params). Forwards to the existing floats path.
85
+ #[wasm_bindgen]
86
+ pub fn bloom_set_material_params_scratch(handle: f64, param_count: f64) {
87
+ let count = param_count as usize;
88
+ let params: Vec<f32> = {
89
+ let eng = engine();
90
+ if eng.models.scratch_f32.len() < count {
91
+ return;
92
+ }
93
+ let p = eng.models.scratch_f32[..count].to_vec();
94
+ eng.models.mesh_scratch_reset();
95
+ p
96
+ };
97
+ crate::material_ffi::bloom_set_material_params_floats(handle, &params);
98
+ }
99
+
100
+ // ============================================================
101
+ // Texture arrays (mirrors ffi_core/game_loop.rs)
102
+ // ============================================================
103
+
104
+ #[wasm_bindgen]
105
+ pub fn bloom_create_texture_array_scratch(
106
+ width: f64,
107
+ height: f64,
108
+ layer_count: f64,
109
+ format: f64,
110
+ mip_levels: f64,
111
+ ) -> f64 {
112
+ let w = width as u32;
113
+ let h = height as u32;
114
+ if w == 0 || h == 0 {
115
+ return 0.0;
116
+ }
117
+ let layers_count = (layer_count as u32)
118
+ .min(bloom_shared::renderer::material_system::MAX_TEXTURE_ARRAY_LAYERS);
119
+ if layers_count == 0 {
120
+ return 0.0;
121
+ }
122
+
123
+ let texels = (w as usize) * (h as usize) * (layers_count as usize);
124
+ // Scoped so the scratch borrow ends before the renderer borrow.
125
+ let bytes: Vec<u8> = {
126
+ let eng = engine();
127
+ if eng.models.scratch_u32.len() < texels {
128
+ // Short buffer: refuse rather than upload uninitialised memory.
129
+ return 0.0;
130
+ }
131
+ eng.models.scratch_u32[..texels]
132
+ .iter()
133
+ .flat_map(|p| p.to_le_bytes())
134
+ .collect()
135
+ };
136
+ let layer_size = (w as usize) * (h as usize) * 4;
137
+ let mut layers: Vec<(&[u8], u32, u32)> = Vec::with_capacity(layers_count as usize);
138
+ for i in 0..(layers_count as usize) {
139
+ let start = i * layer_size;
140
+ let end = start + layer_size;
141
+ if end > bytes.len() {
142
+ break;
143
+ }
144
+ layers.push((&bytes[start..end], w, h));
145
+ }
146
+ engine()
147
+ .renderer
148
+ .create_texture_array_ex(&layers, format as u32, mip_levels as u32) as f64
149
+ }
150
+
151
+ // The from-files path can't read files on wasm, so it is split: JS glue
152
+ // fetches each path in the comma-separated list and pushes the raw file
153
+ // bytes here (decoded engine-side, same as native's `image::open`), then
154
+ // commits. Layers must share dimensions; first file's size wins.
155
+ thread_local! {
156
+ static TEXARRAY_FILES: RefCell<Vec<(Vec<u8>, u32, u32)>> = const { RefCell::new(Vec::new()) };
157
+ }
158
+
159
+ #[wasm_bindgen]
160
+ pub fn bloom_texture_array_files_reset() {
161
+ TEXARRAY_FILES.with(|f| f.borrow_mut().clear());
162
+ }
163
+
164
+ #[wasm_bindgen]
165
+ pub fn bloom_texture_array_files_push(data: &[u8]) -> f64 {
166
+ match bloom_shared::textures::TextureManager::decode_rgba8(data) {
167
+ Some((bytes, w, h)) => {
168
+ TEXARRAY_FILES.with(|f| f.borrow_mut().push((bytes, w, h)));
169
+ 1.0
170
+ }
171
+ None => 0.0,
172
+ }
173
+ }
174
+
175
+ #[wasm_bindgen]
176
+ pub fn bloom_texture_array_files_commit(format: f64, mip_levels: f64) -> f64 {
177
+ let decoded = TEXARRAY_FILES.with(|f| f.borrow_mut().split_off(0));
178
+ if decoded.is_empty() {
179
+ return 0.0;
180
+ }
181
+ let (w, h) = (decoded[0].1, decoded[0].2);
182
+ let mut layers: Vec<(&[u8], u32, u32)> = Vec::with_capacity(decoded.len());
183
+ for (bytes, lw, lh) in decoded.iter() {
184
+ if *lw != w || *lh != h {
185
+ crate::console_warn(&format!(
186
+ "[texarray] layer size {}x{} != {}x{}; skipped",
187
+ lw, lh, w, h
188
+ ));
189
+ continue;
190
+ }
191
+ layers.push((bytes.as_slice(), w, h));
192
+ }
193
+ if layers.is_empty() {
194
+ return 0.0;
195
+ }
196
+ engine()
197
+ .renderer
198
+ .create_texture_array_ex(&layers, format as u32, mip_levels as u32) as f64
199
+ }
200
+
201
+ // ============================================================
202
+ // Staged model loading (bytes half; JS glue fetches the file)
203
+ // ============================================================
204
+
205
+ #[wasm_bindgen]
206
+ pub fn bloom_stage_model_bytes(data: &[u8]) -> f64 {
207
+ match bloom_shared::models::load_gltf_staged(data) {
208
+ Some(staged) => bloom_shared::staging::stage_model(staged),
209
+ None => 0.0,
210
+ }
211
+ }
212
+
213
+ // ============================================================
214
+ // Environment / water / scene odds and ends
215
+ // ============================================================
216
+
217
+ #[cfg(feature = "image-extras")]
218
+ #[wasm_bindgen]
219
+ pub fn bloom_set_env_clear_from_hdr_bytes(data: &[u8]) {
220
+ engine().renderer.set_env_clear_from_hdr_bytes(data);
221
+ }
222
+
223
+ #[wasm_bindgen]
224
+ pub fn bloom_splat_impulse(x: f64, z: f64, radius: f64, strength: f64) {
225
+ engine().renderer.impulse_field.submit_splat(
226
+ x as f32,
227
+ z as f32,
228
+ radius as f32,
229
+ strength as f32,
230
+ );
231
+ }
232
+
233
+ #[wasm_bindgen]
234
+ pub fn bloom_scene_set_gi_only(handle: f64, gi_only: f64) {
235
+ engine().scene.set_gi_only(handle, gi_only != 0.0);
236
+ }
237
+
238
+ // ============================================================
239
+ // Profiler text (mirrors ffi_core/game_loop.rs; GPU column is 0 on
240
+ // web — no TIMESTAMP_QUERY in the WebGPU spec at wgpu 29)
241
+ // ============================================================
242
+
243
+ #[wasm_bindgen]
244
+ pub fn bloom_profiler_overlay_text() -> String {
245
+ let snap = engine().profiler.snapshot();
246
+ let mut s = String::with_capacity(snap.len() * 48);
247
+ for (label, cpu, gpu) in &snap {
248
+ s.push_str(label);
249
+ s.push('|');
250
+ s.push_str(&format!("{:.2}", cpu));
251
+ s.push('|');
252
+ match gpu {
253
+ Some(g) => s.push_str(&format!("{:.2}", g)),
254
+ None => s.push_str("-1"),
255
+ }
256
+ s.push('\n');
257
+ }
258
+ s
259
+ }
260
+
261
+ #[wasm_bindgen]
262
+ pub fn bloom_profiler_frame_history() -> String {
263
+ let hist = engine().profiler.frame_history();
264
+ let mut s = String::with_capacity(hist.len() * 24);
265
+ for (cpu, gpu) in &hist {
266
+ s.push_str(&format!("{:.2}|{:.2}\n", cpu, gpu));
267
+ }
268
+ s
269
+ }
270
+
271
+ #[wasm_bindgen]
272
+ pub fn bloom_profiler_row_count() -> f64 {
273
+ engine().profiler.snapshot().len() as f64
274
+ }
275
+
276
+ #[wasm_bindgen]
277
+ pub fn bloom_profiler_row_label(i: f64) -> String {
278
+ engine()
279
+ .profiler
280
+ .snapshot()
281
+ .get(i as usize)
282
+ .map(|(label, _, _)| label.to_string())
283
+ .unwrap_or_default()
284
+ }
285
+
286
+ #[wasm_bindgen]
287
+ pub fn bloom_profiler_row_cpu_us(i: f64) -> f64 {
288
+ engine()
289
+ .profiler
290
+ .snapshot()
291
+ .get(i as usize)
292
+ .map(|r| r.1)
293
+ .unwrap_or(0.0)
294
+ }
295
+
296
+ #[wasm_bindgen]
297
+ pub fn bloom_profiler_row_gpu_us(i: f64) -> f64 {
298
+ match engine().profiler.snapshot().get(i as usize) {
299
+ Some((_, _, Some(g))) => *g,
300
+ _ => -1.0,
301
+ }
302
+ }
303
+
304
+ #[wasm_bindgen]
305
+ pub fn bloom_profiler_hist_count() -> f64 {
306
+ engine().profiler.frame_history().len() as f64
307
+ }
308
+
309
+ #[wasm_bindgen]
310
+ pub fn bloom_profiler_hist_cpu_us(i: f64) -> f64 {
311
+ engine()
312
+ .profiler
313
+ .frame_history()
314
+ .get(i as usize)
315
+ .map(|(cpu, _)| *cpu)
316
+ .unwrap_or(0.0)
317
+ }
318
+
319
+ #[wasm_bindgen]
320
+ pub fn bloom_profiler_hist_gpu_us(i: f64) -> f64 {
321
+ engine()
322
+ .profiler
323
+ .frame_history()
324
+ .get(i as usize)
325
+ .map(|(_, gpu)| *gpu)
326
+ .unwrap_or(0.0)
327
+ }
328
+
329
+ // ============================================================
330
+ // Host stubs — features a browser page cannot provide, exported so
331
+ // the calls stay cheap typed no-ops
332
+ // ============================================================
333
+
334
+ /// The browser owns presentation (rAF + compositor); Fifo-equivalent
335
+ /// behaviour is all a canvas surface can do, so the mode is fixed.
336
+ #[wasm_bindgen]
337
+ pub fn bloom_set_present_mode(_mode: f64) {}
338
+
339
+ /// Screenshot readback needs a blocking `device.poll(Wait)`, which a
340
+ /// single-threaded wasm host cannot do. Right-click-save or the DOM
341
+ /// `canvas.toBlob` path (from JS) are the web equivalents.
342
+ #[wasm_bindgen]
343
+ pub fn bloom_take_screenshot(_path: f64) {}