use super::*;
use crate::AcquireError;
use gpui::{
Bounds, ContentMask, CustomContent, CustomDraw, DevicePixels, PlatformHeadlessRenderer,
ScaledPixels, Scene, Size, point, size,
};
use gpui_windows::Dx11HeadlessRenderer;
use windows::Win32::Graphics::Dxgi::IDXGIAdapter1;
fn report(line: impl std::fmt::Display) {
eprintln!("windows engine surface: {line}");
}
fn setup() -> Option<(wgpu::Device, wgpu::Queue, Dx11HeadlessRenderer)> {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::DX12,
flags: wgpu::InstanceFlags::default(),
backend_options: wgpu::BackendOptions::default(),
memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(),
display: None,
});
let adapters = gpui::block_on(instance.enumerate_adapters(wgpu::Backends::DX12));
let Some(adapter) = adapters
.into_iter()
.find(|adapter| adapter.get_info().device_type != wgpu::DeviceType::Cpu)
else {
report("skipped: no hardware D3D12 adapter");
return None;
};
report(format!("adapter {:?}", adapter.get_info().name));
let dxgi: IDXGIAdapter1 = unsafe { adapter.as_hal::<Dx12>() }?.as_raw().cast().ok()?;
let host = match Dx11HeadlessRenderer::with_adapter(&dxgi) {
Ok(host) => host,
Err(error) => {
report(format!(
"skipped: hardware D3D11 renderer unavailable ({error})"
));
return None;
}
};
let (device, queue) =
gpui::block_on(adapter.request_device(&wgpu::DeviceDescriptor::default())).ok()?;
Some((device, queue, host))
}
fn desc(format: SurfaceFormat, slots: usize) -> SurfaceDesc {
SurfaceDesc {
width: 16,
height: 8,
format,
slots,
}
}
fn draw(target: &mut WgpuSurfaceTarget, color: wgpu::Color) {
let slot = target.surface().acquire().expect("free slot");
let texture = target.texture(&slot).unwrap();
let view = texture.create_view(&Default::default());
let mut encoder = target.device.create_command_encoder(&Default::default());
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("engine_clear"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(color),
store: wgpu::StoreOp::Store,
},
})],
..Default::default()
});
target.present(slot, [encoder.finish()]).unwrap();
}
fn scene(content: Arc<dyn CustomContent>) -> Scene {
let bounds = Bounds::new(
point(ScaledPixels(0.), ScaledPixels(0.)),
size(ScaledPixels(32.), ScaledPixels(16.)),
);
let mut scene = Scene::default();
scene.insert_primitive(CustomDraw {
order: 0,
bounds,
content_mask: ContentMask { bounds },
corner_radii: Default::default(),
opacity: 1.,
content,
coverage: None,
});
scene.finish();
scene
}
fn viewport() -> Size<DevicePixels> {
size(DevicePixels(32), DevicePixels(16))
}
fn near(actual: [u8; 4], expected: [u8; 4]) -> bool {
actual.iter().zip(expected).all(|(a, e)| a.abs_diff(e) <= 3)
}
#[::core::prelude::v1::test]
fn windows_bgra_frame_composites_scaled_without_copy() {
let Some((device, queue, mut renderer)) = setup() else {
return;
};
let mut target =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Bgra8Srgb, 3), &device, &queue).unwrap();
draw(
&mut target,
wgpu::Color {
r: 0.2,
g: 0.6,
b: 0.9,
a: 1.,
},
);
let image = renderer
.render_scene_to_image(&scene(target.surface().content().unwrap()), viewport())
.unwrap();
for (x, y) in [(1, 1), (16, 8), (30, 14)] {
let pixel = image.get_pixel(x, y).0;
assert!(near(pixel, [51, 153, 230, 255]), "({x},{y}) = {pixel:?}");
}
report("PASS bgra frame composited with exact pixels");
}
#[::core::prelude::v1::test]
fn windows_wide_frame_composites_in_wide_mode() {
let Some((device, queue, mut renderer)) = setup() else {
return;
};
if let Err(error) = renderer.set_color_mode(gpui::WindowColorMode::WideGamutSdr) {
report(format!("skipped: wide mode unavailable ({error})"));
return;
}
let mut target =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Rgba16FloatLinearP3, 2), &device, &queue)
.unwrap();
draw(
&mut target,
wgpu::Color {
r: 0.5,
g: 0.5,
b: 0.5,
a: 1.,
},
);
let image = renderer
.render_scene_to_image(&scene(target.surface().content().unwrap()), viewport())
.unwrap();
let pixel = image.get_pixel(16, 8).0;
assert!(near(pixel, [188, 188, 188, 255]), "{pixel:?}");
report("PASS linear P3 RGBA16F frame composited in wide mode");
}
#[::core::prelude::v1::test]
fn windows_retained_snapshot_keeps_its_slot_immutable() {
let Some((device, queue, mut renderer)) = setup() else {
return;
};
let mut target =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Bgra8Srgb, 2), &device, &queue).unwrap();
draw(&mut target, wgpu::Color::RED);
let old_scene = scene(target.surface().content().unwrap());
draw(&mut target, wgpu::Color::GREEN);
assert_eq!(
target.surface().acquire().err(),
Some(AcquireError::WouldBlock)
);
let replay = renderer
.render_scene_to_image(&old_scene, viewport())
.unwrap();
assert!(near(replay.get_pixel(16, 8).0, [255, 0, 0, 255]));
drop(old_scene);
let mut released = false;
for _ in 0..50 {
let latest = renderer
.render_scene_to_image(&scene(target.surface().content().unwrap()), viewport())
.unwrap();
assert!(near(latest.get_pixel(16, 8).0, [0, 255, 0, 255]));
if target.surface().acquire().is_ok() {
released = true;
break;
}
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert!(released, "slot never returned");
report("PASS replayed snapshot kept its frame; slot returned afterwards");
}
#[::core::prelude::v1::test]
fn windows_repeated_frames_cycle_the_ring() {
let Some((device, queue, mut renderer)) = setup() else {
return;
};
let mut target =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Bgra8Srgb, 3), &device, &queue).unwrap();
for frame in 0..12 {
let (color, expected) = match frame % 3 {
0 => (wgpu::Color::RED, [255, 0, 0, 255]),
1 => (wgpu::Color::GREEN, [0, 255, 0, 255]),
_ => (wgpu::Color::BLUE, [0, 0, 255, 255]),
};
let mut tries = 0;
while target.surface().stats().free == 0 && tries < 50 {
renderer
.render_scene_to_image(&scene(target.surface().content().unwrap()), viewport())
.unwrap();
tries += 1;
}
draw(&mut target, color);
let image = renderer
.render_scene_to_image(&scene(target.surface().content().unwrap()), viewport())
.unwrap();
let pixel = image.get_pixel(16, 8).0;
assert!(near(pixel, expected), "frame {frame}: {pixel:?}");
}
assert_eq!(target.surface().stats().presents, 12);
report("PASS 12 frames through a 3-slot ring, each composited correctly");
}
#[::core::prelude::v1::test]
fn windows_present_from_engine_thread() {
let Some((device, queue, mut renderer)) = setup() else {
return;
};
let target =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Bgra8Srgb, 3), &device, &queue).unwrap();
let surface = target.surface().clone();
let target = std::thread::spawn(move || {
let mut target = target;
draw(&mut target, wgpu::Color::BLUE);
target
})
.join()
.unwrap();
let image = renderer
.render_scene_to_image(&scene(surface.content().unwrap()), viewport())
.unwrap();
assert!(near(image.get_pixel(16, 8).0, [0, 0, 255, 255]));
drop(target);
report("PASS frame presented from an engine thread");
}
#[::core::prelude::v1::test]
fn windows_gpu_side_wait_holds_composition_until_engine_signals() {
let Some((device, queue, mut renderer)) = setup() else {
return;
};
let mut target =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Bgra8Srgb, 3), &device, &queue).unwrap();
if !target.gpu_sync() {
report("skipped: no shared engine fence (CPU-wait mode)");
return;
}
draw(&mut target, wgpu::Color::RED);
renderer
.render_scene_to_image(&scene(target.surface().content().unwrap()), viewport())
.unwrap();
let raw: windows::Win32::Graphics::Direct3D12::ID3D12Device =
unsafe { device.as_hal::<Dx12>() }
.unwrap()
.raw_device()
.clone();
let gate: ID3D12Fence = unsafe {
raw.CreateFence(
0,
windows::Win32::Graphics::Direct3D12::D3D12_FENCE_FLAG_NONE,
)
}
.unwrap();
unsafe { queue.as_hal::<Dx12>() }
.unwrap()
.add_wait_fence(gate.clone(), 1);
draw(&mut target, wgpu::Color::GREEN);
let content = target.surface().content().unwrap();
struct Gate(ID3D12Fence);
unsafe impl Send for Gate {}
let opener_gate = Gate(gate.clone());
let started = std::time::Instant::now();
let opener = std::thread::spawn(move || {
let gate = opener_gate;
std::thread::sleep(std::time::Duration::from_millis(300));
unsafe { gate.0.Signal(1) }.unwrap();
});
let image = renderer
.render_scene_to_image(&scene(content), viewport())
.unwrap();
let elapsed = started.elapsed();
opener.join().unwrap();
let pixel = image.get_pixel(16, 8).0;
report(format!(
"gated frame composited after {elapsed:?}: {pixel:?} (expect green, >= 300 ms)"
));
assert!(
near(pixel, [0, 255, 0, 255]),
"composited stale content: {pixel:?}"
);
assert!(
elapsed >= std::time::Duration::from_millis(250),
"composition did not wait for the engine ({elapsed:?})"
);
report("PASS compositor waited on the GPU for the engine's fence");
}
#[::core::prelude::v1::test]
fn windows_reports_sync_mode() {
let Some((device, queue, _renderer)) = setup() else {
return;
};
let target =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Bgra8Srgb, 2), &device, &queue).unwrap();
report(format!(
"sync mode: {}",
if target.gpu_sync() {
"GPU-side wait (shared fence)"
} else {
"engine waits on CPU"
}
));
}
#[::core::prelude::v1::test]
fn windows_idle_compositor_releases_slots_without_frames() {
let Some((device, queue, mut renderer)) = setup() else {
return;
};
let mut target =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Bgra8Srgb, 2), &device, &queue).unwrap();
draw(&mut target, wgpu::Color::RED);
let frame = scene(target.surface().content().unwrap());
renderer.render_scene(&frame, viewport()).unwrap();
drop(frame);
draw(&mut target, wgpu::Color::GREEN);
let started = std::time::Instant::now();
let slot = loop {
if let Ok(slot) = target.surface().acquire() {
break slot;
}
assert!(
started.elapsed() < std::time::Duration::from_secs(2),
"slot never returned while the compositor was idle"
);
std::thread::sleep(std::time::Duration::from_millis(5));
};
drop(slot);
report(format!(
"PASS slot returned after {:?} with no further compositor frames",
started.elapsed()
));
}
#[::core::prelude::v1::test]
fn windows_engine_device_loss_keeps_last_frame_and_recovers() {
let Some((device, queue, mut renderer)) = setup() else {
return;
};
let mut target =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Bgra8Srgb, 2), &device, &queue).unwrap();
draw(&mut target, wgpu::Color::RED);
let surface = target.surface().clone();
let image = renderer
.render_scene_to_image(&scene(surface.content().unwrap()), viewport())
.unwrap();
assert!(near(image.get_pixel(16, 8).0, [255, 0, 0, 255]));
let pending = surface.acquire().unwrap();
device.destroy();
surface.invalidate();
assert_eq!(surface.acquire().err(), Some(AcquireError::Invalidated));
assert!(surface.present(pending, SyncPoint::completed()).is_err());
drop(target);
drop(queue);
drop(device);
let image = renderer
.render_scene_to_image(&scene(surface.content().unwrap()), viewport())
.unwrap();
assert!(
near(image.get_pixel(16, 8).0, [255, 0, 0, 255]),
"last frame must survive the engine device"
);
let Some((device, queue, _)) = setup() else {
return;
};
let mut replacement =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Bgra8Srgb, 2), &device, &queue).unwrap();
draw(&mut replacement, wgpu::Color::GREEN);
let image = renderer
.render_scene_to_image(&scene(replacement.surface().content().unwrap()), viewport())
.unwrap();
assert!(near(image.get_pixel(16, 8).0, [0, 255, 0, 255]));
report("PASS engine loss: new work rejected, last frame kept, new surface recovered");
}
const P3_PREVIEW: [u8; 4] = [197, 134, 136, 255];
fn near3(actual: [u8; 4], expected: [u8; 4]) -> bool {
actual.iter().zip(expected).all(|(a, e)| a.abs_diff(e) <= 3)
}
#[::core::prelude::v1::test]
fn windows_wide_frame_previews_in_standard_window() {
let Some((device, queue, mut renderer)) = setup() else {
return;
};
let mut target =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Rgba16FloatLinearP3, 2), &device, &queue)
.unwrap();
draw(
&mut target,
wgpu::Color {
r: 0.5,
g: 0.25,
b: 0.25,
a: 1.,
},
);
let image = renderer
.render_scene_to_image(
&scene(target.surface().preview_content().unwrap()),
viewport(),
)
.unwrap();
let pixel = image.get_pixel(16, 8).0;
assert!(near3(pixel, P3_PREVIEW), "{pixel:?}");
report("PASS linear P3 RGBA16F frame composited as sRGB preview in a standard window");
}
#[::core::prelude::v1::test]
fn windows_compute_written_frame_composites() {
let Some((device, queue, mut renderer)) = setup() else {
return;
};
let mut target =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Rgba16FloatLinearP3, 2), &device, &queue)
.unwrap();
let slot = target.surface().acquire().unwrap();
let texture = target.texture(&slot).unwrap();
let commands = crate::test_engine::compute_fill(&device, &texture, [0.5, 0.25, 0.25, 1.0]);
target.present(slot, [commands]).unwrap();
let image = renderer
.render_scene_to_image(
&scene(target.surface().preview_content().unwrap()),
viewport(),
)
.unwrap();
let pixel = image.get_pixel(16, 8).0;
assert!(near3(pixel, P3_PREVIEW), "{pixel:?}");
report("PASS compute-written RGBA16F slot composited");
}
#[::core::prelude::v1::test]
fn windows_compositor_without_gpu_wait_never_blocks_on_the_engine() {
let Some((device, queue, mut renderer)) = setup() else {
return;
};
let mut target =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Bgra8Srgb, 3), &device, &queue).unwrap();
let Some(fence) = target.fence.clone() else {
report("skipped: no shared engine fence (CPU-wait mode)");
return;
};
draw(&mut target, wgpu::Color::RED);
renderer
.render_scene_to_image(&scene(target.surface().content().unwrap()), viewport())
.unwrap();
fence
.no_host_wait
.store(true, std::sync::atomic::Ordering::Release);
let wakes = std::sync::Arc::new(std::sync::atomic::AtomicU32::new(0));
let counter = wakes.clone();
target.surface().on_present(move || {
counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
});
let raw: windows::Win32::Graphics::Direct3D12::ID3D12Device =
unsafe { device.as_hal::<Dx12>() }
.unwrap()
.raw_device()
.clone();
let gate: ID3D12Fence = unsafe {
raw.CreateFence(
0,
windows::Win32::Graphics::Direct3D12::D3D12_FENCE_FLAG_NONE,
)
}
.unwrap();
unsafe { queue.as_hal::<Dx12>() }
.unwrap()
.add_wait_fence(gate.clone(), 1);
draw(&mut target, wgpu::Color::GREEN);
let woken = wakes.load(std::sync::atomic::Ordering::SeqCst);
struct Gate(ID3D12Fence);
unsafe impl Send for Gate {}
let opener_gate = Gate(gate.clone());
let opener = std::thread::spawn(move || {
let gate = opener_gate;
std::thread::sleep(std::time::Duration::from_millis(300));
unsafe { gate.0.Signal(1) }.unwrap();
});
let started = std::time::Instant::now();
let image = renderer
.render_scene_to_image(&scene(target.surface().content().unwrap()), viewport())
.unwrap();
let elapsed = started.elapsed();
let pixel = image.get_pixel(16, 8).0;
assert!(
near(pixel, [255, 0, 0, 255]),
"unready frame shown: {pixel:?}"
);
assert!(
elapsed < std::time::Duration::from_millis(200),
"composition blocked on the engine ({elapsed:?})"
);
assert_eq!(target.surface().stats().waiting, 1);
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(3);
while wakes.load(std::sync::atomic::Ordering::SeqCst) == woken {
assert!(
std::time::Instant::now() < deadline,
"waiter never woke the UI"
);
std::thread::sleep(std::time::Duration::from_millis(2));
}
opener.join().unwrap();
let image = renderer
.render_scene_to_image(&scene(target.surface().content().unwrap()), viewport())
.unwrap();
let pixel = image.get_pixel(16, 8).0;
assert!(
near(pixel, [0, 255, 0, 255]),
"ready frame not shown: {pixel:?}"
);
report(format!(
"PASS compositor without GPU waits showed the last ready frame in {elapsed:?}, then the new frame once woken"
));
}
#[::core::prelude::v1::test]
fn windows_compositor_device_replacement_reopens_slots() {
let Some((device, queue, mut first)) = setup() else {
return;
};
let mut target =
WgpuSurfaceTarget::new(desc(SurfaceFormat::Bgra8Srgb, 3), &device, &queue).unwrap();
draw(&mut target, wgpu::Color::RED);
let retained = scene(target.surface().content().unwrap());
let image = first.render_scene_to_image(&retained, viewport()).unwrap();
assert!(near(image.get_pixel(16, 8).0, [255, 0, 0, 255]));
drop(first);
let Some(mut second) = setup().map(|(_, _, host)| host) else {
return;
};
let image = second.render_scene_to_image(&retained, viewport()).unwrap();
let pixel = image.get_pixel(16, 8).0;
assert!(
near(pixel, [255, 0, 0, 255]),
"retained frame on new compositor: {pixel:?}"
);
drop(retained);
for (i, color) in [
wgpu::Color::GREEN,
wgpu::Color::BLUE,
wgpu::Color::GREEN,
wgpu::Color::BLUE,
]
.into_iter()
.enumerate()
{
draw(&mut target, color);
let image = second
.render_scene_to_image(&scene(target.surface().content().unwrap()), viewport())
.unwrap();
let expected = if i % 2 == 0 {
[0, 255, 0, 255]
} else {
[0, 0, 255, 255]
};
let pixel = image.get_pixel(16, 8).0;
assert!(
near(pixel, expected),
"frame {i} on new compositor: {pixel:?}"
);
}
report("PASS compositor device replaced: retained and new frames composite on the new device");
}
fn present_written(target: &mut WgpuSurfaceTarget, bytes: &[u8]) {
let slot = target.surface().acquire().expect("free slot");
let desc = slot.desc();
let texture = target.texture(&slot).unwrap();
target.queue.write_texture(
texture.as_image_copy(),
bytes,
wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(desc.width * desc.format.bytes_per_pixel()),
rows_per_image: None,
},
texture.size(),
);
target.present(slot, []).unwrap();
}
fn strip_scene(content: Arc<dyn CustomContent>, width: u32, opacity: f32) -> Scene {
let bounds = Bounds::new(
point(ScaledPixels(0.), ScaledPixels(0.)),
size(
ScaledPixels(width as f32),
ScaledPixels(crate::colour_reference::HEIGHT as f32),
),
);
let mut scene = Scene::default();
scene.insert_primitive(CustomDraw {
order: 0,
bounds,
content_mask: ContentMask { bounds },
corner_radii: Default::default(),
opacity,
content,
coverage: None,
});
scene.finish();
scene
}
#[::core::prelude::v1::test]
fn windows_colour_matches_reference() {
use crate::colour_reference::*;
let Some((device, queue, standard)) = setup() else {
return;
};
let mut strip = WgpuSurfaceTarget::new(
SurfaceDesc {
width: width(),
height: HEIGHT,
format: SurfaceFormat::Rgba16FloatLinearP3,
slots: 2,
},
&device,
&queue,
)
.unwrap();
present_written(&mut strip, &strip_rgba16f());
let control_width = SRGB_CONTROLS.len() as u32 * STRIPE;
let mut controls = WgpuSurfaceTarget::new(
SurfaceDesc {
width: control_width,
height: HEIGHT,
format: SurfaceFormat::Bgra8Srgb,
slots: 2,
},
&device,
&queue,
)
.unwrap();
present_written(&mut controls, &controls_bgra8());
let extent = size(DevicePixels(width() as i32), DevicePixels(HEIGHT as i32));
let mut failures = Vec::new();
let mut worst = 0f64;
let mut checked = 0;
let mut renderers = vec![("preview", standard, false)];
if let Some((_, _, mut wide)) = setup()
&& wide
.set_color_mode(gpui::WindowColorMode::WideGamutSdr)
.is_ok()
{
renderers.push(("wide", wide, true));
} else {
report("wide mode unavailable: preview only");
}
for (mode, mut renderer, is_wide) in renderers {
let content = |target: &WgpuSurfaceTarget| {
if is_wide {
target.surface().content().unwrap()
} else {
target.surface().preview_content().unwrap()
}
};
for opacity in [1., 0.5] {
let image = renderer
.render_scene_to_image(&strip_scene(content(&strip), width(), opacity), extent)
.unwrap();
for (i, (name, patch)) in PATCHES.iter().enumerate() {
let actual = image.get_pixel(centre(i), HEIGHT / 2).0;
let expected = preview(*patch, opacity);
let d = distance(actual, expected);
worst = worst.max(d);
checked += 1;
if d > 2.5 {
failures.push(format!(
"{mode} {name} @ {opacity}: {actual:?} vs {expected:.1?}"
));
}
}
let image = renderer
.render_scene_to_image(
&strip_scene(
controls.surface().content().unwrap(),
control_width,
opacity,
),
extent,
)
.unwrap();
for (i, (name, rgba)) in SRGB_CONTROLS.iter().enumerate() {
let actual = image.get_pixel(centre(i), HEIGHT / 2).0;
let expected = control(*rgba, opacity);
let d = distance(actual, expected);
worst = worst.max(d);
checked += 1;
if d > 2.5 {
failures.push(format!(
"{mode} {name} @ {opacity}: {actual:?} vs {expected:.1?}"
));
}
}
}
}
assert!(
failures.is_empty(),
"colour mismatches:\n{}",
failures.join("\n")
);
report(format!(
"PASS colour: {checked} patches match the CPU reference (worst 8-bit distance {worst:.2})"
));
}