Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,21 @@ jobs:
install_lavapipe: true
cache: true

# One test at a time, as the GLES step below already does. Each of these
# stands up its own GPU context, and the ray traced ones also build
# acceleration structures and full sized render targets. Ten of those at
# once on a shared runner, where the "GPU" is a software rasterizer
# competing for the same few cores, stretches the heaviest tests by far
# more than the work in them costs.
- name: Run GPU integration tests (Linux)
if: matrix.name == 'Linux'
run: cargo test --test gpu_examples -- --ignored --nocapture
run: cargo test --test gpu_examples -- --ignored --nocapture --test-threads=1
env:
VK_ICD_FILENAMES: /usr/share/vulkan/icd.d/lvp_icd.json

- name: Run GPU integration tests
if: matrix.name != 'Linux'
run: cargo test --test gpu_examples -- --ignored --nocapture
run: cargo test --test gpu_examples -- --ignored --nocapture --test-threads=1

- name: Install EGL/GLES (Linux)
if: matrix.name == 'Linux'
Expand Down
66 changes: 50 additions & 16 deletions blade-engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ enum Renderer {
inner: blade_render::RayTracer,
frame_config: blade_render::FrameConfig,
ray_config: blade_render::RayConfig,
mode: blade_render::RenderMode,
denoiser_enabled: bool,
denoiser_config: blade_render::DenoiserConfig,
post_proc_config: blade_render::PostProcConfig,
Expand Down Expand Up @@ -544,7 +545,9 @@ impl Engine {
};
let gpu_context = Arc::new(unsafe { gpu::Context::init(context_desc).unwrap() });

let (surface_size, surface_info, target_surface) = match presentation {
// Note: the color space we ask the surface for is also the one
// the renderers have to produce, see `RenderConfig`.
let (surface_size, surface_info, color_space, target_surface) = match presentation {
#[cfg(not(target_os = "android"))]
Presentation::Window(window) => {
let surface_config = Self::make_surface_config(window.inner_size());
Expand All @@ -556,6 +559,7 @@ impl Engine {
(
surface_size,
surface_info,
surface_config.color_space,
TargetSurface::Window(gpu_surface),
)
}
Expand All @@ -565,15 +569,22 @@ impl Engine {
panic!("XR presentation is only supported on Android");
#[cfg(target_os = "android")]
{
let surface_config = gpu_context
.xr_recommended_surface_config(
openxr::ViewConfigurationType::PRIMARY_STEREO,
)
.expect("Unable to query the XR surface configuration");
let xr_surface = gpu_context
.create_xr_surface()
.create_xr_surface_configured(surface_config)
.expect("Unable to create XR surface from GPU context");
let surface_size = xr_surface.extent();
let surface_info = gpu::SurfaceInfo {
format: xr_surface.format(),
alpha: gpu::AlphaMode::Ignored,
};
(surface_size, surface_info, TargetSurface::Xr(xr_surface))
let surface_info = xr_surface.info();
(
surface_size,
surface_info,
surface_config.color_space,
TargetSurface::Xr(xr_surface),
)
}
}
#[cfg(target_os = "android")]
Expand Down Expand Up @@ -603,6 +614,7 @@ impl Engine {
let render_config = blade_render::RenderConfig {
surface_size,
surface_info,
color_space,
max_debug_lines: 1 << 14,
};
let renderer = match config.render_backend {
Expand All @@ -619,8 +631,10 @@ impl Engine {
debug_draw: true,
reset_variance: false,
reset_reservoirs: true,
reset_accumulation: true,
},
ray_config: blade_helpers::default_ray_config(),
mode: blade_render::RenderMode::default(),
denoiser_enabled: true,
denoiser_config: blade_render::DenoiserConfig {
num_passes: 4,
Expand All @@ -630,6 +644,7 @@ impl Engine {
average_luminocity: 0.5,
exposure_key_value: 1.0 / 9.6,
white_level: 1.0,
tone_map: true,
},
},
config::RenderBackend::Rasterizer => Renderer::Rasterizer {
Expand Down Expand Up @@ -876,6 +891,7 @@ impl Engine {
ref mut ray_config,
ref mut denoiser_enabled,
ref mut denoiser_config,
mode,
..
} = self.renderer
{
Expand All @@ -899,12 +915,16 @@ impl Engine {
*frame_config,
);
frame_config.reset_reservoirs = false;
frame_config.reset_accumulation = false;

if !self.render_objects.is_empty() {
inner.ray_trace(command_encoder, self.debug, *ray_config);
if *denoiser_enabled {
inner.denoise(command_encoder, *denoiser_config);
}
inner.render(
command_encoder,
mode,
self.debug,
*ray_config,
denoiser_enabled.then_some(*denoiser_config),
);
}
}
}
Expand Down Expand Up @@ -1135,14 +1155,18 @@ impl Engine {
command_encoder.init_texture(frame.texture());

match self.renderer {
//Note: the canonical renderer is of no use in a headset,
// so XR always takes the real-time path.
Renderer::RayTracer {
ref mut inner,
ray_config,
ref mut frame_config,
denoiser_enabled,
denoiser_config,
post_proc_config,
..
} => {
let mode = blade_render::RenderMode::RealTime;
if can_render {
inner.build_scene(
command_encoder,
Expand Down Expand Up @@ -1178,11 +1202,15 @@ impl Engine {
};
inner.prepare(command_encoder, &render_camera, *frame_config);
frame_config.reset_reservoirs = false;
frame_config.reset_accumulation = false;
if !self.render_objects.is_empty() {
inner.ray_trace(command_encoder, self.debug, ray_config);
if denoiser_enabled {
inner.denoise(command_encoder, denoiser_config);
}
inner.render(
command_encoder,
mode,
self.debug,
ray_config,
denoiser_enabled.then_some(denoiser_config),
);
}
if let mut pass = command_encoder.render(
"xr-draw",
Expand Down Expand Up @@ -1433,15 +1461,21 @@ impl Engine {
.default_open(false)
.show(ui, |ui| match self.renderer {
Renderer::RayTracer {
ref mut mode,
ref mut ray_config,
ref mut denoiser_enabled,
ref mut denoiser_config,
ref mut post_proc_config,
ref mut frame_config,
..
} => {
if blade_helpers::populate_render_mode(mode, ui) {
frame_config.reset_accumulation = true;
}
ray_config.populate_hud(ui);
frame_config.reset_reservoirs |= ui.button("Reset Accumulation").clicked();
let reset = ui.button("Reset Accumulation").clicked();
frame_config.reset_reservoirs |= reset;
frame_config.reset_accumulation |= reset;
ui.checkbox(denoiser_enabled, "Enable Denoiser");
denoiser_config.populate_hud(ui);
post_proc_config.populate_hud(ui);
Expand Down
9 changes: 5 additions & 4 deletions blade-graphics/src/metal/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,12 @@ impl super::Context {

pub fn reconfigure_surface(&self, surface: &mut super::Surface, config: crate::SurfaceConfig) {
let device = self.device.lock().unwrap();
let format = match config.color_space {
crate::ColorSpace::Linear => crate::TextureFormat::Bgra8UnormSrgb,
crate::ColorSpace::Srgb => crate::TextureFormat::Bgra8Unorm,
};
surface.info = crate::SurfaceInfo {
format: match config.color_space {
crate::ColorSpace::Linear => crate::TextureFormat::Bgra8UnormSrgb,
crate::ColorSpace::Srgb => crate::TextureFormat::Bgra8Unorm,
},
format,
alpha: if config.transparent {
crate::AlphaMode::PostMultiplied
} else {
Expand Down
23 changes: 18 additions & 5 deletions blade-graphics/src/vulkan/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,13 @@ impl super::XrSurface {
self.swapchain.format
}

pub fn info(&self) -> crate::SurfaceInfo {
crate::SurfaceInfo {
format: self.swapchain.format,
alpha: self.swapchain.alpha,
}
}

pub fn swapchain(&self) -> &xr::Swapchain<xr::Vulkan> {
&self.raw
}
Expand Down Expand Up @@ -528,7 +535,8 @@ impl super::Context {
};
}

fn xr_recommended_surface_config(
/// Surface configuration matching what the XR runtime recommends.
pub fn xr_recommended_surface_config(
&self,
view_type: xr::ViewConfigurationType,
) -> Option<crate::XrSurfaceConfig> {
Expand All @@ -547,7 +555,9 @@ impl super::Context {
depth: 1,
},
usage: crate::TextureUsage::TARGET,
color_space: crate::ColorSpace::Linear,
//Note: a plain swapchain format is the one XR runtimes are
// happiest with, and it means we do the encoding ourselves.
color_space: crate::ColorSpace::Srgb,
view_count,
})
}
Expand All @@ -558,7 +568,7 @@ impl super::Context {
self.create_xr_surface_configured(config)
}

fn create_xr_surface_configured(
pub fn create_xr_surface_configured(
&self,
config: crate::XrSurfaceConfig,
) -> Option<super::XrSurface> {
Expand Down Expand Up @@ -783,9 +793,12 @@ fn select_xr_swapchain_format(
}
}
}
// Unlike a window surface, an XR swapchain can't declare a color space:
// the runtime linearizes the sRGB formats and passes the plain ones
// through to the compositor. So the format is what honors the request.
match color_space {
crate::ColorSpace::Linear => linear_candidate.or(srgb_candidate),
crate::ColorSpace::Srgb => srgb_candidate.or(linear_candidate),
crate::ColorSpace::Linear => srgb_candidate.or(linear_candidate),
crate::ColorSpace::Srgb => linear_candidate.or(srgb_candidate),
}
.expect("No compatible XR swapchain format available")
}
56 changes: 41 additions & 15 deletions blade-helpers/src/hud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,25 @@ pub trait ExposeHud {
impl ExposeHud for blade_render::RayConfig {
fn populate_hud(&mut self, ui: &mut egui::Ui) {
ui.add(
egui::Slider::new(&mut self.num_environment_samples, 1..=100u32)
egui::Slider::new(&mut self.num_environment_samples, 0..=100u32)
.text("Num env samples")
.logarithmic(true),
);
ui.add(
egui::Slider::new(&mut self.num_brdf_samples, 0..=100u32)
.text("Num BRDF samples")
.logarithmic(true),
);
ui.checkbox(
&mut self.environment_importance_sampling,
"Env importance sampling",
);
ui.add(egui::widgets::Slider::new(&mut self.max_bounces, 0..=16).text("Max bounces"));
ui.add(
egui::Slider::new(&mut self.max_accumulated_samples, 0..=4096u32)
.text("Accumulation limit")
.logarithmic(true),
);
ui.add(egui::widgets::Slider::new(&mut self.tap_count, 0..=10).text("Tap count"));
ui.add(egui::widgets::Slider::new(&mut self.tap_radius, 1..=50).text("Tap radius (px)"));
ui.add(
Expand Down Expand Up @@ -43,17 +54,20 @@ impl ExposeHud for blade_render::DenoiserConfig {

impl ExposeHud for blade_render::PostProcConfig {
fn populate_hud(&mut self, ui: &mut egui::Ui) {
ui.add(
egui::Slider::new(&mut self.average_luminocity, 0.1f32..=1_000f32)
.text("Average luminocity")
.logarithmic(true),
);
ui.add(
egui::Slider::new(&mut self.exposure_key_value, 0.01f32..=10f32)
.text("Key value")
.logarithmic(true),
);
ui.add(egui::Slider::new(&mut self.white_level, 0.1f32..=2f32).text("White level"));
ui.checkbox(&mut self.tone_map, "Tone map");
ui.add_enabled_ui(self.tone_map, |ui| {
ui.add(
egui::Slider::new(&mut self.average_luminocity, 0.1f32..=1_000f32)
.text("Average luminocity")
.logarithmic(true),
);
ui.add(
egui::Slider::new(&mut self.exposure_key_value, 0.01f32..=10f32)
.text("Key value")
.logarithmic(true),
);
ui.add(egui::Slider::new(&mut self.white_level, 0.1f32..=2f32).text("White level"));
});
}
}

Expand Down Expand Up @@ -82,9 +96,6 @@ impl ExposeHud for blade_render::RasterConfig {
});
});

ui.add(egui::Slider::new(&mut self.roughness, 0.0..=1.0).text("Roughness"));
ui.add(egui::Slider::new(&mut self.metallic, 0.0..=1.0).text("Metallic"));

ui.label("Light direction");
ui.horizontal(|ui| {
ui.add(egui::DragValue::new(&mut self.light_dir.x).speed(0.05));
Expand Down Expand Up @@ -145,6 +156,21 @@ impl ExposeHud for blade_render::DebugConfig {
}
}

/// Pick the mode of the ray tracer, returning true when it changes.
pub fn populate_render_mode(mode: &mut blade_render::RenderMode, ui: &mut egui::Ui) -> bool {
use strum::IntoEnumIterator as _;

let old = *mode;
egui::ComboBox::from_label("Mode")
.selected_text(format!("{mode:?}"))
.show_ui(ui, |ui| {
for value in blade_render::RenderMode::iter() {
ui.selectable_value(mode, value, format!("{value:?}"));
}
});
*mode != old
}

pub fn populate_debug_selection(
mouse_pos: &mut Option<[i32; 2]>,
selection: &blade_render::SelectionInfo,
Expand Down
5 changes: 4 additions & 1 deletion blade-helpers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ mod hud;

pub use blade_render::Camera;
pub use camera::ControlledCamera;
pub use hud::{ExposeHud, populate_debug_selection};
pub use hud::{ExposeHud, populate_debug_selection, populate_render_mode};

pub fn default_ray_config() -> blade_render::RayConfig {
blade_render::RayConfig {
num_environment_samples: 1,
num_brdf_samples: 1,
environment_importance_sampling: true,
max_bounces: 3,
max_accumulated_samples: 0,
tap_count: 2,
tap_radius: 20,
tap_confidence_near: 15,
Expand Down
6 changes: 5 additions & 1 deletion blade-render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ blade-macros = { workspace = true }
bytemuck = { workspace = true }
choir = { workspace = true }
exr = { version = "1.6", optional = true }
gltf = { workspace = true, features = ["names", "utils"], optional = true }
gltf = { workspace = true, features = [
"names",
"utils",
"KHR_materials_emissive_strength",
], optional = true }
glam = { workspace = true }
log = { workspace = true }
mikktspace = { package = "bevy_mikktspace", version = "0.15.0-rc.3", optional = true }
Expand Down
Loading
Loading