use std::collections::HashSet;
use std::sync::Arc;
use std::time::Instant;
use winit::application::ApplicationHandler;
use winit::event::{MouseButton, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::keyboard::{KeyCode, PhysicalKey};
use winit::window::Window;
use dreamwell_engine::physics::simulation::{BodyId, CollisionShape, PhysicsWorld, RigidBody};
use wgpu::util::DeviceExt;
mod safety;
mod scene;
mod scene_stress;
mod spawn_test;
use dreamwell_matter::benchmark::BenchmarkState;
use dreamwell_matter::config::BenchmarkConfig;
use dreamwell_matter::stats::BenchmarkStats;
const CAMERA_SPEED: f32 = 8.0;
const DEFAULT_CAM_POS: glam::Vec3 = glam::Vec3::new(0.0, 5.0, 18.0);
const DEFAULT_CAM_YAW: f32 = -std::f32::consts::FRAC_PI_2;
const DEFAULT_CAM_PITCH: f32 = -0.14;
const PROJECTILE_SPEED: f32 = 30.0;
const PROJECTILE_RADIUS: f32 = 0.15;
const EXPLOSION_COUNT: usize = 16;
const DEBRIS_LIFETIME: f32 = 2.0;
fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
let args: Vec<String> = std::env::args().collect();
let headless = args.iter().any(|a| a == "--headless");
let use_dream_pipeline = args.iter().any(|a| a == "--dream" || a == "--dvr");
let use_traditional = args.iter().any(|a| a == "--traditional" || a == "--trp");
let spawn_test_mode = args.iter().any(|a| a == "--spawn-test");
let micro_test_mode = args.iter().any(|a| a == "--micro-test");
let _avatar_demo_mode = args.iter().any(|a| a == "--avatar-demo");
let max_dreamlets: u32 = args.iter()
.position(|a| a == "--max")
.and_then(|i| args.get(i + 1))
.and_then(|s| s.parse().ok())
.unwrap_or(1_048_576);
let _use_dreamwell_unified = args.iter().any(|a| a == "--dreamwell");
let use_experimental = args.iter().any(|a| a == "--experimental");
let gallery_mode = args.iter().any(|a| a == "--ray-tracing-gallery" || a == "--rt-gallery");
let gallery_dir = args.iter()
.position(|a| a == "--gallery-dir")
.and_then(|i| args.get(i + 1))
.map(|s| std::path::PathBuf::from(s))
.unwrap_or_else(|| {
dreamwell_matter::scene_loader::resolve_project_dir(&args)
.unwrap_or_else(|| {
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent().unwrap().parent().unwrap()
.join("dreamwell-benchmark-project")
})
});
let use_rtx = args.iter().any(|a| a == "--ray-tracing" || a == "--rtx");
let quality_preset: Option<dreamwell_engine::material::DreamLightingQuality> = args.iter()
.position(|a| a == "--quality")
.and_then(|i| args.get(i + 1))
.and_then(|s| match s.to_lowercase().as_str() {
"low" => Some(dreamwell_engine::material::DreamLightingQuality::Low),
"medium" => Some(dreamwell_engine::material::DreamLightingQuality::Medium),
"high" => Some(dreamwell_engine::material::DreamLightingQuality::High),
"ultra" => Some(dreamwell_engine::material::DreamLightingQuality::Ultra),
"cinematic" => Some(dreamwell_engine::material::DreamLightingQuality::Cinematic),
_ => { log::warn!("Unknown quality preset '{}', using auto-detect", s); None }
});
let render_mode = if use_rtx {
dreamwell_gpu::scene::RenderPath::RayTraced
} else if use_dream_pipeline {
dreamwell_gpu::scene::RenderPath::Dreamwell
} else if use_traditional {
dreamwell_gpu::scene::RenderPath::Traditional
} else {
dreamwell_gpu::scene::RenderPath::Dreamwell
};
let stress_count: u32 = if std::env::args().any(|a| a == "--stress-1m") {
1_000_000
} else if std::env::args().any(|a| a == "--stress-100k") {
100_000
} else if std::env::args().any(|a| a == "--stress-10k") {
10_000
} else {
0
};
let mut config = BenchmarkConfig::default();
config.duration_secs = 90.0;
config.warmup_end = 8.0;
config.particle_end = 25.0;
config.convergence_end = 45.0;
config.settled_end = 60.0;
config.materialized_end = 78.0;
config.csv_path = "dreamwell_keynote_benchmark.csv".into();
for w in &config.validate() { log::warn!("Config: {}", w); }
log::info!("Pipeline: {:?}", render_mode);
if headless && !spawn_test_mode && !micro_test_mode {
run_headless(&config);
return;
}
if micro_test_mode && headless {
run_micro_test_headless(&config, render_mode, max_dreamlets, use_experimental, quality_preset);
return;
}
if spawn_test_mode && headless {
run_spawn_test_headless(&config, render_mode, max_dreamlets, quality_preset);
return;
}
if gallery_mode {
let event_loop = EventLoop::new().expect("event loop");
log::info!("RT Gallery: loading models from {}", gallery_dir.display());
let mut app = GalleryApp::new(gallery_dir, quality_preset);
event_loop.run_app(&mut app).expect("event loop error");
return;
}
log::info!(
"DreamMatter Keynote: {}x{}, {:.0}s, {} Dreamlets",
config.width, config.height, config.duration_secs,
scene::DEFAULT_DREAMLET_CAPACITY,
);
let event_loop = EventLoop::new().expect("event loop");
log::info!("Render mode: {:?}", render_mode);
let unified = !use_rtx && (_use_dreamwell_unified || use_experimental || (!use_dream_pipeline && !use_traditional));
let mut app = BenchmarkApp::new(config, stress_count, render_mode, unified);
app.quality_preset = quality_preset;
event_loop.run_app(&mut app).expect("event loop error");
}
fn run_spawn_test_headless(
config: &BenchmarkConfig,
render_mode: dreamwell_gpu::scene::RenderPath,
max_dreamlets: u32,
quality_preset: Option<dreamwell_engine::material::DreamLightingQuality>,
) {
let pipeline_name = match render_mode {
dreamwell_gpu::scene::RenderPath::Dreamwell => "Dreamwell (GPU-only)",
dreamwell_gpu::scene::RenderPath::Traditional => "TRP (Traditional)",
dreamwell_gpu::scene::RenderPath::RayTraced => "RTX (Ray-Traced GI + Shadows)",
};
log::info!("Dreamlet spawn test: {} pipeline, max {} dreamlets", pipeline_name, max_dreamlets);
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { backends: wgpu::Backends::all(), ..wgpu::InstanceDescriptor::new_without_display_handle() });
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
compatible_surface: None,
force_fallback_adapter: false,
})).expect("No GPU adapter");
let info = adapter.get_info();
let (features, limits, experimental) =
dreamwell_gpu::features::GpuCapabilities::device_descriptor_parts(&adapter);
let rt_available = adapter.features().contains(wgpu::Features::EXPERIMENTAL_RAY_QUERY);
if render_mode == dreamwell_gpu::scene::RenderPath::RayTraced && !rt_available {
log::error!("Ray tracing not supported on this adapter: {}", info.name);
log::error!(" Required: Vulkan ray query extension");
log::error!(" Minimum hardware: NVIDIA RTX 2000+ / AMD RX 6000+ / Intel Arc");
std::process::exit(1);
}
let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
label: Some("spawn_test"),
required_features: features,
required_limits: limits,
memory_hints: Default::default(),
experimental_features: experimental,
trace: Default::default(),
})).expect("device");
if rt_available {
log::info!("RT: Ray tracing hardware detected ({})", info.name);
}
let safety = safety::run_safety_checks(&adapter, &device, config);
safety.print();
if !safety.passed { std::process::exit(1); }
let format = wgpu::TextureFormat::Bgra8Unorm;
let mut fabric = dreamwell_fabric::DreamFabric::new(&device, format, false);
fabric.set_ssao_enabled(false);
fabric.set_taa_enabled(false);
fabric.set_ssr_enabled(false);
fabric.set_dof_enabled(false);
fabric.set_ssgi_enabled(false);
fabric.set_csm_enabled(false);
fabric.set_hiz_enabled(false);
fabric.set_motion_vectors_enabled(false);
fabric.set_volumetric_fog_enabled(false);
if render_mode == dreamwell_gpu::scene::RenderPath::RayTraced && rt_available {
fabric.enable_ray_tracing();
log::info!("RT: Enabled SHaRC radiance cache GI + ray-traced shadows");
}
if let Some(quality) = quality_preset {
fabric.set_dream_lighting_quality(quality);
log::info!("Dream Lighting: {:?} preset applied (spawn test)", quality);
}
fabric.resize(&device, config.width, config.height);
fabric.init_ibl(&device, &queue);
let limits = device.limits();
let max_buffer = limits.max_buffer_size;
let max_storage_binding = limits.max_storage_buffer_binding_size as u64;
let vram_budget = config.vram_budget_bytes;
let dreamlet_size = 144u64;
let max_by_storage = (max_storage_binding / dreamlet_size) as u32;
let safe_max = max_dreamlets.min(max_by_storage);
device.on_uncaptured_error(std::sync::Arc::new(|error| {
log::error!("GPU error (non-fatal): {error}");
}));
log::info!("VRAM: budget={} GB, max_buffer={} GB, max_storage_binding={} MB, max_dreamlets={}",
vram_budget / (1024 * 1024 * 1024),
max_buffer / (1024 * 1024 * 1024),
max_storage_binding / (1024 * 1024),
safe_max);
let mut catalog = dreamwell_gpu::dreamlet_catalog::DreamletCatalog::default();
catalog.init(&device, safe_max + 1); catalog.init_render_pipeline(&device, dreamwell_gpu::post::HDR_FORMAT);
let w = config.width as u64;
let h = config.height as u64;
let mut fixed_overhead = w * h * 8 + w * h * 4 + (w/2) * (h/2) * 8 + (w/4) * (h/4) * 8 + 512 * 512 * 8 + 64 * 64 * 6 * 8 + 128 * 128 * 6 * 8 + 4 * 32 * 4 + 64 * 32 + 32 * 64;
if render_mode == dreamwell_gpu::scene::RenderPath::RayTraced {
let hash_cap = 1_048_576u64;
let rt_overhead = hash_cap * 8 + hash_cap * 16 + hash_cap * 16 + w * h * 8 + w * h * 1 + 64 * 1024 * 1024; fixed_overhead += rt_overhead;
log::info!("RT VRAM overhead: {} MB", rt_overhead / (1024 * 1024));
}
let platform = spawn_test::generate_spawn_dreamlets(0);
catalog.upload(&queue, &platform);
let mut rt_ctx = dreamwell_gpu::ray_tracing::RayTracingContext::default();
if render_mode == dreamwell_gpu::scene::RenderPath::RayTraced && rt_available {
rt_ctx = dreamwell_gpu::ray_tracing::RayTracingContext::init_if_supported(&device, device.features());
if rt_ctx.enabled {
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("rt_blas_build"),
});
let mut total_verts = 0u32;
let mut total_indices = 0u32;
for shape in dreamwell_gpu::primitives::PrimitiveShape::ALL {
let mesh = shape.generate(dreamwell_gpu::primitives::DEFAULT_PRIMITIVE_COLOR);
let vertex_data: Vec<[f32; 3]> = mesh.vertices.iter().map(|v| v.position).collect();
let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(shape.name()),
contents: bytemuck::cast_slice(&vertex_data),
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT,
});
let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(shape.name()),
contents: bytemuck::cast_slice(&mesh.indices),
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::BLAS_INPUT,
});
rt_ctx.register_primitive_blas(
&device, &mut encoder, shape.name(),
&vertex_buf, vertex_data.len() as u32,
std::mem::size_of::<[f32; 3]>() as u64,
Some(&index_buf), Some(mesh.indices.len() as u32),
);
total_verts += vertex_data.len() as u32;
total_indices += mesh.indices.len() as u32;
}
queue.submit(std::iter::once(encoder.finish()));
log::info!("RT: {} BLAS registered ({} total verts, {} total indices)",
dreamwell_gpu::primitives::PrimitiveShape::ALL.len(), total_verts, total_indices);
if let Some(tlas) = rt_ctx.tlas() {
fabric.init_rt(&device, config.width, config.height, tlas);
}
}
}
let is_rt_active = render_mode == dreamwell_gpu::scene::RenderPath::RayTraced && rt_available;
let mut state = spawn_test::SpawnTestState::new(
pipeline_name, &info.name, safe_max,
max_buffer, vram_budget, fixed_overhead, is_rt_active,
);
let mut prev_count = 0u32;
let mut trp_staging: Vec<u8> = Vec::new();
let camera = dreamwell_gpu::camera::Camera::default();
let mut all_dreamlets: Vec<dreamwell_gpu::dreamlet_catalog::GpuDreamlet> = platform.clone();
let rt_depth_placeholder = if rt_ctx.enabled {
Some(device.create_texture(&wgpu::TextureDescriptor {
label: Some("rt_depth_placeholder"),
size: wgpu::Extent3d { width: config.width, height: config.height, depth_or_array_layers: 1 },
mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth32Float,
usage: wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
}))
} else { None };
let rt_hdr_placeholder = if rt_ctx.enabled {
Some(device.create_texture(&wgpu::TextureDescriptor {
label: Some("rt_hdr_placeholder"),
size: wgpu::Extent3d { width: config.width, height: config.height, depth_or_array_layers: 1 },
mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba16Float,
usage: wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
}))
} else { None };
log::info!("Starting dreamlet spawn test checkpoints...");
loop {
let to_spawn = state.tick();
if to_spawn > 0 {
let new_count = state.current_count;
if new_count + 1 > catalog.capacity {
log::warn!("DVR: {} dreamlets exceeds catalog capacity {} — stopping",
new_count + 1, catalog.capacity);
state.complete = true;
continue;
}
let batch = spawn_test::generate_dreamlet_batch(prev_count, new_count);
catalog.upload_batch(&queue, &batch);
all_dreamlets.extend_from_slice(&batch);
prev_count = new_count;
if rt_ctx.enabled && !all_dreamlets.is_empty() {
if all_dreamlets.len() > rt_ctx.max_instances as usize {
rt_ctx.resize_tlas(&device, (all_dreamlets.len() as u32).next_power_of_two());
if let Some(tlas) = rt_ctx.tlas() {
fabric.init_rt(&device, config.width, config.height, tlas);
}
}
rt_ctx.sync_tlas_from_dreamlets_mapped(&all_dreamlets, None);
let mut build_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("rt_tlas_rebuild"),
});
rt_ctx.rebuild_tlas(&mut build_encoder);
queue.submit(std::iter::once(build_encoder.finish()));
}
}
if state.measuring {
let frame_ok = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("spawn_test_frame"),
});
match render_mode {
dreamwell_gpu::scene::RenderPath::Dreamwell
| dreamwell_gpu::scene::RenderPath::RayTraced => {
catalog.dispatch_cull(
&device, &queue, &mut encoder, &camera,
config.width as f32, config.height as f32,
200.0, 1.0,
);
catalog.dispatch_physics(&device, &queue, &mut encoder, 0.016);
}
dreamwell_gpu::scene::RenderPath::Traditional => {
let uniform_size = 256usize;
let obj_count = state.current_count as usize;
let staging_size = obj_count * uniform_size;
if staging_size > 0 {
if trp_staging.len() < staging_size {
trp_staging.resize(staging_size, 0);
}
if let Some(ref ring) = catalog.dreamlet_buffer {
let write_size = staging_size.min(ring.size() as usize);
queue.write_buffer(ring, 0, &trp_staging[..write_size]);
}
}
}
}
if rt_ctx.enabled {
if let (Some(tlas), Some(ref depth_tex), Some(ref hdr_tex)) =
(rt_ctx.tlas(), &rt_depth_placeholder, &rt_hdr_placeholder)
{
let depth_v = depth_tex.create_view(&wgpu::TextureViewDescriptor::default());
let hdr_v = hdr_tex.create_view(&wgpu::TextureViewDescriptor::default());
fabric.dispatch_rt(
&device, &queue, &mut encoder, tlas,
&depth_v, &hdr_v, &camera,
[0.4, 0.8, 0.3], 1.0,
);
}
}
let cmds = encoder.finish();
queue.submit(std::iter::once(cmds));
let _ = device.poll(wgpu::PollType::Wait {
submission_index: None, timeout: None,
});
}));
if frame_ok.is_err() {
log::error!("GPU error at {} dreamlets — stopping safely", state.current_count);
state.complete = true;
}
}
if state.complete {
break;
}
}
state.results.print();
let csv_name = format!("dreamwell_spawn_test_{}.csv",
pipeline_name.split_whitespace().next().unwrap_or("unknown").to_lowercase());
match state.results.export_csv(&csv_name) {
Ok(()) => log::info!("CSV exported: {}", csv_name),
Err(e) => log::warn!("CSV export failed: {e}"),
}
}
fn run_micro_test_headless(
config: &BenchmarkConfig,
render_mode: dreamwell_gpu::scene::RenderPath,
max_dreamlets: u32,
experimental: bool,
quality_preset: Option<dreamwell_engine::material::DreamLightingQuality>,
) {
let pipeline_name = if experimental {
"Experimental (micro + coarse cull)"
} else { match render_mode {
dreamwell_gpu::scene::RenderPath::Dreamwell
| dreamwell_gpu::scene::RenderPath::RayTraced => "Dreamwell (micro GPU-only)",
dreamwell_gpu::scene::RenderPath::Traditional => "TRP (micro Traditional)",
}};
log::info!("MicroDreamlet test: {} pipeline, max {} units", pipeline_name, max_dreamlets);
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { backends: wgpu::Backends::all(), ..wgpu::InstanceDescriptor::new_without_display_handle() });
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
compatible_surface: None,
force_fallback_adapter: false,
})).expect("No GPU adapter");
let info = adapter.get_info();
let (dev_features, dev_limits, dev_experimental) =
dreamwell_gpu::features::GpuCapabilities::device_descriptor_parts(&adapter);
let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
label: Some("micro_test"),
required_features: dev_features,
required_limits: dev_limits,
memory_hints: Default::default(),
experimental_features: dev_experimental,
trace: Default::default(),
})).expect("device");
let safety = safety::run_safety_checks(&adapter, &device, config);
safety.print();
if !safety.passed { std::process::exit(1); }
let format = wgpu::TextureFormat::Bgra8Unorm;
let mut fabric = dreamwell_fabric::DreamFabric::new(&device, format, false);
fabric.set_ssao_enabled(false);
fabric.set_taa_enabled(false);
fabric.set_ssr_enabled(false);
fabric.set_dof_enabled(false);
fabric.set_ssgi_enabled(false);
fabric.set_csm_enabled(false);
fabric.set_hiz_enabled(false);
fabric.set_motion_vectors_enabled(false);
fabric.set_volumetric_fog_enabled(false);
if let Some(quality) = quality_preset {
fabric.set_dream_lighting_quality(quality);
log::info!("Dream Lighting: {:?} preset applied (micro test)", quality);
}
fabric.resize(&device, config.width, config.height);
fabric.init_ibl(&device, &queue);
let limits = device.limits();
let max_buffer = limits.max_buffer_size;
let max_storage_binding = limits.max_storage_buffer_binding_size as u64;
let vram_budget = config.vram_budget_bytes;
let micro_size = 64u64;
let max_by_storage = (max_storage_binding / micro_size) as u32;
let safe_max = max_dreamlets.min(max_by_storage);
device.on_uncaptured_error(std::sync::Arc::new(|error| {
log::error!("GPU error (non-fatal): {error}");
}));
log::info!("VRAM: budget={} GB, max_storage_binding={} MB, max_micro_dreamlets={}",
vram_budget / (1024 * 1024 * 1024),
max_storage_binding / (1024 * 1024),
safe_max);
let mut catalog = dreamwell_gpu::dreamlet_catalog::DreamletCatalog::default();
catalog.init(&device, safe_max + 1);
catalog.init_render_pipeline(&device, dreamwell_gpu::post::HDR_FORMAT);
catalog.init_micro_pipelines(&device, dreamwell_gpu::post::HDR_FORMAT);
let w = config.width as u64;
let h = config.height as u64;
let fixed_overhead = w * h * 8 + w * h * 4
+ (w/2) * (h/2) * 8 + (w/4) * (h/4) * 8
+ 512 * 512 * 8 + 64 * 64 * 6 * 8 + 128 * 128 * 6 * 8
+ 4 * 32 * 4 + 64 * 32 + 32 * 64;
let mut coarse_grid: Option<dreamwell_gpu::micro_dreamlet::CoarseCullGrid> = None;
let mut state = spawn_test::SpawnTestState::with_unit_size(
pipeline_name, &info.name, safe_max,
max_buffer, vram_budget, fixed_overhead, 84, false,
);
let mut prev_count = 0u32;
let mut trp_staging: Vec<u8> = Vec::new();
let camera = dreamwell_gpu::camera::Camera::default();
log::info!("Starting micro-dreamlet test checkpoints...");
loop {
let to_spawn = state.tick();
if to_spawn > 0 {
let new_count = state.current_count;
if new_count + 1 > catalog.capacity {
log::warn!("Micro: {} units exceeds capacity {} — stopping",
new_count + 1, catalog.capacity);
state.complete = true;
continue;
}
let batch = spawn_test::generate_micro_batch(prev_count, new_count);
if experimental {
let positions: Vec<[f32; 3]> = batch.iter().map(|d| d.position).collect();
let radii: Vec<f32> = batch.iter().map(|d| d.bounding_radius).collect();
coarse_grid = Some(dreamwell_gpu::micro_dreamlet::CoarseCullGrid::build(
&positions, &radii, 10.0,
));
}
catalog.upload_micro_batch(&queue, &batch);
prev_count = new_count;
}
if state.measuring {
let frame_ok = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("micro_test_frame"),
});
match render_mode {
dreamwell_gpu::scene::RenderPath::Dreamwell
| dreamwell_gpu::scene::RenderPath::RayTraced => {
if experimental {
let vp = (camera.projection_matrix * camera.view_matrix).to_cols_array();
if let Some(ref grid) = coarse_grid {
let _ = grid.cull_regions(&vp);
}
}
catalog.dispatch_micro_cull(
&device, &queue, &mut encoder, &camera,
config.width as f32, config.height as f32,
);
}
dreamwell_gpu::scene::RenderPath::Traditional => {
let uniform_size = 256usize;
let obj_count = state.current_count as usize;
let staging_size = obj_count * uniform_size;
if staging_size > 0 {
if trp_staging.len() < staging_size {
trp_staging.resize(staging_size, 0);
}
if let Some(ref ring) = catalog.dreamlet_buffer {
let write_size = staging_size.min(ring.size() as usize);
queue.write_buffer(ring, 0, &trp_staging[..write_size]);
}
}
}
}
let cmds = encoder.finish();
queue.submit(std::iter::once(cmds));
let _ = device.poll(wgpu::PollType::Wait {
submission_index: None, timeout: None,
});
}));
if frame_ok.is_err() {
log::error!("GPU error at {} micro-dreamlets — stopping safely", state.current_count);
state.complete = true;
}
}
if state.complete {
break;
}
}
state.results.print();
let csv_name = format!("dreamwell_micro_test_{}.csv",
pipeline_name.split_whitespace().next().unwrap_or("unknown").to_lowercase());
match state.results.export_csv(&csv_name) {
Ok(()) => log::info!("CSV exported: {}", csv_name),
Err(e) => log::warn!("CSV export failed: {e}"),
}
}
fn run_headless(config: &BenchmarkConfig) {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { backends: wgpu::Backends::all(), ..wgpu::InstanceDescriptor::new_without_display_handle() });
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance, compatible_surface: None, force_fallback_adapter: false,
})).expect("No GPU adapter");
let (features, limits, experimental) =
dreamwell_gpu::features::GpuCapabilities::device_descriptor_parts(&adapter);
let (device, _) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
label: Some("headless"), required_features: features, required_limits: limits,
memory_hints: Default::default(), experimental_features: experimental, trace: Default::default(),
})).expect("device");
let result = safety::run_safety_checks(&adapter, &device, config);
result.print();
if !result.passed { std::process::exit(1); }
}
struct Projectile {
body_id: BodyId,
age: f32,
}
struct Debris {
pos: glam::Vec3,
vel: glam::Vec3,
age: f32,
#[allow(dead_code)]
size: f32,
}
struct BenchmarkApp {
config: BenchmarkConfig,
window: Option<Arc<Window>>,
gpu: Option<GpuState>,
benchmark: BenchmarkState,
stats: BenchmarkStats,
frame_timer: Instant,
ready: bool,
started: bool,
finished: bool,
show_profiler: bool,
cinematic: bool,
smoothed_fps: f32,
profiler_fps: f32,
profiler_ft_ms: f32,
profiler_update_timer: f32,
adapter_name_cached: String,
free_camera: bool,
cam_pos: glam::Vec3,
cam_yaw: f32,
cam_pitch: f32,
keys_down: HashSet<KeyCode>,
cursor_pos: [f32; 2],
right_mouse_held: bool,
last_mouse_pos: [f32; 2],
physics: PhysicsWorld,
projectiles: Vec<Projectile>,
debris: Vec<Debris>,
base_scene: Option<dreamwell_engine::game_object::GameObjectScene>,
scene_dirty: bool,
prev_dynamic_count: usize,
render_mode: dreamwell_gpu::scene::RenderPath,
unified_mode: bool,
stress_count: u32,
gpu_driven: dreamwell_gpu::gpu_driven::GpuDrivenPipeline,
stress_objects: Vec<dreamwell_gpu::gpu_driven::GpuObjectData>,
quality_preset: Option<dreamwell_engine::material::DreamLightingQuality>,
import_path_buf: String,
import_status: Option<String>,
#[allow(dead_code)]
scene_loading: bool,
available_scenes: Vec<String>,
project_dir: Option<std::path::PathBuf>,
avatar_demo_state: Option<dreamwell_matter::avatar_demo::AvatarDemoState>,
}
struct GpuState {
surface: wgpu::Surface<'static>,
device: wgpu::Device,
queue: wgpu::Queue,
surface_config: wgpu::SurfaceConfiguration,
fabric: dreamwell_fabric::DreamFabric,
#[allow(dead_code)]
depth_texture: wgpu::Texture,
depth_view: wgpu::TextureView,
#[allow(dead_code)]
adapter_name: String,
#[allow(dead_code)]
adapter_backend: String,
rt_ctx: dreamwell_gpu::ray_tracing::RayTracingContext,
#[allow(dead_code)]
rt_hdr_fallback: wgpu::Texture,
rt_hdr_fallback_view: wgpu::TextureView,
egui_ctx: egui::Context,
egui_winit: egui_winit::State,
egui_renderer: egui_wgpu::Renderer,
}
impl BenchmarkApp {
fn new(config: BenchmarkConfig, stress_count: u32, render_mode: dreamwell_gpu::scene::RenderPath, unified_mode: bool) -> Self {
let duration = config.duration_secs;
Self {
config, window: None, gpu: None,
benchmark: BenchmarkState::new(duration),
stats: BenchmarkStats::new(),
frame_timer: Instant::now(),
ready: false, started: false, finished: false,
show_profiler: true, cinematic: true,
smoothed_fps: 0.0,
profiler_fps: 0.0,
profiler_ft_ms: 0.0,
profiler_update_timer: 0.0,
adapter_name_cached: String::new(),
free_camera: false,
cam_pos: DEFAULT_CAM_POS,
cam_yaw: DEFAULT_CAM_YAW,
cam_pitch: DEFAULT_CAM_PITCH,
keys_down: HashSet::new(),
cursor_pos: [0.0; 2],
right_mouse_held: false,
last_mouse_pos: [0.0; 2],
physics: PhysicsWorld::new(),
projectiles: Vec::new(),
debris: Vec::new(),
base_scene: None,
scene_dirty: false,
prev_dynamic_count: 0,
render_mode,
unified_mode,
stress_count,
gpu_driven: dreamwell_gpu::gpu_driven::GpuDrivenPipeline::default(),
stress_objects: Vec::new(),
quality_preset: None,
import_path_buf: String::new(),
import_status: None,
scene_loading: false,
available_scenes: Vec::new(),
project_dir: dreamwell_matter::scene_loader::resolve_project_dir(
&std::env::args().collect::<Vec<_>>()
),
avatar_demo_state: None,
}
}
fn camera_forward(&self) -> glam::Vec3 {
glam::Vec3::new(
self.cam_yaw.cos() * self.cam_pitch.cos(),
self.cam_pitch.sin(),
self.cam_yaw.sin() * self.cam_pitch.cos(),
).normalize_or_zero()
}
fn build_camera(&self) -> dreamwell_gpu::camera::Camera {
if self.free_camera {
let mut camera = dreamwell_gpu::camera::Camera::default();
camera.position = self.cam_pos;
let target = self.cam_pos + self.camera_forward();
camera.view_matrix = glam::Mat4::look_at_rh(self.cam_pos, target, glam::Vec3::Y);
camera.update_projection(self.config.aspect_ratio(), 55.0);
camera
} else if self.cinematic && self.started && !self.finished {
scene::cinematic_camera(
self.benchmark.elapsed(),
self.config.duration_secs,
self.config.aspect_ratio(),
)
} else {
scene::benchmark_camera(self.config.aspect_ratio())
}
}
fn update_free_camera(&mut self, dt: f32) {
if !self.free_camera { return; }
let speed = CAMERA_SPEED * dt;
let forward = glam::Vec3::new(
self.cam_yaw.cos() * self.cam_pitch.cos(),
0.0,
self.cam_yaw.sin() * self.cam_pitch.cos(),
).normalize_or_zero();
let right = glam::Vec3::new(-self.cam_yaw.sin(), 0.0, self.cam_yaw.cos()).normalize_or_zero();
if self.keys_down.contains(&KeyCode::KeyW) { self.cam_pos += forward * speed; }
if self.keys_down.contains(&KeyCode::KeyS) { self.cam_pos -= forward * speed; }
if self.keys_down.contains(&KeyCode::KeyA) { self.cam_pos -= right * speed; }
if self.keys_down.contains(&KeyCode::KeyD) { self.cam_pos += right * speed; }
if self.keys_down.contains(&KeyCode::Space) { self.cam_pos.y += speed; }
if self.keys_down.contains(&KeyCode::ControlLeft) || self.keys_down.contains(&KeyCode::ControlRight) {
self.cam_pos.y -= speed;
}
let look_speed = 2.0 * dt;
if self.keys_down.contains(&KeyCode::ArrowLeft) { self.cam_yaw -= look_speed; }
if self.keys_down.contains(&KeyCode::ArrowRight) { self.cam_yaw += look_speed; }
if self.keys_down.contains(&KeyCode::ArrowUp) { self.cam_pitch = (self.cam_pitch + look_speed).min(1.4); }
if self.keys_down.contains(&KeyCode::ArrowDown) { self.cam_pitch = (self.cam_pitch - look_speed).max(-1.4); }
}
fn launch_projectile(&mut self) {
let forward = self.camera_forward();
let spawn = self.cam_pos + forward * 0.5;
let mut body = RigidBody::dynamic(1.0, CollisionShape::Sphere { radius: PROJECTILE_RADIUS });
body.position = spawn.to_array();
body.velocity = (forward * PROJECTILE_SPEED).to_array();
body.restitution = 0.6;
body.friction = 0.3;
let id = self.physics.add_body(body);
self.projectiles.push(Projectile { body_id: id, age: 0.0 });
self.scene_dirty = true;
}
fn spawn_explosion(&mut self, pos: glam::Vec3) {
for i in 0..EXPLOSION_COUNT {
let angle = (i as f32 / EXPLOSION_COUNT as f32) * std::f32::consts::TAU;
let elevation = (i as f32 * 2.7).sin() * 0.8;
let speed = 4.0 + (i as f32 * 1.3).cos().abs() * 6.0;
let vel = glam::Vec3::new(
angle.cos() * speed,
elevation * speed + 3.0,
angle.sin() * speed,
);
self.debris.push(Debris {
pos,
vel,
age: 0.0,
size: 0.04 + (i as f32 * 0.7).sin().abs() * 0.08,
});
}
}
fn step_physics(&mut self, dt: f32) {
self.physics.step(dt);
let contacts = self.physics.contacts().to_vec();
let body_ids = self.physics.body_ids().to_vec();
let mut exploded_ids: Vec<BodyId> = Vec::new();
let mut explosion_points: Vec<glam::Vec3> = Vec::new();
for proj in &self.projectiles {
if exploded_ids.contains(&proj.body_id) { continue; }
for contact in &contacts {
let id_a = body_ids.get(contact.body_a).copied();
let id_b = body_ids.get(contact.body_b).copied();
let is_proj = id_a == Some(proj.body_id) || id_b == Some(proj.body_id);
if is_proj {
let pos = glam::Vec3::from(contact.point);
exploded_ids.push(proj.body_id);
explosion_points.push(pos);
break;
}
}
}
if !explosion_points.is_empty() {
self.scene_dirty = true;
}
for pos in &explosion_points {
self.spawn_explosion(*pos);
}
for id in &exploded_ids {
self.physics.remove_body(*id);
}
self.projectiles.retain(|p| !exploded_ids.contains(&p.body_id));
let mut expired_ids: Vec<BodyId> = Vec::new();
for p in &mut self.projectiles {
p.age += dt;
if p.age > 5.0 {
expired_ids.push(p.body_id);
}
}
for id in &expired_ids {
self.physics.remove_body(*id);
}
self.projectiles.retain(|p| !expired_ids.contains(&p.body_id));
let gravity = glam::Vec3::new(0.0, -9.81, 0.0);
self.debris.retain_mut(|d| {
d.age += dt;
if d.age > DEBRIS_LIFETIME { return false; }
d.vel += gravity * dt;
d.pos += d.vel * dt;
if d.pos.y < 0.0 {
d.pos.y = 0.0;
d.vel.y = -d.vel.y * 0.3;
d.vel.x *= 0.8;
d.vel.z *= 0.8;
}
true
});
}
fn stop_benchmark(&mut self) {
if !self.started || self.finished { return; }
self.finished = true;
self.stats.finalize();
self.stats.print_results();
match self.stats.export_csv(&self.config.csv_path) {
Ok(()) => log::info!("CSV exported: {}", self.config.csv_path),
Err(e) => log::warn!("CSV export failed: {e}"),
}
}
#[allow(dead_code)]
fn feature_summary(&self) -> String {
let mut features = Vec::new();
features.push("PBR");
features.push("IBL");
features.push("Shadow");
features.push("LOD");
features.push("Bloom");
features.push("ACES");
if let Some(ref gpu) = self.gpu {
if gpu.fabric.ssao_enabled() { features.push("SSAO"); }
if gpu.fabric.taa_enabled() { features.push("TAA"); }
if gpu.fabric.ssr_enabled() { features.push("SSR"); }
if gpu.fabric.dof_enabled() { features.push("DOF"); }
if gpu.fabric.ssgi_enabled() { features.push("SSGI"); }
if gpu.fabric.rt_enabled() { features.push("RT-GI"); }
if gpu.fabric.rt_shadow_enabled() { features.push("RT-Shadow"); }
}
features.join(" | ")
}
fn render_menu_bar(&mut self, ctx: &egui::Context) {
egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| {
egui::MenuBar::new().ui(ui, |ui| {
ui.menu_button("File", |ui| {
ui.label("Import Scene:");
ui.horizontal(|ui| {
ui.text_edit_singleline(&mut self.import_path_buf);
if ui.button("Load").clicked() && !self.import_path_buf.is_empty() {
let path = std::path::PathBuf::from(&self.import_path_buf);
match dreamwell_matter::scene_loader::load_scene_auto(&path) {
Ok(loaded) => {
let (warnings, errors) = dreamwell_engine::dream_file::reality_check(&loaded.scene);
if !errors.is_empty() {
self.import_status = Some(format!("Reality Check FAILED: {}", errors.join("; ")));
} else {
let obj_count = loaded.game_objects.objects.len();
self.base_scene = Some(loaded.game_objects);
self.scene_dirty = true;
if loaded.scene.asset_refs.iter().any(|a| a.kind == "fbx") {
self.avatar_demo_state = Some(
dreamwell_matter::avatar_demo::AvatarDemoState::from_scene(loaded.scene)
);
}
let warn_str = if warnings.is_empty() {
String::new()
} else {
format!(" ({} warnings)", warnings.len())
};
self.import_status = Some(format!(
"Loaded: {} objects{warn_str}", obj_count
));
}
}
Err(e) => {
self.import_status = Some(format!("Error: {e}"));
}
}
ui.close();
}
});
if let Some(ref project_dir) = self.project_dir.clone() {
ui.separator();
ui.label("Project Scenes:");
if self.available_scenes.is_empty() {
self.available_scenes = dreamwell_matter::scene_loader::list_project_scenes(project_dir);
}
for scene_name in self.available_scenes.clone() {
if ui.button(&scene_name).clicked() {
if let Some(path) = dreamwell_matter::scene_loader::resolve_scene_path(project_dir, &scene_name) {
match dreamwell_matter::scene_loader::load_scene_auto(&path) {
Ok(loaded) => {
let obj_count = loaded.game_objects.objects.len();
if loaded.scene.asset_refs.iter().any(|a| a.kind == "fbx") {
self.avatar_demo_state = Some(
dreamwell_matter::avatar_demo::AvatarDemoState::from_scene(loaded.scene)
);
}
self.base_scene = Some(loaded.game_objects);
self.scene_dirty = true;
self.import_status = Some(format!("Loaded '{scene_name}': {obj_count} objects"));
}
Err(e) => {
self.import_status = Some(format!("Error loading '{scene_name}': {e}"));
}
}
}
ui.close();
}
}
}
ui.separator();
if ui.button("Quit").clicked() {
std::process::exit(0);
}
});
ui.menu_button("View", |ui| {
if ui.checkbox(&mut self.show_profiler, "Profiler (F4)").clicked() {
ui.close();
}
});
if let Some(ref status) = self.import_status {
ui.separator();
ui.label(status);
}
});
});
}
fn render_profiler(&mut self, ctx: &egui::Context) -> Option<dreamwell_gpu::debug_vis::DebugVisMode> {
if !self.show_profiler { return None; }
let mut requested_mode: Option<dreamwell_gpu::debug_vis::DebugVisMode> = None;
let display_fps = if self.profiler_fps > 0.0 { self.profiler_fps } else { self.smoothed_fps };
let display_ft = if self.profiler_ft_ms > 0.0 { self.profiler_ft_ms }
else if self.smoothed_fps > 0.0 { 1000.0 / self.smoothed_fps }
else { 0.0 };
egui::Window::new("DreamMatter Profiler")
.collapsible(true)
.resizable(false)
.default_pos([10.0, 10.0])
.default_width(360.0)
.show(ctx, |ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
egui::CollapsingHeader::new("Performance (3s avg)").default_open(true).show(ui, |ui| {
ui.horizontal(|ui| {
ui.label("FPS:");
ui.label(format!("{:.0}", display_fps));
ui.separator();
ui.label("Frame:");
ui.label(format!("{:.2}ms", display_ft));
});
if self.stats.frame_count() > 10 {
ui.label(format!(
"p50: {:.2}ms p90: {:.2}ms p95: {:.2}ms p99: {:.2}ms",
self.stats.p50_ms(), self.stats.p90_ms(),
self.stats.p95_ms(), self.stats.p99_ms(),
));
ui.label(format!(
"Avg: {:.0} FPS ({:.2}ms) Min: {:.2}ms Max: {:.2}ms",
self.stats.avg_fps(), self.stats.avg_ms(),
self.stats.min_ms(), self.stats.max_ms(),
));
}
});
egui::CollapsingHeader::new("Camera").default_open(true).show(ui, |ui| {
let mode = if self.free_camera { "Free" }
else if self.cinematic { "Cinematic" }
else { "Fixed" };
ui.label(format!("Mode: {} Pos: ({:.1}, {:.1}, {:.1})",
mode, self.cam_pos.x, self.cam_pos.y, self.cam_pos.z));
});
egui::CollapsingHeader::new("Scene").default_open(true).show(ui, |ui| {
let build_name = if self.unified_mode {
"Dreamwell"
} else {
match self.render_mode {
dreamwell_gpu::scene::RenderPath::Dreamwell => "Dream (GPU-only)",
dreamwell_gpu::scene::RenderPath::Traditional => "Traditional (CPU+GPU)",
dreamwell_gpu::scene::RenderPath::RayTraced => "RTX (Ray-Traced)",
}
};
let render_name = match self.render_mode {
dreamwell_gpu::scene::RenderPath::Dreamwell => "Dreamwell",
dreamwell_gpu::scene::RenderPath::Traditional => "Traditional",
dreamwell_gpu::scene::RenderPath::RayTraced => "RTX",
};
let scene_mode = if self.stress_count > 0 { "Stress" } else { "Keynote" };
let rt_on = if let Some(ref gpu) = self.gpu { gpu.fabric.rt_enabled() } else { false };
ui.label(format!("Build: {} | Render: {} | Scene: {}", build_name, render_name, scene_mode));
ui.label(format!("Ray Tracing: {}", if rt_on { "On (GI + Shadows)" } else { "Off" }));
let gpu_only_count = if let Some(ref gpu) = self.gpu {
gpu.fabric.dreamlet_catalog().count
} else { 0 };
let cpu_gpu_count = if let Some(ref gpu) = self.gpu {
gpu.fabric.gpu_scene().object_count() as u32
} else { 0 };
let gpu_bytes = gpu_only_count as u64 * 144; let cpu_bytes = cpu_gpu_count as u64 * 256;
ui.separator();
ui.label("Rendered Objects:");
ui.label(format!(" GPU-only (dreamlets): {} ({:.1} KB, 1 draw)",
gpu_only_count, gpu_bytes as f64 / 1024.0));
ui.label(format!(" CPU+GPU (authored): {} ({:.1} KB, {} draws)",
cpu_gpu_count, cpu_bytes as f64 / 1024.0, cpu_gpu_count));
ui.label(format!(" Total visible: {}",
gpu_only_count + cpu_gpu_count));
ui.label(format!(" GPU payload/frame: {} bytes",
if gpu_only_count > 0 { 100 } else { 0 })); ui.label(format!(" CPU payload/frame: {:.1} KB",
cpu_bytes as f64 / 1024.0));
ui.separator();
ui.label(format!("Lights: {} Dreamlets: {}K",
self.stats.light_count, scene::DEFAULT_DREAMLET_CAPACITY / 1000));
if self.stress_count == 0 {
ui.label(format!("Projectiles: {} Debris: {}",
self.projectiles.len(), self.debris.len()));
}
});
egui::CollapsingHeader::new("Pipeline").default_open(true).show(ui, |ui| {
ui.label(format!("PBR: {} variants IBL: Active Shadow: Active",
self.stats.pbr_pipeline_count));
if let Some(ref gpu) = self.gpu {
let effects: Vec<&str> = [
("SSAO", gpu.fabric.ssao_enabled()),
("TAA", gpu.fabric.taa_enabled()),
("SSR", gpu.fabric.ssr_enabled()),
("DOF", gpu.fabric.dof_enabled()),
("SSGI", gpu.fabric.ssgi_enabled()),
].iter().filter(|(_, on)| *on).map(|(n, _)| *n).collect();
ui.label(format!("Effects: {}", effects.join(" | ")));
}
ui.label("LOD: 3-level Bloom: On Tonemap: ACES");
});
egui::CollapsingHeader::new("GPU").default_open(false).show(ui, |ui| {
ui.label(&self.adapter_name_cached);
if let Some(ref gpu) = self.gpu {
ui.label(format!("Backend: {}", gpu.adapter_backend));
ui.label(format!("Multi-draw: {}", gpu.fabric.gpu_scene().multi_draw_supported));
}
});
egui::CollapsingHeader::new("DreamNet").default_open(false).show(ui, |ui| {
let current_mode = if let Some(ref gpu) = self.gpu {
gpu.fabric.debug_vis_mode()
} else {
dreamwell_gpu::debug_vis::DebugVisMode::None
};
ui.label(format!("Active: {}", current_mode.label()));
ui.separator();
for mode in dreamwell_gpu::debug_vis::DebugVisMode::ALL {
let is_current = *mode == current_mode;
let label = if is_current {
format!("[{}]", mode.label())
} else {
mode.label().to_string()
};
if ui.selectable_label(is_current, &label).clicked() {
requested_mode = Some(*mode);
}
}
ui.separator();
ui.label("F6: cycle modes");
});
egui::CollapsingHeader::new("Physics").default_open(true).show(ui, |ui| {
ui.label(format!("Bodies: {} Contacts: {}",
self.physics.body_count(), self.physics.contacts().len()));
ui.label(format!("Projectiles: {} Debris: {}",
self.projectiles.len(), self.debris.len()));
});
if self.started && !self.finished {
ui.separator();
let r = (self.config.duration_secs - self.benchmark.elapsed()).max(0.0);
ui.label(format!("Benchmark: {} | {:.0}s remaining",
self.benchmark.phase_label(), r));
} else if self.finished {
ui.separator();
ui.label(format!("DONE | {:.0} FPS avg | p95 {:.1}ms",
self.stats.avg_fps(), self.stats.p95_ms()));
}
});
requested_mode
}
}
impl Drop for BenchmarkApp {
fn drop(&mut self) {
self.gpu_driven.destroy();
if let Some(mut gpu) = self.gpu.take() {
gpu.fabric.destroy();
}
}
}
impl ApplicationHandler for BenchmarkApp {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if self.window.is_some() { return; }
let attrs = Window::default_attributes()
.with_title("DreamMatter Keynote v1.0.0 — Loading...")
.with_inner_size(winit::dpi::LogicalSize::new(self.config.width, self.config.height))
.with_resizable(false);
let window = Arc::new(event_loop.create_window(attrs).expect("window"));
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { backends: wgpu::Backends::all(), ..wgpu::InstanceDescriptor::new_without_display_handle() });
let surface = instance.create_surface(window.clone()).expect("surface");
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance, compatible_surface: Some(&surface), force_fallback_adapter: false,
})).expect("adapter");
let info = adapter.get_info();
let (features, limits, experimental) =
dreamwell_gpu::features::GpuCapabilities::device_descriptor_parts(&adapter);
let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
label: Some("dreamwell_keynote"), required_features: features, required_limits: limits,
memory_hints: Default::default(), experimental_features: experimental, trace: Default::default(),
})).expect("device");
device.on_uncaptured_error(std::sync::Arc::new(|error| {
log::error!("gpu_error:{error}");
}));
let safety_result = safety::run_safety_checks(&adapter, &device, &self.config);
safety_result.print();
if !safety_result.passed { event_loop.exit(); return; }
let caps = surface.get_capabilities(&adapter);
let format = caps.formats.iter()
.find(|f| !f.is_srgb())
.or_else(|| caps.formats.first())
.copied()
.unwrap_or(wgpu::TextureFormat::Bgra8Unorm);
let size = window.inner_size();
let surface_config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format,
width: size.width.max(1), height: size.height.max(1),
present_mode: self.config.present_mode(),
alpha_mode: caps.alpha_modes[0], view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &surface_config);
let multi_draw = device.features().contains(wgpu::Features::MULTI_DRAW_INDIRECT_COUNT);
let mut fabric = dreamwell_fabric::DreamFabric::new(&device, format, false);
fabric.resize(&device, surface_config.width, surface_config.height);
fabric.init_ibl(&device, &queue);
fabric.gpu_scene_mut().ensure_pbr_pipelines(&device, &queue, dreamwell_gpu::post::HDR_FORMAT);
fabric.gpu_scene_mut().set_multi_draw_support(multi_draw);
fabric.init_debug_vis(&device, surface_config.width, surface_config.height);
if self.stress_count > 0 {
self.stress_objects = scene_stress::generate_stress_scene(self.stress_count);
self.gpu_driven.init(&device, self.stress_count);
scene_stress::setup_stress_lights(&mut fabric);
log::info!("GPU-driven stress mode: {} objects", self.stress_count);
} else {
let base_scene = scene::create_base_scene();
scene::setup_benchmark_scene(&device, &queue, &mut fabric, &base_scene);
let gpu_objects = scene::scene_to_gpu_objects(&base_scene, 0.35, 0.15);
let max_objects = (gpu_objects.len() as u32 + 256).max(512); self.gpu_driven.init(&device, max_objects);
self.stress_objects = gpu_objects;
self.base_scene = Some(base_scene);
log::info!("GPU-driven keynote: {} base objects, {} max capacity", self.stress_objects.len(), max_objects);
}
let resolved_mode = if self.unified_mode {
let detected = dreamwell_gpu::scene::GpuScene::auto_detect_render_path(&device);
log::info!("Build: Dreamwell | Auto-detected: {:?}", detected);
detected
} else {
log::info!("Build: {} | Explicit profile",
match self.render_mode {
dreamwell_gpu::scene::RenderPath::Dreamwell => "Dream (GPU-only)",
dreamwell_gpu::scene::RenderPath::Traditional => "Traditional (CPU+GPU)",
dreamwell_gpu::scene::RenderPath::RayTraced => "RTX (Ray-Traced)",
});
self.render_mode
};
self.render_mode = resolved_mode;
fabric.gpu_scene_mut().active_render_path = resolved_mode;
let mut rt_ctx = dreamwell_gpu::ray_tracing::RayTracingContext::default();
if resolved_mode == dreamwell_gpu::scene::RenderPath::RayTraced {
let rt_available = device.features().contains(wgpu::Features::EXPERIMENTAL_RAY_QUERY);
if rt_available {
fabric.enable_ray_tracing();
rt_ctx = dreamwell_gpu::ray_tracing::RayTracingContext::init_if_supported(
&device, device.features(),
);
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("rt_init"),
});
for shape in dreamwell_gpu::primitives::PrimitiveShape::ALL {
let mesh = shape.generate(dreamwell_gpu::primitives::DEFAULT_PRIMITIVE_COLOR);
let vertex_data: Vec<[f32; 3]> = mesh.vertices.iter().map(|v| v.position).collect();
let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(shape.name()),
contents: bytemuck::cast_slice(&vertex_data),
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT,
});
let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(shape.name()),
contents: bytemuck::cast_slice(&mesh.indices),
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::BLAS_INPUT,
});
rt_ctx.register_primitive_blas(
&device, &mut encoder, shape.name(),
&vertex_buf, vertex_data.len() as u32,
std::mem::size_of::<[f32; 3]>() as u64,
Some(&index_buf), Some(mesh.indices.len() as u32),
);
}
queue.submit(std::iter::once(encoder.finish()));
if let Some(tlas) = rt_ctx.tlas() {
fabric.init_rt(&device, surface_config.width, surface_config.height, tlas);
}
log::info!("RT: {} BLAS + GI + shadows enabled for interactive keynote",
dreamwell_gpu::primitives::PrimitiveShape::ALL.len());
} else {
log::warn!("RT: --ray-tracing requested but adapter lacks ray query support, falling back to Dreamwell");
self.render_mode = dreamwell_gpu::scene::RenderPath::Dreamwell;
}
}
if let Some(quality) = self.quality_preset {
fabric.set_dream_lighting_quality(quality);
log::info!("Dream Lighting: {:?} preset applied", quality);
}
if resolved_mode == dreamwell_gpu::scene::RenderPath::Dreamwell {
let dvr_capacity = (self.stress_objects.len() as u32 + 256).max(512);
fabric.dreamlet_catalog_mut().init(&device, dvr_capacity);
fabric.dreamlet_catalog_mut().init_render_pipeline(&device, dreamwell_gpu::post::HDR_FORMAT);
let dreamlets = self.stress_objects.iter().map(|obj| {
dreamwell_gpu::dreamlet_catalog::GpuDreamlet {
position: obj.bounding_center,
scale: obj.bounding_radius.max(0.3),
velocity: [0.0; 3],
angular_velocity: 0.0,
rotation: [0.0, 0.0, 0.0, 1.0], base_color: obj.base_color,
roughness: obj.roughness,
metallic: obj.metallic,
mass: 1.0,
restitution: 0.5,
state: dreamwell_gpu::dreamlet_catalog::DREAMLET_STATE_STATIC,
mesh_id: obj.mesh_id,
parent_id: 0,
flags: dreamwell_gpu::dreamlet_catalog::DREAMLET_FLAG_ACTIVE
| dreamwell_gpu::dreamlet_catalog::DREAMLET_FLAG_PHYSICS,
age: 0.0,
lifetime: 0.0,
_pad_target: [0.0; 2],
target_position: obj.bounding_center,
deconstruct_level: 0,
bounding_radius: obj.bounding_radius,
skeleton_index: 0,
_pad_struct: [0.0; 2],
}
}).collect::<Vec<_>>();
fabric.dreamlet_catalog_mut().upload(&queue, &dreamlets);
fabric.gpu_scene_mut().upload_pbr_lights(&device, &queue);
let dir_buf_ptr = fabric.gpu_scene().pbr_directional_buf_ref().map(|b| b.clone());
let pt_buf_ptr = fabric.gpu_scene().pbr_point_buf_ref().map(|b| b.clone());
if let (Some(ref dir_buf), Some(ref pt_buf)) = (&dir_buf_ptr, &pt_buf_ptr) {
let ibl = fabric.ibl_precompute();
let irr = ibl.irradiance_view().is_some();
if irr {
let irr_view = fabric.ibl_precompute().irradiance_view().unwrap().clone();
let spec_view = fabric.ibl_precompute().specular_view().unwrap().clone();
let brdf_view = fabric.ibl_precompute().brdf_lut_view().unwrap().clone();
fabric.dreamlet_catalog_mut().create_render_bind_groups_from_views(
&device, dir_buf, pt_buf,
&irr_view, &spec_view, &brdf_view,
);
}
}
log::info!("DVR active: {} dreamlets uploaded, render pipeline ready", dreamlets.len());
}
if self.stress_count > 0 {
fabric.set_ssao_enabled(false);
fabric.set_taa_enabled(false);
fabric.set_ssr_enabled(false);
fabric.set_dof_enabled(false);
fabric.set_ssgi_enabled(false);
fabric.set_csm_enabled(false);
fabric.set_hiz_enabled(false);
fabric.set_motion_vectors_enabled(false);
fabric.set_volumetric_fog_enabled(false);
log::info!("Stress mode: orphaned effects disabled (VRAM saved)");
} else {
fabric.set_ssao_enabled(true);
fabric.set_taa_enabled(true);
fabric.set_ssr_enabled(true);
fabric.set_dof_enabled(false);
fabric.set_ssgi_enabled(true);
}
let depth_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("keynote_depth"),
size: wgpu::Extent3d { width: surface_config.width, height: surface_config.height, depth_or_array_layers: 1 },
mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2,
format: dreamwell_gpu::formats::DEPTH_FORMAT,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
let egui_ctx = egui::Context::default();
let egui_winit = egui_winit::State::new(
egui_ctx.clone(),
egui_ctx.viewport_id(),
&window,
Some(window.scale_factor() as f32),
None,
None,
);
let egui_renderer = egui_wgpu::Renderer::new(
&device,
format,
egui_wgpu::RendererOptions::default(),
);
egui_ctx.set_visuals(egui::Visuals::dark());
self.stats.adapter_name = info.name.clone();
self.stats.adapter_backend = format!("{:?}", info.backend);
self.stats.resolution = [surface_config.width, surface_config.height];
self.stats.scene_object_count = fabric.scene_object_count() as u32;
self.stats.dreamlet_capacity = fabric.matter().dreammatter_capacity();
self.stats.light_count = (fabric.gpu_scene().scene_lights.directional.len()
+ fabric.gpu_scene().scene_lights.point.len()) as u32;
self.stats.pbr_pipeline_count = fabric.gpu_scene().pbr_pipeline_count() as u32;
self.stats.ibl_initialized = true;
self.stats.post_process_enabled = true;
self.stats.quality_preset = self.quality_preset
.map(|q| format!("{:?}", q))
.unwrap_or_default();
self.adapter_name_cached = info.name.clone();
scene::setup_physics_colliders(&mut self.physics);
log::info!(
"Keynote ready: {} | {} | SSAO TAA SSR SSGI | {}x{} | {} objects | {} lights | {} Dreamlets",
info.name, format!("{:?}", info.backend),
surface_config.width, surface_config.height,
fabric.scene_object_count(),
self.stats.light_count,
scene::DEFAULT_DREAMLET_CAPACITY,
);
let rt_hdr_fallback = device.create_texture(&wgpu::TextureDescriptor {
label: Some("rt_hdr_fallback"),
size: wgpu::Extent3d { width: 1, height: 1, depth_or_array_layers: 1 },
mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba16Float,
usage: wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let rt_hdr_fallback_view = rt_hdr_fallback.create_view(&wgpu::TextureViewDescriptor::default());
self.gpu = Some(GpuState {
surface, device, queue, surface_config, fabric,
depth_texture, depth_view,
adapter_name: info.name, adapter_backend: format!("{:?}", info.backend),
rt_ctx,
rt_hdr_fallback, rt_hdr_fallback_view,
egui_ctx, egui_winit, egui_renderer,
});
self.window = Some(window);
self.ready = true;
self.frame_timer = Instant::now();
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: winit::window::WindowId, event: WindowEvent) {
if let (Some(ref mut gpu), Some(ref window)) = (&mut self.gpu, &self.window) {
let response = gpu.egui_winit.on_window_event(window, &event);
if response.consumed { return; }
}
match event {
WindowEvent::CloseRequested => {
if self.started && !self.finished { self.stop_benchmark(); }
if let Some(mut gpu) = self.gpu.take() { gpu.fabric.destroy(); }
event_loop.exit();
}
WindowEvent::CursorMoved { position, .. } => {
let new_pos = [position.x as f32, position.y as f32];
if self.right_mouse_held && self.free_camera {
let dx = new_pos[0] - self.last_mouse_pos[0];
let dy = new_pos[1] - self.last_mouse_pos[1];
self.cam_yaw += dx * 0.003;
self.cam_pitch = (self.cam_pitch - dy * 0.003).clamp(-1.4, 1.4);
}
self.last_mouse_pos = new_pos;
self.cursor_pos = new_pos;
}
WindowEvent::MouseInput { state, button, .. } => {
let pressed = state == winit::event::ElementState::Pressed;
match button {
MouseButton::Right => {
self.right_mouse_held = pressed;
}
MouseButton::Left if pressed => {
if self.free_camera {
self.launch_projectile();
}
}
_ => {}
}
}
WindowEvent::KeyboardInput { event, .. } => {
if let PhysicalKey::Code(code) = event.physical_key {
if event.state == winit::event::ElementState::Pressed {
self.keys_down.insert(code);
match code {
KeyCode::Enter => {
if self.ready && !self.started {
self.started = true;
self.benchmark.start();
self.frame_timer = Instant::now();
log::info!("Keynote started ({:.0}s)", self.config.duration_secs);
} else if self.started && !self.finished {
log::info!("Keynote stopped early at {:.1}s", self.benchmark.elapsed());
self.stop_benchmark();
}
}
KeyCode::F1 => {
self.cam_pos = DEFAULT_CAM_POS;
self.cam_yaw = DEFAULT_CAM_YAW;
self.cam_pitch = DEFAULT_CAM_PITCH;
self.free_camera = false;
self.cinematic = true;
}
KeyCode::F2 => {
self.free_camera = !self.free_camera;
if self.free_camera {
self.cinematic = false;
self.cam_pos = DEFAULT_CAM_POS;
self.cam_yaw = DEFAULT_CAM_YAW;
self.cam_pitch = DEFAULT_CAM_PITCH;
log::info!("Free camera (WASD + Right-drag pan + Left-click shoot)");
} else {
log::info!("Free camera off");
}
}
KeyCode::F3 => {
if !self.free_camera {
self.cinematic = !self.cinematic;
log::info!("Cinematic orbit: {}", if self.cinematic { "on" } else { "off" });
}
}
KeyCode::F4 => { self.show_profiler = !self.show_profiler; }
KeyCode::F5 => {
if let Some(ref mut gpu) = self.gpu {
let new_state = !gpu.fabric.dof_enabled();
gpu.fabric.set_dof_enabled(new_state);
log::info!("DOF: {}", if new_state { "on" } else { "off" });
}
}
KeyCode::F6 => {
if let Some(ref mut gpu) = self.gpu {
gpu.fabric.cycle_debug_vis();
let mode = gpu.fabric.debug_vis_mode();
log::info!("DreamNet: {}", mode.label());
}
}
KeyCode::Escape if self.finished => { event_loop.exit(); }
_ => {}
}
} else {
self.keys_down.remove(&code);
}
}
}
WindowEvent::RedrawRequested => {
let raw_dt = self.frame_timer.elapsed().as_secs_f32();
let dt = raw_dt.clamp(0.0001, self.config.max_dt);
self.frame_timer = Instant::now();
let fps = if dt > 0.0 { 1.0 / dt } else { 0.0 };
self.smoothed_fps = if self.smoothed_fps == 0.0 { fps }
else { self.smoothed_fps * 0.95 + fps * 0.05 };
self.profiler_update_timer += dt;
if self.profiler_update_timer >= 3.0 {
self.profiler_fps = self.smoothed_fps;
self.profiler_ft_ms = if self.smoothed_fps > 0.0 { 1000.0 / self.smoothed_fps } else { 0.0 };
self.profiler_update_timer = 0.0;
}
self.update_free_camera(dt);
self.step_physics(dt);
if self.gpu_driven.enabled {
let dynamic_count = self.projectiles.len() + self.debris.len();
if dynamic_count != self.prev_dynamic_count || self.scene_dirty {
if self.stress_count == 0 {
if let Some(ref base_scene) = self.base_scene {
self.stress_objects = scene::scene_to_gpu_objects(base_scene, 0.35, 0.15);
let proj_positions: Vec<glam::Vec3> = self.projectiles.iter()
.filter_map(|p| self.physics.body(p.body_id).map(|b| glam::Vec3::from(b.position)))
.collect();
let debris_data: Vec<(glam::Vec3, f32)> = self.debris.iter()
.map(|d| (d.pos, d.age))
.collect();
scene::add_dynamic_gpu_objects(&mut self.stress_objects, &proj_positions, &debris_data);
}
}
if let Some(ref gpu) = self.gpu {
self.gpu_driven.upload_objects(&gpu.queue, &self.stress_objects);
}
self.stats.scene_object_count = self.stress_objects.len() as u32;
self.prev_dynamic_count = dynamic_count;
self.scene_dirty = false;
}
}
let camera = self.build_camera();
let mut render_result: Option<(
wgpu::SurfaceTexture,
wgpu::TextureView,
wgpu::CommandBuffer,
bool, // should_stop
)> = None;
if let Some(gpu) = &mut self.gpu {
let output = match gpu.surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(t)
| wgpu::CurrentSurfaceTexture::Suboptimal(t) => t,
wgpu::CurrentSurfaceTexture::Lost
| wgpu::CurrentSurfaceTexture::Outdated => {
gpu.surface.configure(&gpu.device, &gpu.surface_config);
return;
}
_ => return,
};
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = gpu.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("keynote_render"),
});
let mut should_stop = false;
if self.started && !self.finished {
let phase = self.benchmark.phase_label();
self.stats.record_frame_with_phase(dt, phase);
self.benchmark.advance(dt);
scene::update_sun_orbit(&mut gpu.fabric, self.benchmark.elapsed(), self.config.duration_secs);
if self.benchmark.is_complete() {
should_stop = true;
}
}
if gpu.fabric.dreamlet_catalog().count > 0 {
let w = gpu.surface_config.width as f32;
let h = gpu.surface_config.height as f32;
gpu.fabric.dreamlet_catalog().dispatch_cull(
&gpu.device, &gpu.queue, &mut encoder,
&camera, w, h, 200.0, 1.0,
);
}
if gpu.fabric.rt_enabled() {
if let Some(tlas) = gpu.rt_ctx.tlas() {
gpu.fabric.dispatch_rt(
&gpu.device, &gpu.queue, &mut encoder, tlas,
&gpu.depth_view, &gpu.rt_hdr_fallback_view,
&camera,
[0.4, 0.8, 0.3], 1.0,
);
}
}
let fc = gpu.fabric.begin_frame(&gpu.queue, dt, camera.position, dreamwell_engine::TopologyLayer::Area);
gpu.fabric.end_frame(&gpu.device, &gpu.queue, &mut encoder, &view, &gpu.depth_view, &camera, &fc, None);
let render_cmds = encoder.finish();
render_result = Some((output, view, render_cmds, should_stop));
}
if let Some((output, view, render_cmds, should_stop)) = render_result {
let egui_ctx = self.gpu.as_ref().unwrap().egui_ctx.clone();
let raw_input = self.gpu.as_mut().unwrap().egui_winit
.take_egui_input(self.window.as_ref().unwrap());
egui_ctx.begin_pass(raw_input);
self.render_menu_bar(&egui_ctx);
if let Some(ref mut demo) = self.avatar_demo_state {
demo.render_ui(&egui_ctx);
}
let debug_mode_request = self.render_profiler(&egui_ctx);
let full_output = egui_ctx.end_pass();
let clipped_primitives = egui_ctx.tessellate(full_output.shapes, full_output.pixels_per_point);
if let Some(mode) = debug_mode_request {
self.gpu.as_mut().unwrap().fabric.set_debug_vis_mode(mode);
}
let gpu = self.gpu.as_mut().unwrap();
let screen_desc = egui_wgpu::ScreenDescriptor {
size_in_pixels: [gpu.surface_config.width, gpu.surface_config.height],
pixels_per_point: full_output.pixels_per_point,
};
for (id, image_delta) in &full_output.textures_delta.set {
gpu.egui_renderer.update_texture(&gpu.device, &gpu.queue, *id, image_delta);
}
let mut egui_encoder = gpu.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("egui_encoder"),
});
gpu.egui_renderer.update_buffers(&gpu.device, &gpu.queue, &mut egui_encoder, &clipped_primitives, &screen_desc);
let encoder_ptr = Box::into_raw(Box::new(egui_encoder));
{
let encoder_ref: &'static mut wgpu::CommandEncoder = unsafe { &mut *encoder_ptr };
let mut pass = encoder_ref.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("egui_pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
gpu.egui_renderer.render(&mut pass, &clipped_primitives, &screen_desc);
}
for id in &full_output.textures_delta.free {
gpu.egui_renderer.free_texture(id);
}
let egui_encoder = unsafe { Box::from_raw(encoder_ptr) };
let egui_cmds = egui_encoder.finish();
let mut submissions: Vec<wgpu::CommandBuffer> = Vec::with_capacity(2);
submissions.push(render_cmds);
submissions.push(egui_cmds);
gpu.queue.submit(submissions);
output.present();
gpu.egui_winit.handle_platform_output(self.window.as_ref().unwrap(), full_output.platform_output);
if should_stop { self.stop_benchmark(); }
}
if let Some(ref window) = self.window {
let cam = if self.free_camera { "Free" }
else if self.cinematic { "Cinematic" }
else { "Fixed" };
let build = if self.unified_mode { "Dreamwell" }
else { match self.render_mode {
dreamwell_gpu::scene::RenderPath::Dreamwell => "Dream",
dreamwell_gpu::scene::RenderPath::Traditional => "Traditional",
dreamwell_gpu::scene::RenderPath::RayTraced => "RTX",
}};
let render = match self.render_mode {
dreamwell_gpu::scene::RenderPath::Dreamwell => "Dreamwell",
dreamwell_gpu::scene::RenderPath::Traditional => "Traditional",
dreamwell_gpu::scene::RenderPath::RayTraced => "RTX",
};
window.set_title(&format!(
"DreamMatter {} ({}) | {:.0} FPS | {} | {} | [F4] profiler",
build, render, self.smoothed_fps, cam, self.adapter_name_cached,
));
}
if let Some(ref window) = self.window { window.request_redraw(); }
}
_ => {}
}
}
}
const GALLERY_WIDTH: u32 = 1920;
const GALLERY_HEIGHT: u32 = 1080;
struct GalleryApp {
#[allow(dead_code)]
gallery_dir: std::path::PathBuf,
model_paths: Vec<std::path::PathBuf>,
current_index: usize,
window: Option<Arc<Window>>,
gpu: Option<GpuState>,
frame_timer: Instant,
cam_pos: glam::Vec3,
cam_yaw: f32,
cam_pitch: f32,
cam_distance: f32,
cam_target: glam::Vec3,
free_camera: bool,
keys_held: HashSet<KeyCode>,
smoothed_fps: f32,
loading: bool,
current_model_name: String,
current_stats: Option<dreamwell_gpu::gltf::GltfStats>,
import_time_ms: f32,
right_mouse_held: bool,
last_mouse_pos: [f32; 2],
gpu_driven: dreamwell_gpu::gpu_driven::GpuDrivenPipeline,
gpu_objects: Vec<dreamwell_gpu::gpu_driven::GpuObjectData>,
adapter_name_cached: String,
rt_available: bool,
quality_preset: Option<dreamwell_engine::material::DreamLightingQuality>,
}
impl GalleryApp {
fn new(gallery_dir: std::path::PathBuf, quality_preset: Option<dreamwell_engine::material::DreamLightingQuality>) -> Self {
let mut model_paths: Vec<std::path::PathBuf> = Vec::new();
if let Ok(entries) = std::fs::read_dir(&gallery_dir) {
for entry in entries.flatten() {
let path = entry.path();
let ext = path.extension()
.and_then(|e| e.to_str())
.map(|s| s.to_lowercase())
.unwrap_or_default();
if ext == "zip" || ext == "glb" || ext == "gltf" {
model_paths.push(path);
}
}
}
model_paths.sort();
let default_index = model_paths.iter()
.position(|p| {
p.file_stem()
.and_then(|s| s.to_str())
.map(|s| s == "adamHead")
.unwrap_or(false)
})
.unwrap_or(0);
let model_name = model_paths.get(default_index)
.and_then(|p| p.file_stem())
.and_then(|s| s.to_str())
.unwrap_or("(none)")
.to_string();
log::info!("RT Gallery: found {} models in {}", model_paths.len(), gallery_dir.display());
if model_paths.is_empty() {
log::warn!("RT Gallery: no .zip/.glb/.gltf files found in {}", gallery_dir.display());
}
Self {
gallery_dir,
model_paths,
current_index: default_index,
window: None,
gpu: None,
frame_timer: Instant::now(),
cam_pos: glam::Vec3::new(0.0, 1.0, 5.0),
cam_yaw: -std::f32::consts::FRAC_PI_4,
cam_pitch: -0.2,
cam_distance: 5.0,
cam_target: glam::Vec3::ZERO,
free_camera: false,
keys_held: HashSet::new(),
smoothed_fps: 0.0,
loading: false,
current_model_name: model_name,
current_stats: None,
import_time_ms: 0.0,
right_mouse_held: false,
last_mouse_pos: [0.0; 2],
gpu_driven: dreamwell_gpu::gpu_driven::GpuDrivenPipeline::default(),
gpu_objects: Vec::new(),
adapter_name_cached: String::new(),
rt_available: false,
quality_preset,
}
}
fn build_camera(&self) -> dreamwell_gpu::camera::Camera {
let aspect = GALLERY_WIDTH as f32 / GALLERY_HEIGHT as f32;
let mut camera = dreamwell_gpu::camera::Camera::default();
if self.free_camera {
let forward = glam::Vec3::new(
self.cam_yaw.cos() * self.cam_pitch.cos(),
self.cam_pitch.sin(),
self.cam_yaw.sin() * self.cam_pitch.cos(),
).normalize_or_zero();
camera.position = self.cam_pos;
let target = self.cam_pos + forward;
camera.view_matrix = glam::Mat4::look_at_rh(self.cam_pos, target, glam::Vec3::Y);
} else {
let eye = self.cam_target + glam::Vec3::new(
self.cam_yaw.cos() * self.cam_pitch.cos() * self.cam_distance,
self.cam_pitch.sin() * self.cam_distance,
self.cam_yaw.sin() * self.cam_pitch.cos() * self.cam_distance,
);
camera.position = eye;
camera.view_matrix = glam::Mat4::look_at_rh(eye, self.cam_target, glam::Vec3::Y);
}
camera.update_projection(aspect, 55.0);
camera
}
fn update_free_camera(&mut self, dt: f32) {
if !self.free_camera { return; }
let speed = CAMERA_SPEED * dt;
let forward = glam::Vec3::new(
self.cam_yaw.cos() * self.cam_pitch.cos(),
0.0,
self.cam_yaw.sin() * self.cam_pitch.cos(),
).normalize_or_zero();
let right = glam::Vec3::new(-self.cam_yaw.sin(), 0.0, self.cam_yaw.cos()).normalize_or_zero();
if self.keys_held.contains(&KeyCode::KeyW) { self.cam_pos += forward * speed; }
if self.keys_held.contains(&KeyCode::KeyS) { self.cam_pos -= forward * speed; }
if self.keys_held.contains(&KeyCode::KeyA) { self.cam_pos -= right * speed; }
if self.keys_held.contains(&KeyCode::KeyD) { self.cam_pos += right * speed; }
if self.keys_held.contains(&KeyCode::Space) { self.cam_pos.y += speed; }
if self.keys_held.contains(&KeyCode::ControlLeft) || self.keys_held.contains(&KeyCode::ControlRight) {
self.cam_pos.y -= speed;
}
}
fn load_current_model(&mut self) {
if self.model_paths.is_empty() {
self.current_model_name = "(no models)".to_string();
self.current_stats = None;
self.import_time_ms = 0.0;
return;
}
let path = &self.model_paths[self.current_index];
self.current_model_name = path.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("unknown")
.to_string();
self.loading = true;
log::info!("RT Gallery: loading {} ({}/{})",
self.current_model_name, self.current_index + 1, self.model_paths.len());
let config = dreamwell_gpu::gltf::GltfImportConfig::default();
let import_start = Instant::now();
match dreamwell_gpu::gltf::import_gltf(path, &config) {
Ok(report) => {
self.import_time_ms = import_start.elapsed().as_secs_f32() * 1000.0;
let result = &report.result;
self.current_stats = Some(result.stats.clone());
let mut objects: Vec<dreamwell_gpu::gpu_driven::GpuObjectData> = Vec::new();
let default_mat = dreamwell_gpu::gltf::GltfMaterial::default();
for node in &result.nodes {
let mesh_idx = match node.mesh_index {
Some(idx) => idx,
None => continue,
};
let mesh = match result.meshes.get(mesh_idx) {
Some(m) => m,
None => continue,
};
let t = node.translation;
let r = node.rotation; let s = node.scale;
let quat = glam::Quat::from_xyzw(r[0], r[1], r[2], r[3]);
let model_mat = glam::Mat4::from_scale_rotation_translation(
glam::Vec3::new(s[0], s[1], s[2]),
quat,
glam::Vec3::new(t[0], t[1], t[2]),
);
let model = model_mat.to_cols_array();
for prim in &mesh.primitives {
let mat = prim.material_index
.and_then(|i| result.materials.get(i))
.unwrap_or(&default_mat);
let mut prim_center = glam::Vec3::ZERO;
if !prim.vertices.is_empty() {
for v in &prim.vertices {
prim_center += glam::Vec3::from(v.position);
}
prim_center /= prim.vertices.len() as f32;
}
let mut prim_radius: f32 = 0.0;
for v in &prim.vertices {
let d = glam::Vec3::from(v.position).distance(prim_center);
if d > prim_radius { prim_radius = d; }
}
let world_center = model_mat.transform_point3(prim_center);
let max_scale = s[0].abs().max(s[1].abs()).max(s[2].abs());
let world_radius = prim_radius * max_scale;
objects.push(dreamwell_gpu::gpu_driven::GpuObjectData {
model,
base_color: mat.base_color_factor,
roughness: mat.roughness_factor,
metallic: mat.metallic_factor,
emissive_strength: 0.0,
normal_scale: 1.0,
bounding_center: world_center.to_array(),
bounding_radius: world_radius.max(0.01),
mesh_id: 0, _pad: [0; 3],
});
}
}
self.gpu_objects = objects;
let bs = result.bounding_sphere;
self.cam_target = glam::Vec3::new(bs[0], bs[1], bs[2]);
self.cam_distance = bs[3].max(0.5) * 2.5;
self.cam_yaw = -std::f32::consts::FRAC_PI_4; self.cam_pitch = -0.2;
let gallery_scene = self.build_gallery_scene(&report.result);
if let Some(ref mut gpu) = self.gpu {
let needed = (self.gpu_objects.len() as u32 + 64).max(128);
if needed > self.gpu_driven.object_capacity {
self.gpu_driven.destroy();
self.gpu_driven = dreamwell_gpu::gpu_driven::GpuDrivenPipeline::default();
self.gpu_driven.init(&gpu.device, needed);
}
self.gpu_driven.upload_objects(&gpu.queue, &self.gpu_objects);
gpu.fabric.upload_gltf(&gpu.device, &gpu.queue, &report.result);
gpu.fabric.upload_scene(&gpu.device, &gallery_scene);
if gpu.rt_ctx.enabled {
let materials: Vec<[f32; 8]> = self.gpu_objects.iter().map(|obj| [
obj.base_color[0], obj.base_color[1], obj.base_color[2], obj.base_color[3],
obj.roughness, obj.metallic, obj.emissive_strength, 0.0,
]).collect();
gpu.fabric.rt_gi_pass().upload_materials(&gpu.queue, &materials);
}
if gpu.rt_ctx.enabled && !self.gpu_objects.is_empty() {
let instance_count = self.gpu_objects.len();
if instance_count > gpu.rt_ctx.max_instances as usize {
gpu.rt_ctx.resize_tlas(&gpu.device, (instance_count as u32).next_power_of_two());
if let Some(tlas) = gpu.rt_ctx.tlas() {
gpu.fabric.init_rt(
&gpu.device,
gpu.surface_config.width,
gpu.surface_config.height,
tlas,
);
}
}
gpu.rt_ctx.clear_instances();
let blas_count = gpu.rt_ctx.blas_list.len();
if blas_count > 0 {
for (i, obj) in self.gpu_objects.iter().enumerate() {
if i >= gpu.rt_ctx.max_instances as usize { break; }
let m = &obj.model;
let transform = [
m[0], m[4], m[8], m[12],
m[1], m[5], m[9], m[13],
m[2], m[6], m[10], m[14],
];
let blas_idx = (obj.mesh_id as usize / 3).min(blas_count - 1);
gpu.rt_ctx.set_instance(i, blas_idx, transform, i as u32, 0xFF);
}
}
let mut encoder = gpu.device.create_command_encoder(
&wgpu::CommandEncoderDescriptor { label: Some("gallery_tlas_rebuild") },
);
gpu.rt_ctx.rebuild_tlas(&mut encoder);
gpu.queue.submit(std::iter::once(encoder.finish()));
}
}
log::info!(
"RT Gallery: loaded {} — {} meshes, {} verts, {} tris, {} materials, {:.1}ms",
self.current_model_name,
result.stats.mesh_count,
result.stats.vertex_count,
result.stats.triangle_count,
result.stats.material_count,
self.import_time_ms,
);
}
Err(e) => {
self.import_time_ms = import_start.elapsed().as_secs_f32() * 1000.0;
self.current_stats = None;
self.gpu_objects.clear();
log::error!("RT Gallery: import failed for {}: {}", self.current_model_name, e);
}
}
self.loading = false;
}
fn build_gallery_scene(
&self,
_result: &dreamwell_gpu::gltf::GltfImportResult,
) -> dreamwell_engine::game_object::GameObjectScene {
let mut scene = dreamwell_engine::game_object::GameObjectScene::new(
format!("RT Gallery: {}", self.current_model_name),
);
if let Ok(id) = scene.spawn_primitive(
"GalleryGround".into(),
dreamwell_engine::game_object::PrimitiveKind::Plane,
) {
if let Some(obj) = scene.find_mut(id) {
obj.transform.scale = [50.0, 1.0, 50.0];
obj.transform.position = [0.0, -0.01, 0.0]; }
}
scene
}
fn setup_gallery_lighting(fabric: &mut dreamwell_fabric::DreamFabric) {
fabric.gpu_scene_mut().scene_lights.add_directional(
dreamwell_engine::lighting::DirectionalLightDesc {
direction: scene::normalize([0.5, -0.7, 0.3]),
color: [1.0, 0.95, 0.88],
intensity_lux: 3.0,
},
);
fabric.gpu_scene_mut().scene_lights.add_directional(
dreamwell_engine::lighting::DirectionalLightDesc {
direction: scene::normalize([-0.3, -0.5, -0.4]),
color: [0.4, 0.5, 0.7],
intensity_lux: 0.8,
},
);
fabric.gpu_scene_mut().scene_lights.add_directional(
dreamwell_engine::lighting::DirectionalLightDesc {
direction: scene::normalize([-0.2, -0.3, -0.8]),
color: [0.8, 0.85, 1.0],
intensity_lux: 1.2,
},
);
let accent_lights = [
([0.0, 5.0, 0.0], [1.0, 0.98, 0.95], 400.0, 20.0), ([-5.0, 2.0, 3.0], [0.3, 0.5, 0.9], 200.0, 12.0), ([5.0, 2.0, 3.0], [0.9, 0.6, 0.2], 200.0, 12.0), ];
for (pos, color, lumens, range) in accent_lights {
fabric.gpu_scene_mut().scene_lights.add_point(
dreamwell_engine::lighting::PointLightDesc {
position: pos,
color,
intensity_lumens: lumens,
range,
},
);
}
fabric.gpu_scene_mut().default_roughness = 0.4;
fabric.gpu_scene_mut().default_metallic = 0.1;
}
fn navigate(&mut self, delta: isize) {
if self.model_paths.is_empty() { return; }
let count = self.model_paths.len() as isize;
self.current_index = ((self.current_index as isize + delta).rem_euclid(count)) as usize;
self.load_current_model();
}
fn render_gallery_ui(&self, ctx: &egui::Context) -> isize {
let mut nav_delta: isize = 0;
egui::Window::new("RT Gallery")
.collapsible(false)
.resizable(false)
.default_pos([10.0, 10.0])
.default_width(280.0)
.show(ctx, |ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.horizontal(|ui| {
if ui.button(" < ").clicked() {
nav_delta = -1;
}
ui.heading(&self.current_model_name);
if ui.button(" > ").clicked() {
nav_delta = 1;
}
});
if !self.model_paths.is_empty() {
ui.label(format!("{} / {}", self.current_index + 1, self.model_paths.len()));
}
ui.separator();
if self.loading {
ui.label("Loading...");
} else if let Some(ref stats) = self.current_stats {
ui.label(format!("Meshes: {}", stats.mesh_count));
ui.label(format!("Vertices: {}", stats.vertex_count));
ui.label(format!("Triangles: {}", stats.triangle_count));
ui.label(format!("Materials: {}", stats.material_count));
ui.label(format!("Textures: {}", stats.texture_count));
ui.label(format!("Nodes: {}", stats.node_count));
if stats.animation_count > 0 {
ui.label(format!("Animations: {}", stats.animation_count));
}
ui.separator();
ui.label(format!("Import: {:.1} ms", self.import_time_ms));
ui.label(format!("Objects: {}", self.gpu_objects.len()));
} else {
ui.label("No model loaded");
}
ui.separator();
ui.label("Controls:");
ui.label(" Left/Right or A/D: cycle models");
ui.label(" Right drag: orbit");
ui.label(" Scroll: zoom");
ui.label(" F2: toggle free camera");
ui.label(" Escape: quit");
});
egui::Window::new("Gallery HUD")
.collapsible(false)
.resizable(false)
.title_bar(false)
.anchor(egui::Align2::RIGHT_TOP, [-10.0, 10.0])
.default_width(260.0)
.show(ctx, |ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.label(format!("FPS: {:.0}", self.smoothed_fps));
ui.label(format!("Camera: {}",
if self.free_camera { "Free" } else { "Orbit" }));
ui.label(format!("RT: {}",
if self.rt_available { "On (GI + Shadows)" } else { "Off" }));
ui.label(&self.adapter_name_cached);
});
nav_delta
}
}
impl Drop for GalleryApp {
fn drop(&mut self) {
self.gpu_driven.destroy();
if let Some(mut gpu) = self.gpu.take() {
gpu.fabric.destroy();
}
}
}
impl ApplicationHandler for GalleryApp {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if self.window.is_some() { return; }
let attrs = Window::default_attributes()
.with_title("DreamMatter RT Gallery — Loading...")
.with_inner_size(winit::dpi::LogicalSize::new(GALLERY_WIDTH, GALLERY_HEIGHT))
.with_resizable(false);
let window = Arc::new(event_loop.create_window(attrs).expect("window"));
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(), ..wgpu::InstanceDescriptor::new_without_display_handle()
});
let surface = instance.create_surface(window.clone()).expect("surface");
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
compatible_surface: Some(&surface),
force_fallback_adapter: false,
})).expect("adapter");
let info = adapter.get_info();
self.adapter_name_cached = info.name.clone();
self.rt_available = adapter.features().contains(wgpu::Features::EXPERIMENTAL_RAY_QUERY);
let (features, limits, experimental) =
dreamwell_gpu::features::GpuCapabilities::device_descriptor_parts(&adapter);
let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
label: Some("rt_gallery"),
required_features: features,
required_limits: limits,
memory_hints: Default::default(),
experimental_features: experimental,
trace: Default::default(),
})).expect("device");
device.on_uncaptured_error(std::sync::Arc::new(|error| {
log::error!("gpu_error:{error}");
}));
let caps = surface.get_capabilities(&adapter);
let format = caps.formats.iter()
.find(|f| !f.is_srgb())
.or_else(|| caps.formats.first())
.copied()
.unwrap_or(wgpu::TextureFormat::Bgra8Unorm);
let size = window.inner_size();
let surface_config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format,
width: size.width.max(1),
height: size.height.max(1),
present_mode: wgpu::PresentMode::Fifo,
alpha_mode: caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &surface_config);
let multi_draw = device.features().contains(wgpu::Features::MULTI_DRAW_INDIRECT_COUNT);
let mut fabric = dreamwell_fabric::DreamFabric::new(&device, format, false);
fabric.resize(&device, surface_config.width, surface_config.height);
fabric.init_ibl(&device, &queue);
fabric.gpu_scene_mut().ensure_pbr_pipelines(&device, &queue, dreamwell_gpu::post::HDR_FORMAT);
fabric.gpu_scene_mut().set_multi_draw_support(multi_draw);
fabric.set_ssao_enabled(true);
fabric.set_taa_enabled(true);
fabric.set_ssr_enabled(true);
fabric.set_dof_enabled(false);
fabric.set_ssgi_enabled(true);
Self::setup_gallery_lighting(&mut fabric);
fabric.init_debug_vis(&device, surface_config.width, surface_config.height);
let ground_scene = dreamwell_engine::game_object::GameObjectScene::new("RT Gallery".to_string());
fabric.upload_scene(&device, &ground_scene);
fabric.gpu_scene_mut().upload_pbr_lights(&device, &queue);
let initial_capacity = 1024u32;
self.gpu_driven.init(&device, initial_capacity);
let mut rt_ctx = dreamwell_gpu::ray_tracing::RayTracingContext::default();
if self.rt_available {
fabric.enable_ray_tracing();
rt_ctx = dreamwell_gpu::ray_tracing::RayTracingContext::init_if_supported(
&device, device.features(),
);
if rt_ctx.enabled {
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("gallery_rt_init"),
});
for shape in dreamwell_gpu::primitives::PrimitiveShape::ALL {
let mesh = shape.generate(dreamwell_gpu::primitives::DEFAULT_PRIMITIVE_COLOR);
let vertex_data: Vec<[f32; 3]> = mesh.vertices.iter().map(|v| v.position).collect();
let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(shape.name()),
contents: bytemuck::cast_slice(&vertex_data),
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT,
});
let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(shape.name()),
contents: bytemuck::cast_slice(&mesh.indices),
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::BLAS_INPUT,
});
rt_ctx.register_primitive_blas(
&device, &mut encoder, shape.name(),
&vertex_buf, vertex_data.len() as u32,
std::mem::size_of::<[f32; 3]>() as u64,
Some(&index_buf), Some(mesh.indices.len() as u32),
);
}
queue.submit(std::iter::once(encoder.finish()));
if let Some(tlas) = rt_ctx.tlas() {
fabric.init_rt(&device, surface_config.width, surface_config.height, tlas);
}
log::info!("RT Gallery: {} BLAS + GI + shadows enabled",
dreamwell_gpu::primitives::PrimitiveShape::ALL.len());
}
} else {
log::warn!("RT Gallery: ray tracing not available on this adapter, rendering without RT");
}
if let Some(quality) = self.quality_preset {
fabric.set_dream_lighting_quality(quality);
log::info!("Gallery: Dream Lighting {:?} preset applied", quality);
}
let depth_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("gallery_depth"),
size: wgpu::Extent3d {
width: surface_config.width,
height: surface_config.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: dreamwell_gpu::formats::DEPTH_FORMAT,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
let egui_ctx = egui::Context::default();
let egui_winit = egui_winit::State::new(
egui_ctx.clone(),
egui_ctx.viewport_id(),
&window,
Some(window.scale_factor() as f32),
None,
None,
);
let egui_renderer = egui_wgpu::Renderer::new(
&device,
format,
egui_wgpu::RendererOptions::default(),
);
egui_ctx.set_visuals(egui::Visuals::dark());
log::info!("RT Gallery ready: {} | RT: {} | {}x{}",
info.name,
if rt_ctx.enabled { "On" } else { "Off" },
surface_config.width, surface_config.height);
let rt_hdr_fallback = device.create_texture(&wgpu::TextureDescriptor {
label: Some("rt_hdr_fallback"),
size: wgpu::Extent3d { width: 1, height: 1, depth_or_array_layers: 1 },
mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba16Float,
usage: wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let rt_hdr_fallback_view = rt_hdr_fallback.create_view(&wgpu::TextureViewDescriptor::default());
self.gpu = Some(GpuState {
surface, device, queue, surface_config, fabric,
depth_texture, depth_view,
adapter_name: info.name.clone(),
adapter_backend: format!("{:?}", info.backend),
rt_ctx,
rt_hdr_fallback, rt_hdr_fallback_view,
egui_ctx, egui_winit, egui_renderer,
});
self.window = Some(window);
self.frame_timer = Instant::now();
self.load_current_model();
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: winit::window::WindowId, event: WindowEvent) {
if let (Some(ref mut gpu), Some(ref window)) = (&mut self.gpu, &self.window) {
let response = gpu.egui_winit.on_window_event(window, &event);
if response.consumed { return; }
}
match event {
WindowEvent::CloseRequested => {
if let Some(mut gpu) = self.gpu.take() { gpu.fabric.destroy(); }
event_loop.exit();
}
WindowEvent::CursorMoved { position, .. } => {
let new_pos = [position.x as f32, position.y as f32];
if self.right_mouse_held {
let dx = new_pos[0] - self.last_mouse_pos[0];
let dy = new_pos[1] - self.last_mouse_pos[1];
self.cam_yaw += dx * 0.003;
self.cam_pitch = (self.cam_pitch - dy * 0.003).clamp(-1.4, 1.4);
}
self.last_mouse_pos = new_pos;
}
WindowEvent::MouseInput { state, button, .. } => {
let pressed = state == winit::event::ElementState::Pressed;
if button == MouseButton::Right {
self.right_mouse_held = pressed;
}
}
WindowEvent::MouseWheel { delta, .. } => {
let scroll = match delta {
winit::event::MouseScrollDelta::LineDelta(_, y) => y,
winit::event::MouseScrollDelta::PixelDelta(p) => p.y as f32 * 0.01,
};
if !self.free_camera {
self.cam_distance = (self.cam_distance - scroll * self.cam_distance * 0.1)
.clamp(0.1, 500.0);
}
}
WindowEvent::KeyboardInput { event, .. } => {
if let PhysicalKey::Code(code) = event.physical_key {
if event.state == winit::event::ElementState::Pressed {
self.keys_held.insert(code);
match code {
KeyCode::ArrowLeft | KeyCode::KeyA if !self.free_camera => {
self.navigate(-1);
}
KeyCode::ArrowRight | KeyCode::KeyD if !self.free_camera => {
self.navigate(1);
}
KeyCode::F2 => {
self.free_camera = !self.free_camera;
if self.free_camera {
self.cam_pos = self.build_camera().position;
log::info!("RT Gallery: free camera (WASD + right-drag)");
} else {
log::info!("RT Gallery: orbit camera");
}
}
KeyCode::F6 => {
if let Some(ref mut gpu) = self.gpu {
gpu.fabric.cycle_debug_vis();
log::info!("DreamNet: {}", gpu.fabric.debug_vis_mode().label());
}
}
KeyCode::Escape => {
if let Some(mut gpu) = self.gpu.take() { gpu.fabric.destroy(); }
event_loop.exit();
}
_ => {}
}
} else {
self.keys_held.remove(&code);
}
}
}
WindowEvent::RedrawRequested => {
let raw_dt = self.frame_timer.elapsed().as_secs_f32();
let dt = raw_dt.clamp(0.0001, 0.1);
self.frame_timer = Instant::now();
let fps = if dt > 0.0 { 1.0 / dt } else { 0.0 };
self.smoothed_fps = if self.smoothed_fps == 0.0 { fps }
else { self.smoothed_fps * 0.95 + fps * 0.05 };
self.update_free_camera(dt);
let camera = self.build_camera();
let mut render_result: Option<(
wgpu::SurfaceTexture,
wgpu::TextureView,
wgpu::CommandBuffer,
)> = None;
if let Some(gpu) = &mut self.gpu {
let output = match gpu.surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(t)
| wgpu::CurrentSurfaceTexture::Suboptimal(t) => t,
wgpu::CurrentSurfaceTexture::Lost
| wgpu::CurrentSurfaceTexture::Outdated => {
gpu.surface.configure(&gpu.device, &gpu.surface_config);
return;
}
_ => return,
};
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = gpu.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("gallery_render"),
});
if gpu.fabric.dreamlet_catalog().count > 0 {
let w = gpu.surface_config.width as f32;
let h = gpu.surface_config.height as f32;
gpu.fabric.dreamlet_catalog().dispatch_cull(
&gpu.device, &gpu.queue, &mut encoder,
&camera, w, h, 200.0, 1.0,
);
}
if gpu.fabric.rt_enabled() {
if let Some(tlas) = gpu.rt_ctx.tlas() {
gpu.fabric.dispatch_rt(
&gpu.device, &gpu.queue, &mut encoder, tlas,
&gpu.depth_view, &gpu.rt_hdr_fallback_view,
&camera,
[0.5, -0.7, 0.3], 3.0, );
}
}
let fc = gpu.fabric.begin_frame(
&gpu.queue, dt, camera.position,
dreamwell_engine::TopologyLayer::Area,
);
gpu.fabric.end_frame(
&gpu.device, &gpu.queue, &mut encoder,
&view, &gpu.depth_view, &camera, &fc, None,
);
let render_cmds = encoder.finish();
render_result = Some((output, view, render_cmds));
}
if let Some((output, view, render_cmds)) = render_result {
let egui_ctx = self.gpu.as_ref().unwrap().egui_ctx.clone();
let raw_input = self.gpu.as_mut().unwrap().egui_winit
.take_egui_input(self.window.as_ref().unwrap());
egui_ctx.begin_pass(raw_input);
let nav_delta = self.render_gallery_ui(&egui_ctx);
let full_output = egui_ctx.end_pass();
if nav_delta != 0 {
self.navigate(nav_delta);
}
let clipped_primitives = egui_ctx.tessellate(
full_output.shapes, full_output.pixels_per_point,
);
let gpu = self.gpu.as_mut().unwrap();
let screen_desc = egui_wgpu::ScreenDescriptor {
size_in_pixels: [gpu.surface_config.width, gpu.surface_config.height],
pixels_per_point: full_output.pixels_per_point,
};
for (id, image_delta) in &full_output.textures_delta.set {
gpu.egui_renderer.update_texture(&gpu.device, &gpu.queue, *id, image_delta);
}
let mut egui_encoder = gpu.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("gallery_egui"),
});
gpu.egui_renderer.update_buffers(
&gpu.device, &gpu.queue, &mut egui_encoder,
&clipped_primitives, &screen_desc,
);
let encoder_ptr = Box::into_raw(Box::new(egui_encoder));
{
let encoder_ref: &'static mut wgpu::CommandEncoder = unsafe { &mut *encoder_ptr };
let mut pass = encoder_ref.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("gallery_egui_pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
gpu.egui_renderer.render(&mut pass, &clipped_primitives, &screen_desc);
}
for id in &full_output.textures_delta.free {
gpu.egui_renderer.free_texture(id);
}
let egui_encoder = unsafe { Box::from_raw(encoder_ptr) };
let egui_cmds = egui_encoder.finish();
let mut submissions: Vec<wgpu::CommandBuffer> = Vec::with_capacity(2);
submissions.push(render_cmds);
submissions.push(egui_cmds);
gpu.queue.submit(submissions);
output.present();
gpu.egui_winit.handle_platform_output(
self.window.as_ref().unwrap(),
full_output.platform_output,
);
}
if let Some(ref window) = self.window {
let cam = if self.free_camera { "Free" } else { "Orbit" };
let rt = if self.rt_available { "RT" } else { "Raster" };
window.set_title(&format!(
"DreamMatter RT Gallery | {} | {:.0} FPS | {} | {}",
self.current_model_name, self.smoothed_fps, cam, rt,
));
}
if let Some(ref window) = self.window { window.request_redraw(); }
}
_ => {}
}
}
}