adapter.request_device asked for Limits::default(), which requests desktop-tier compute-shader limits unconditionally even though nothing in iris/iris-core creates a ComputePipeline or writes a @compute stage. That crashed device creation outright on a downlevel GL adapter reporting OpenGL ES 3.0 (no compute at all) -- the Android emulator's EMU_GPU=software/force-gles path, and any real GLES-3.0-only device. New iris_core::device_limits(), shared by both platform backends, zeros exactly the six max_compute_* fields rather than switching to a downlevel Limits preset -- downlevel_webgl2_defaults() also zeros max_storage_buffers_per_shader_stage, which shader.wgsl's vertex stage needs. rigs/gpu-probe's own mirrored limits were updated to match. Not verified against the actual SwiftShader-ES-3.0 crash on-device this pass: the cold boot needed would have force-restarted this checkout's emulator while another session had its own app running on it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
149 lines
5.7 KiB
Rust
149 lines
5.7 KiB
Rust
//! Ask a device whether it can give iris the GPU it asks for.
|
|
//!
|
|
//! Until 2026-09-04 iris's renderer bound every texture it had drawn as one
|
|
//! binding array and indexed it non-uniformly from the shader, which needed
|
|
//! descriptor indexing and a very large per-stage binding-array limit
|
|
//! (101,000 elements: 100,000 textures and 1,000 samplers,
|
|
//! `UiLimits::default`). That was ordinary on a desktop and, per
|
|
//! TEXTURES.md's "iris's binding array does not survive real Android
|
|
//! hardware", not available on a real share of Android GPUs -- and it failed
|
|
//! outright on this emulator's software Vulkan, which is what this rig
|
|
//! caught first. iris now asks for nothing beyond wgpu's own defaults (see
|
|
//! `iris/src/default/render.rs`): the glyph atlas is one `texture_2d_array`
|
|
//! and a standalone image is its own ordinary bind group, and neither needs
|
|
//! descriptor indexing. This rig still asks `request_device` for exactly
|
|
//! what iris asks for, so it keeps being the answer to "does iris's actual
|
|
//! device request succeed here" rather than a guess from reading the code.
|
|
//!
|
|
//! It runs as a plain executable with no window and no APK, because
|
|
//! `request_adapter` needs no surface -- so it can be pushed to a device with
|
|
//! `adb push` and run from `/data/local/tmp`, which is far cheaper than an
|
|
//! app. What it therefore cannot answer is anything about presenting to a
|
|
//! surface; that is the Android backend's own problem.
|
|
|
|
mod vk;
|
|
|
|
use wgpu::*;
|
|
|
|
/// What `iris/src/default/render.rs` asks `request_device` for, now that the
|
|
/// binding array is gone: nothing beyond wgpu's own default feature set.
|
|
fn iris_features() -> Features {
|
|
Features::empty()
|
|
}
|
|
|
|
/// The one non-default limit iris asks for -- unrelated to the binding array,
|
|
/// kept for the big storage buffers behind rects/glyphs.
|
|
const IRIS_MAX_BUFFER_SIZE: u64 = 1 << 30;
|
|
|
|
/// Mirrors `iris_core::device_limits()` (`iris/core/src/render/mod.rs`) --
|
|
/// cannot call it directly, since this rig is deliberately its own crate,
|
|
/// not a workspace member (this file's own Cargo.toml comment). Keep the
|
|
/// two in sync by hand when one changes; this rig's whole purpose is "does
|
|
/// the device iris actually builds come back," so a stale copy here would
|
|
/// silently stop answering that question. Zeroed rather than left at
|
|
/// `Limits::default()`'s desktop-tier values because nothing in iris
|
|
/// creates a `ComputePipeline` or a `@compute` shader stage -- found by
|
|
/// grepping the whole `iris`/`iris-core` tree before this rig's comment was
|
|
/// written -- and the unconditional default request is what crashed
|
|
/// `request_device` on the Android emulator's software GL path
|
|
/// (`EMU_GPU=software`, `force-gles`: SwiftShader's GL reports itself as
|
|
/// OpenGL ES 3.0, which has no compute shaders at all, so the adapter's
|
|
/// real limit is 0). The same would happen on a real GLES-3.0-only Android
|
|
/// device.
|
|
fn iris_limits() -> Limits {
|
|
Limits {
|
|
max_buffer_size: IRIS_MAX_BUFFER_SIZE,
|
|
max_compute_workgroup_storage_size: 0,
|
|
max_compute_invocations_per_workgroup: 0,
|
|
max_compute_workgroup_size_x: 0,
|
|
max_compute_workgroup_size_y: 0,
|
|
max_compute_workgroup_size_z: 0,
|
|
max_compute_workgroups_per_dimension: 0,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
vk::report();
|
|
|
|
let instance = Instance::new(&InstanceDescriptor {
|
|
backends: Backends::from_env().unwrap_or(Backends::PRIMARY),
|
|
..Default::default()
|
|
});
|
|
|
|
let adapters = pollster::block_on(instance.enumerate_adapters(Backends::all()));
|
|
println!("adapters: {}", adapters.len());
|
|
for adapter in &adapters {
|
|
let info = adapter.get_info();
|
|
println!(
|
|
" {:?} {} ({:?})",
|
|
info.backend, info.name, info.device_type
|
|
);
|
|
}
|
|
|
|
let Some(adapter) = pollster::block_on(instance.request_adapter(&RequestAdapterOptions {
|
|
power_preference: PowerPreference::default(),
|
|
compatible_surface: None,
|
|
force_fallback_adapter: false,
|
|
}))
|
|
.ok() else {
|
|
println!("\nNO ADAPTER");
|
|
std::process::exit(1);
|
|
};
|
|
|
|
let info = adapter.get_info();
|
|
println!(
|
|
"\nchosen: {:?} {} ({:?})",
|
|
info.backend, info.name, info.device_type
|
|
);
|
|
println!("driver: {} {}", info.driver, info.driver_info);
|
|
|
|
let have = adapter.features();
|
|
println!("\nfeatures iris requires:");
|
|
let mut missing = Features::empty();
|
|
for f in iris_features().iter() {
|
|
let ok = have.contains(f);
|
|
println!(
|
|
" {:60} {}",
|
|
format!("{f:?}"),
|
|
if ok { "yes" } else { "NO" }
|
|
);
|
|
if !ok {
|
|
missing |= f;
|
|
}
|
|
}
|
|
|
|
let limits = adapter.limits();
|
|
println!("\nlimits iris requires:");
|
|
println!(
|
|
" {:52} want {:>7} have {:>7} {}",
|
|
"max_buffer_size",
|
|
IRIS_MAX_BUFFER_SIZE,
|
|
limits.max_buffer_size,
|
|
if limits.max_buffer_size >= IRIS_MAX_BUFFER_SIZE {
|
|
"ok"
|
|
} else {
|
|
"TOO SMALL"
|
|
}
|
|
);
|
|
|
|
// The question that actually matters: does the device iris builds come
|
|
// back, or does wgpu refuse it? With no features and no binding-array
|
|
// limits requested, this is expected to succeed everywhere -- this rig
|
|
// is what turned that from an assumption into a measurement, first on
|
|
// this emulator's software Vulkan.
|
|
let wanted = iris_limits();
|
|
match pollster::block_on(adapter.request_device(&DeviceDescriptor {
|
|
required_features: iris_features(),
|
|
required_limits: wanted,
|
|
..Default::default()
|
|
})) {
|
|
Ok(_) => println!("\nIRIS DEVICE: ok"),
|
|
Err(e) => println!("\nIRIS DEVICE: FAILED -- {e}"),
|
|
}
|
|
|
|
if !missing.is_empty() {
|
|
println!("\nmissing features: {missing:?}");
|
|
}
|
|
}
|