use lgpu::{
BufferDesc, BufferUsage, Instance, MemoryLocation, TextureDesc, TextureFormat, TextureUsage,
};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
#[test]
fn test_timeline_semaphore_cpu_signaling_and_waiting() {
let instance = Instance::new().expect("Failed to create instance");
let adapters = instance
.enumerate_adapters()
.expect("Failed to enumerate adapters");
let (device, _queue) = adapters[0]
.request_device(&instance)
.expect("Failed to request device");
let semaphore = Arc::new(
device
.create_timeline_semaphore(0)
.expect("Failed to create timeline semaphore"),
);
let initial_val = semaphore.value().expect("Failed to get semaphore value");
assert_eq!(initial_val, 0, "Initial timeline semaphore value must be 0");
semaphore
.signal(5)
.expect("Failed to signal timeline semaphore");
let val_after_signal = semaphore.value().expect("Failed to get semaphore value");
assert_eq!(
val_after_signal, 5,
"Timeline semaphore value must be 5 after signaling"
);
let sem_clone = Arc::clone(&semaphore);
let handle = thread::spawn(move || {
thread::sleep(Duration::from_millis(50));
sem_clone
.signal(10)
.expect("Thread failed to signal semaphore");
});
semaphore
.wait(10, 5_000_000_000)
.expect("Wait on timeline semaphore failed");
let val_final = semaphore.value().expect("Failed to get semaphore value");
assert!(val_final >= 10, "Timeline semaphore value must be >= 10");
handle.join().expect("Worker thread panicked");
println!("SUCCESS: Timeline semaphore CPU signaling and cross-thread waiting verified.");
}
#[test]
fn test_timeline_semaphore_gpu_async_pipelining() {
let instance = Instance::new().expect("Failed to create instance");
let adapters = instance
.enumerate_adapters()
.expect("Failed to enumerate adapters");
let (device, queue) = adapters[0]
.request_device(&instance)
.expect("Failed to request device");
let timeline = device
.create_timeline_semaphore(0)
.expect("Failed to create timeline semaphore");
let tex_desc = TextureDesc {
label: Some("AsyncTex"),
width: 128,
height: 128,
depth: 1,
mip_levels: 1,
sample_count: 1,
format: TextureFormat::Rgba8Unorm,
usage: TextureUsage::RENDER_ATTACHMENT | TextureUsage::COPY_SRC,
};
let texture = device
.create_texture(&tex_desc)
.expect("Failed to create texture");
let mut encoder1 = device
.create_command_encoder()
.expect("Failed to create encoder");
encoder1
.clear_and_render_texture(&texture, [0.1, 0.2, 0.3, 1.0])
.expect("clear failed");
encoder1.memory_barrier();
let cmd1 = encoder1.finish();
queue
.submit_async(cmd1, &[], &[(&timeline, 1)])
.expect("Failed to submit async batch 1");
timeline
.wait(1, 5_000_000_000)
.expect("Timeout waiting for GPU timeline semaphore at value 1");
let val1 = timeline.value().expect("Failed to read timeline value");
assert!(
val1 >= 1,
"Timeline value must be >= 1 after GPU completion"
);
let mut encoder2 = device
.create_command_encoder()
.expect("Failed to create encoder");
encoder2
.clear_and_render_texture(&texture, [0.4, 0.5, 0.6, 1.0])
.expect("clear failed");
let cmd2 = encoder2.finish();
queue
.submit_async(cmd2, &[(&timeline, 1)], &[(&timeline, 2)])
.expect("Failed to submit async batch 2");
timeline
.wait(2, 5_000_000_000)
.expect("Timeout waiting for GPU timeline semaphore at value 2");
let val2 = timeline.value().expect("Failed to read timeline value");
assert!(
val2 >= 2,
"Timeline value must be >= 2 after GPU completion"
);
println!("SUCCESS: GPU async pipelining with timeline semaphores verified.");
}
#[test]
fn test_multithreaded_batch_submission() {
let instance = Instance::new().expect("Failed to create instance");
let adapters = instance
.enumerate_adapters()
.expect("Failed to enumerate adapters");
let (device, queue) = adapters[0]
.request_device(&instance)
.expect("Failed to request device");
let device_arc = Arc::new(device);
let mut thread_handles = Vec::new();
for thread_idx in 0..4 {
let dev = Arc::clone(&device_arc);
let handle = thread::spawn(move || {
let mut encoder = dev
.create_command_encoder()
.expect("Failed to create encoder");
encoder.memory_barrier();
let tex_desc = TextureDesc {
label: Some("ThreadTex"),
width: 64,
height: 64,
depth: 1,
mip_levels: 1,
sample_count: 1,
format: TextureFormat::Rgba8Unorm,
usage: TextureUsage::RENDER_ATTACHMENT,
};
let tex = dev
.create_texture(&tex_desc)
.expect("Failed to create texture");
let c = (thread_idx as f32 + 1.0) * 0.2;
encoder
.clear_and_render_texture(&tex, [c, c, c, 1.0])
.expect("clear failed");
(encoder.finish(), tex)
});
thread_handles.push(handle);
}
let mut command_buffers = Vec::new();
let mut kept_textures = Vec::new();
for h in thread_handles {
let (cmd, tex) = h.join().expect("Worker thread recording failed");
command_buffers.push(cmd);
kept_textures.push(tex);
}
assert_eq!(command_buffers.len(), 4);
queue
.submit_batches(command_buffers)
.expect("Failed to submit command buffer batch");
drop(kept_textures);
println!("SUCCESS: Multi-threaded recording and atomic batch submission verified.");
}
#[test]
fn test_gpu_barriers_and_buffer_synchronization() {
let instance = Instance::new().expect("Failed to create instance");
let adapters = instance
.enumerate_adapters()
.expect("Failed to enumerate adapters");
let (device, queue) = adapters[0]
.request_device(&instance)
.expect("Failed to request device");
let buf_desc = BufferDesc {
label: Some("BarrierTestBuffer"),
size: 512,
usage: BufferUsage::STORAGE | BufferUsage::TRANSFER_SRC | BufferUsage::TRANSFER_DST,
memory_location: MemoryLocation::CpuToGpu,
};
let buffer = device
.create_buffer(&buf_desc)
.expect("Failed to create buffer");
let mut encoder = device
.create_command_encoder()
.expect("Failed to create encoder");
encoder.memory_barrier();
encoder.buffer_barrier(&buffer);
let cmd = encoder.finish();
queue
.submit(cmd)
.expect("Failed to submit barrier command buffer");
println!("SUCCESS: GPU memory barrier and buffer-specific barrier verified.");
}
#[test]
fn test_async_recorder_harvesting_leak_free() {
let instance = Instance::new().expect("Failed to create instance");
let adapters = instance
.enumerate_adapters()
.expect("Failed to enumerate adapters");
let (device, queue) = adapters[0]
.request_device(&instance)
.expect("Failed to request device");
let timeline = device
.create_timeline_semaphore(0)
.expect("Failed to create timeline semaphore");
for frame in 1..=50u64 {
let mut encoder = device
.create_command_encoder()
.expect("Failed to create encoder");
encoder.memory_barrier();
let cmd = encoder.finish();
let wait_val = frame - 1;
let signal_val = frame;
let wait_sems: &[(&lgpu::TimelineSemaphore, u64)] = if wait_val > 0 {
&[(&timeline, wait_val)]
} else {
&[]
};
queue
.submit_async(cmd, wait_sems, &[(&timeline, signal_val)])
.expect("Async submit failed");
}
timeline
.wait(50, 5_000_000_000)
.expect("Timeout waiting for final async frame");
let final_val = timeline.value().expect("Failed to get timeline value");
assert_eq!(final_val, 50, "Final timeline value must reach 50");
println!("SUCCESS: 50 iterations of async submissions with zero leaks and automatic recycling verified.");
}