-
-
Notifications
You must be signed in to change notification settings - Fork 3
fix(linux): make Bloom actually run on Linux — device limits, screenshots, window close #146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5b156cd
8d6132d
fb79f52
8bacd1d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -92,6 +92,13 @@ mod x11_impl { | |
| static mut WARP_CENTER_X: i32 = 0; | ||
| static mut WARP_CENTER_Y: i32 = 0; | ||
| static mut RELATIVE_MODE: bool = false; | ||
| /// WM_PROTOCOLS / WM_DELETE_WINDOW, interned once in `create_window`. | ||
| /// Cached because `poll_events` compares against them every event. | ||
| static mut WM_PROTOCOLS: x11::xlib::Atom = 0; | ||
| static mut WM_DELETE_WINDOW: x11::xlib::Atom = 0; | ||
|
|
||
| pub fn wm_protocols_atom() -> x11::xlib::Atom { unsafe { WM_PROTOCOLS } } | ||
| pub fn wm_delete_window_atom() -> x11::xlib::Atom { unsafe { WM_DELETE_WINDOW } } | ||
|
|
||
| pub fn set_fullscreen(fullscreen: bool) { | ||
| unsafe { | ||
|
|
@@ -184,6 +191,20 @@ mod x11_impl { | |
| x11::xlib::ButtonPressMask | x11::xlib::ButtonReleaseMask | | ||
| x11::xlib::PointerMotionMask | x11::xlib::StructureNotifyMask); | ||
|
|
||
| // Opt into the WM close protocol before mapping, so the titlebar | ||
| // ✕ / Alt-F4 arrives as a ClientMessage we can turn into | ||
| // `windowShouldClose()` instead of the WM dropping our X | ||
| // connection and Xlib exit(1)-ing the game mid-frame. | ||
| WM_PROTOCOLS = x11::xlib::XInternAtom( | ||
| DISPLAY, b"WM_PROTOCOLS\0".as_ptr() as *const _, 0); | ||
| WM_DELETE_WINDOW = x11::xlib::XInternAtom( | ||
| DISPLAY, b"WM_DELETE_WINDOW\0".as_ptr() as *const _, 0); | ||
| if WM_DELETE_WINDOW != 0 { | ||
| let mut protocols = [WM_DELETE_WINDOW]; | ||
| x11::xlib::XSetWMProtocols( | ||
| DISPLAY, X11_WINDOW, protocols.as_mut_ptr(), 1); | ||
| } | ||
|
|
||
| if !headless { | ||
| x11::xlib::XMapWindow(DISPLAY, X11_WINDOW); | ||
| } | ||
|
|
@@ -489,6 +510,23 @@ mod x11_impl { | |
| } | ||
| } | ||
| } | ||
| x11::xlib::ClientMessage => { | ||
| // The WM asking us to close (titlebar ✕, Alt-F4, or a | ||
| // session logout). Without WM_DELETE_WINDOW in our | ||
| // WM_PROTOCOLS the WM instead destroys the connection, | ||
| // and Xlib's default IO-error handler calls exit(1) | ||
| // from under the game — `windowShouldClose()` never | ||
| // goes true, so `closeWindow()` and every shutdown | ||
| // path after it (audio teardown, save-on-exit) are | ||
| // skipped. Observed as: | ||
| // XIO: fatal IO error 62 (Timer expired) | ||
| if event.client_message.message_type == wm_protocols_atom() | ||
| && event.client_message.data.get_long(0) | ||
| == wm_delete_window_atom() as i64 | ||
| { | ||
| engine().should_close = true; | ||
| } | ||
| } | ||
| x11::xlib::DestroyNotify => { | ||
| engine().should_close = true; | ||
| } | ||
|
|
@@ -572,6 +610,14 @@ pub extern "C" fn bloom_init_window(width: f64, height: f64, title_ptr: *const u | |
| if !force_sw_gi && supported.contains(rt_mask) { | ||
| required_features |= rt_mask; | ||
| } | ||
| // PT-2: texture binding array + non-uniform indexing for textured | ||
| // path-trace hit shading. Both or neither (the kernel indexes the | ||
| // array with a per-thread material id). | ||
| let pt_tex_mask = wgpu::Features::TEXTURE_BINDING_ARRAY | ||
| | wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING; | ||
| if supported.contains(pt_tex_mask) { | ||
| required_features |= pt_tex_mask; | ||
| } | ||
| let experimental_features = if required_features.intersects(rt_mask) { | ||
| unsafe { wgpu::ExperimentalFeatures::enabled() } | ||
| } else { | ||
|
|
@@ -582,6 +628,34 @@ pub extern "C" fn bloom_init_window(width: f64, height: f64, title_ptr: *const u | |
| // PerMaterial, PerDraw, SceneInputs). wgpu's default limit is | ||
| // 4. Vulkan supports at least 7 here, so 5 is universally safe. | ||
| required_limits.max_bind_groups = 5; | ||
| // The refractive/translucent material profile binds up to 19 | ||
| // sampled textures in the fragment stage (5 material maps + env/ | ||
| // BRDF/3 shadow cascades/env-diffuse + planar reflection + 3 texture | ||
| // arrays + the group-4 scene_color/scene_depth/impulse/motion inputs). | ||
| // wgpu's default is 16. Raise to whatever the adapter actually | ||
| // supports — every real Vulkan/GL GPU exposes ≥128 — so opaque/ | ||
| // transparent materials are unaffected and refractive ones link. | ||
| let adapter_limits = adapter.limits(); | ||
| required_limits.max_sampled_textures_per_shader_stage = required_limits | ||
| .max_sampled_textures_per_shader_stage | ||
| .max(adapter_limits.max_sampled_textures_per_shader_stage); | ||
| required_limits.max_samplers_per_shader_stage = required_limits | ||
| .max_samplers_per_shader_stage | ||
| .max(adapter_limits.max_samplers_per_shader_stage); | ||
| // PT-2: binding arrays have their own element budget, default 0. | ||
| // Take whatever the adapter offers; the renderer checks the | ||
| // granted value against its fixed array size before compiling | ||
| // the textured kernel variant. | ||
| if required_features.contains(pt_tex_mask) { | ||
| required_limits.max_binding_array_elements_per_shader_stage = | ||
| adapter_limits.max_binding_array_elements_per_shader_stage; | ||
| } | ||
| // PT-4: the path-trace kernel binds 9 storage buffers (accum + | ||
| // moments + reservoir ping-pongs on top of instance/geo data); | ||
| // the wgpu default limit is 8. | ||
| required_limits.max_storage_buffers_per_shader_stage = required_limits | ||
| .max_storage_buffers_per_shader_stage | ||
| .max(adapter_limits.max_storage_buffers_per_shader_stage.min(16)); | ||
| if required_features.intersects(rt_mask) { | ||
| required_limits = required_limits | ||
| .using_minimum_supported_acceleration_structure_values(); | ||
|
|
@@ -603,7 +677,11 @@ pub extern "C" fn bloom_init_window(width: f64, height: f64, title_ptr: *const u | |
| // logical size separately so screenWidth() etc. stay | ||
| // DPI-independent. | ||
| let surface_config = wgpu::SurfaceConfiguration { | ||
| usage: wgpu::TextureUsages::RENDER_ATTACHMENT, | ||
| // COPY_SRC: bloom_take_screenshot reads the swapchain back; | ||
| // without it the readback copy is a validation error that | ||
| // aborts the process the first time a game calls it. | ||
| usage: wgpu::TextureUsages::RENDER_ATTACHMENT | ||
| | wgpu::TextureUsages::COPY_SRC, | ||
|
Comment on lines
+680
to
+684
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | sed -n '1,120p'
echo "== target file size/outline =="
wc -l native/linux/src/lib.rs || true
ast-grep outline native/linux/src/lib.rs --view compact 2>/dev/null | sed -n '1,220p' || true
echo "== relevant lines =="
sed -n '620,730p' native/linux/src/lib.rs | cat -nRepository: Bloom-Engine/engine Length of output: 10276 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== screenshot and screenshot-related locations =="
rg -n "bloom_take_screenshot|bloom_register_geisterhand_screenshot|COPY_SRC|swapchain|swap_chain|SurfaceCapabilities|surface_caps|configure" native/linux/src/lib.rs native/linux -g '*.rs' || true
echo "== renderer surface usage/configuration context =="
rg -n "SurfaceConfiguration|RENDER_ATTACHMENT|COPY_SRC|bloom_take_screenshot|screen reader|readback|present" --glob '*.rs' .
echo "== likely screenshots section =="
sed -n '1140,1284p' native/linux/src/lib.rs | cat -nRepository: Bloom-Engine/engine Length of output: 37222 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== windows surface config around COPY_SRC =="
sed -n '790,835p' native/windows/src/lib.rs | cat -n
echo "== web copy-src handling =="
sed -n '160,195p' native/web/src/lib.rs | cat -n
echo "== shared attach =="
sed -n '220,255p' native/shared/src/attach.rs | cat -n
echo "== shared renderer surface config =="
sed -n '1989,2032p' native/shared/src/renderer/mod.rs | cat -n
echo "== screenshot implementation in shared renderer =="
sed -n '11160,11245p' native/shared/src/renderer/mod.rs | cat -n
echo "== wasm screenshot =="
sed -n '334,350p' native/web/src/parity_ffi.rs | cat -nRepository: Bloom-Engine/engine Length of output: 13276 🌐 Web query:
💡 Result: To use a surface texture as a source for copy operations (e.g., using COPY_SRC), you must ensure that your SurfaceConfiguration includes the appropriate TextureUsages when calling surface.configure. 1. Query Surface Capabilities: Before configuring the surface, retrieve the supported texture usages for your specific adapter by calling surface.get_capabilities(adapter) [1][2]. This returns a SurfaceCapabilities struct, which contains a usages field specifying which TextureUsages are supported by the surface [3][2]. Note that TextureUsages::RENDER_ATTACHMENT is the only usage guaranteed to be supported across all surfaces [3][4][5]. 2. Configure the Surface: When you call surface.configure, you must provide a SurfaceConfiguration [1][4]. The usage field in this struct defines the allowed operations for textures retrieved from the surface via get_current_texture [4][5]. To perform a copy-src operation (such as copy_texture_to_buffer), the SurfaceConfiguration's usage must include TextureUsages::COPY_SRC [6][7]. 3. Important Considerations: - Compatibility: Always check the usages returned by get_capabilities before configuring [2]. If you request a usage not supported by the underlying surface/adapter, the configuration may fail or cause runtime issues [1][8]. - Performance and Driver Behavior: Different backends (Vulkan, DX12, Metal) have varying levels of support for surface usages beyond RENDER_ATTACHMENT [8]. Always ensure the requested flags are subset of the capabilities returned by the surface [2]. - COPY_SRC Usage: TextureUsages::COPY_SRC specifically allows a texture to be used as the source in command encoder operations like copy_texture_to_buffer or copy_texture_to_texture [6][7]. Example flow: let caps = surface.get_capabilities(&adapter); let config = wgpu::SurfaceConfiguration { usage: caps.usages & wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::RENDER_ATTACHMENT, format: caps.formats, //... other fields }; surface.configure(&device, &config); Citations:
Check
🤖 Prompt for AI Agents |
||
| format, | ||
| width: phys_w, | ||
| height: phys_h, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: Bloom-Engine/engine
Length of output: 22087
🏁 Script executed:
Repository: Bloom-Engine/engine
Length of output: 15576
🌐 Web query:
Bloom Engine native linux renderer 19 sampled textures 9 storage buffers path-trace kernel💡 Result:
The Bloom Engine is a cross-platform graphics engine written in TypeScript that compiles to native binaries (macOS, Windows, Linux, iOS, tvOS) and WASM for the web [1][2]. Its renderer is built on the wgpu API [1][2], which abstracts graphics backends such as Vulkan, Metal, DirectX 12, and WebGPU [1]. Regarding your query about a "19 sampled textures 9 storage buffers path-trace kernel" configuration, there is no public documentation or technical evidence indicating that the Bloom Engine utilizes a specific hardcoded configuration of 19 sampled textures and 9 storage buffers for a path-tracing kernel. The engine's current development focus involves GPU-driven rendering techniques, including indirect multi-draw and GPU culling [3], as well as advanced lighting features like global illumination probes and variance-driven refinement [4][5]. While the engine explores compute-heavy tasks like global illumination feedback loops (WSRC) [4], it explicitly notes that some "exotic" features such as native hardware ray tracing are currently off the table due to the constraints of the wgpu abstraction layer [2]. Technical discussions within the Bloom Engine repository often reference compute shader dispatch and binding patterns [4][5][3], but do not align with the specific numbers mentioned in your query. It is possible that these numbers refer to a custom implementation or a specific project configuration not part of the core Bloom Engine codebase.
Citations:
Reject adapters below Bloom’s fixed resource budgets.
The Linux/Windows path-tracing setup documents hard requirements of 19 sampled textures and 9 storage buffers, but the device request currently downgrades to the adapter’s lower limits. Query the requested limits against these minima before
request_device; if an adapter cannot support them, fail initialization clearly or select a fallback renderer.🤖 Prompt for AI Agents