Compare commits

...

5 commits

9 changed files with 1305 additions and 458 deletions

View file

@ -17,7 +17,6 @@ tracing = "0.1.40"
tracing-subscriber = "0.3.18" tracing-subscriber = "0.3.18"
vk-mem = "0.4.0" vk-mem = "0.4.0"
vk-sync = "0.1.6" vk-sync = "0.1.6"
winit = "0.30.5"
tinyvec = "1.8" tinyvec = "1.8"
rand = "0.8.5" rand = "0.8.5"
tokio = "1.42.0" tokio = "1.42.0"
@ -25,3 +24,9 @@ tokio = "1.42.0"
futures = "0.3" futures = "0.3"
smol = "2.0" smol = "2.0"
rayon = "1.10" rayon = "1.10"
winit = {version = "0.30.5", features = ["rwh_06"]}
raw-window-handle = "0.6"
egui = "0.30.0"
egui_winit_platform = "0.24.0"

View file

@ -7,4 +7,7 @@ edition = "2021"
winit = { workspace = true } winit = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
tracing-subscriber = { workspace = true } tracing-subscriber = { workspace = true }
renderer = { path = "../renderer" } renderer = { path = "../renderer" }
egui = { workspace = true }
egui_winit_platform = { workspace = true }

View file

@ -12,25 +12,33 @@ use winit::{
}; };
struct WindowState { struct WindowState {
last_resize_events: BTreeMap<WindowId, PhysicalSize<u32>>, window: Window,
window_attrs: WindowAttributes, egui_platform: egui_winit_platform::Platform,
window: Option<Window>,
windows: BTreeMap<WindowId, Window>,
renderer: Renderer,
} }
impl WindowState { struct WinitState {
fn new(window_title: String, display: DisplayHandle) -> WindowState { last_resize_events: BTreeMap<WindowId, PhysicalSize<u32>>,
window_attrs: WindowAttributes,
windows2: BTreeMap<WindowId, WindowState>,
renderer: Renderer<WindowId>,
}
impl WinitState {
const BASE_WIDTH: u32 = 800;
const BASE_HEIGHT: u32 = 600;
fn new(window_title: String, display: DisplayHandle) -> WinitState {
Self { Self {
window: None, windows2: BTreeMap::new(),
windows: BTreeMap::new(),
last_resize_events: BTreeMap::new(), last_resize_events: BTreeMap::new(),
window_attrs: WindowAttributes::default() window_attrs: WindowAttributes::default()
.with_title(window_title) .with_title(window_title)
.with_resizable(true) .with_resizable(true)
.with_inner_size(LogicalSize::new(800, 600)), .with_inner_size(LogicalSize::new(
Self::BASE_WIDTH,
Self::BASE_HEIGHT,
)),
// TODO: pass down this error and add some kind of error handling UI or dump // TODO: pass down this error and add some kind of error handling UI or dump
renderer: Renderer::new(display).expect("renderer"), renderer: Renderer::new(display.as_raw()).expect("renderer"),
} }
} }
@ -42,10 +50,10 @@ impl WindowState {
_ = (window_id, new_size); _ = (window_id, new_size);
info!("TODO: implement resize events"); info!("TODO: implement resize events");
if let Some(ctx) = self.renderer.window_contexts.get_mut(&window_id) { if let Some(ctx) = self.renderer.window_contexts.get_mut(&window_id) {
ctx.recreate_swapchain(renderer::Extent2D { ctx.recreate_with(Some(renderer::Extent2D {
width: new_size.width, width: new_size.width,
height: new_size.height, height: new_size.height,
}) }))
.expect("swapchain recreation"); .expect("swapchain recreation");
} }
} }
@ -56,7 +64,30 @@ impl WindowState {
window_id = u64::from(window_id), window_id = u64::from(window_id),
"TODO: implement draw request" "TODO: implement draw request"
); );
self.renderer.debug_draw().expect("drawing"); if let Some(window) = self.windows2.get_mut(&window_id) {
// egui
window.egui_platform.begin_frame();
let output = window.egui_platform.end_frame(Some(&window.window));
let _draw_data = window
.egui_platform
.context()
.tessellate(output.shapes, output.pixels_per_point);
// rendering
self.renderer
.debug_draw(&window_id, || window.window.pre_present_notify())
.expect("drawing");
window.window.request_redraw();
}
}
fn remove_window(&mut self, window_id: WindowId) {
tracing::info!(window = u64::from(window_id), "window close requested");
self.windows2.remove(&window_id);
self.renderer.window_contexts.remove(&window_id);
} }
fn create_window( fn create_window(
@ -89,25 +120,43 @@ impl WindowState {
window.window_handle().expect("window handle"), window.window_handle().expect("window handle"),
); );
self.windows.insert(window.id(), window); self.windows2.insert(
window_id,
WindowState {
window,
egui_platform: egui_winit_platform::Platform::new(
egui_winit_platform::PlatformDescriptor {
physical_width: size.width,
physical_height: size.height,
scale_factor: 1.0,
..Default::default()
},
),
},
);
} }
} }
impl ApplicationHandler for WindowState { impl ApplicationHandler for WinitState {
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
tracing::debug!("winit::resumed"); tracing::info!("winit::resumed");
self.create_window(event_loop); self.create_window(event_loop);
} }
fn about_to_wait( fn about_to_wait(
&mut self, &mut self,
_event_loop: &winit::event_loop::ActiveEventLoop, event_loop: &winit::event_loop::ActiveEventLoop,
) { ) {
tracing::info!("winit::about_to_wait");
for (&window, &resize) in self.last_resize_events.clone().iter() { for (&window, &resize) in self.last_resize_events.clone().iter() {
self.handle_final_resize(window, resize); self.handle_final_resize(window, resize);
} }
self.last_resize_events.clear(); self.last_resize_events.clear();
if self.windows2.is_empty() {
event_loop.exit();
}
} }
fn window_event( fn window_event(
@ -116,27 +165,35 @@ impl ApplicationHandler for WindowState {
window_id: winit::window::WindowId, window_id: winit::window::WindowId,
event: winit::event::WindowEvent, event: winit::event::WindowEvent,
) { ) {
if !matches!(event, winit::event::WindowEvent::Resized(_)) { // if !matches!(event, winit::event::WindowEvent::Resized(_)) {
if let Some(resize) = self.last_resize_events.remove(&window_id) { // if let Some(resize) = self.last_resize_events.remove(&window_id) {
self.handle_final_resize(window_id, resize); // self.handle_final_resize(window_id, resize);
} // }
// }
if let Some(window) = self.windows2.get_mut(&window_id) {
window.egui_platform.handle_event(&event);
} }
match event { match event {
winit::event::WindowEvent::Resized(physical_size) => { winit::event::WindowEvent::Resized(physical_size) => {
_ = self.last_resize_events.insert(window_id, physical_size); _ = self.last_resize_events.insert(window_id, physical_size);
} }
winit::event::WindowEvent::CloseRequested => { winit::event::WindowEvent::CloseRequested
tracing::info!( | winit::event::WindowEvent::KeyboardInput {
window = u64::from(window_id), event:
"window close requested" winit::event::KeyEvent {
); physical_key:
winit::keyboard::PhysicalKey::Code(
self.windows.remove(&window_id); winit::keyboard::KeyCode::KeyQ,
self.renderer.window_contexts.remove(&window_id); ),
state: ElementState::Pressed,
if self.windows.is_empty() { repeat: false,
event_loop.exit(); ..
} },
..
} => {
self.remove_window(window_id);
} }
winit::event::WindowEvent::KeyboardInput { winit::event::WindowEvent::KeyboardInput {
event: event:
@ -175,9 +232,6 @@ impl ApplicationHandler for WindowState {
} }
winit::event::WindowEvent::RedrawRequested => { winit::event::WindowEvent::RedrawRequested => {
self.handle_draw_request(window_id); self.handle_draw_request(window_id);
if let Some(window) = self.windows.get(&window_id) {
window.request_redraw();
}
} }
_ => {} // unhandled event _ => {} // unhandled event
} }
@ -187,8 +241,9 @@ impl ApplicationHandler for WindowState {
fn main() { fn main() {
tracing_subscriber::fmt().init(); tracing_subscriber::fmt().init();
let ev = EventLoop::new().unwrap(); let ev = EventLoop::new().unwrap();
ev.set_control_flow(winit::event_loop::ControlFlow::Poll);
let display = ev.display_handle().expect("display handle"); let display = ev.display_handle().expect("display handle");
let mut game = WindowState::new("Vidya".to_owned(), display); let mut game = WinitState::new("Vidya".to_owned(), display);
ev.run_app(&mut game).unwrap(); ev.run_app(&mut game).unwrap();
} }

View file

@ -17,4 +17,11 @@ tracing = "0.1.40"
tracing-subscriber = "0.3.18" tracing-subscriber = "0.3.18"
vk-mem = "0.4.0" vk-mem = "0.4.0"
vk-sync = "0.1.6" vk-sync = "0.1.6"
winit = "0.30.5" crossbeam = "0.8.4"
parking_lot = "0.12.3"
smol.workspace = true
tracing-test = "0.2.5"
raw-window-handle = { workspace = true }
egui = { workspace = true }
egui_winit_platform = { workspace = true }

View file

@ -1,64 +1,14 @@
use std::{future::Future, marker::PhantomData}; use std::{future::Future, marker::PhantomData, sync::Arc};
use super::{Device2 as Device, Queue}; use crate::sync::{self, FenceFuture};
use super::{Device, Queue};
use ash::{prelude::*, vk}; use ash::{prelude::*, vk};
pub struct FenceFuture<'a> {
device: Device,
fence: vk::Fence,
_pd: PhantomData<&'a ()>,
}
impl Drop for FenceFuture<'_> {
fn drop(&mut self) {
unsafe { self.device.dev().destroy_fence(self.fence, None) };
}
}
impl FenceFuture<'_> {
/// Unsafe because `fence` must not be destroyed while this future is live.
pub unsafe fn from_fence(device: Device, fence: vk::Fence) -> Self {
Self {
device,
fence,
_pd: PhantomData,
}
}
pub fn block_until(&self) -> VkResult<()> {
unsafe {
self.device
.dev()
.wait_for_fences(&[self.fence], true, u64::MAX)
}
}
}
impl Future for FenceFuture<'_> {
type Output = ();
fn poll(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
let signaled = unsafe {
self.device
.dev()
.get_fence_status(self.fence)
.unwrap_or(false)
};
if signaled {
std::task::Poll::Ready(())
} else {
tokio::task::spawn_blocking(move || {});
std::task::Poll::Pending
}
}
}
pub struct SingleUseCommandPool { pub struct SingleUseCommandPool {
device: Device, device: Device,
pool: vk::CommandPool, pool: vk::CommandPool,
queue: Queue,
} }
impl Drop for SingleUseCommandPool { impl Drop for SingleUseCommandPool {
@ -70,15 +20,27 @@ impl Drop for SingleUseCommandPool {
} }
impl SingleUseCommandPool { impl SingleUseCommandPool {
pub fn new(device: Device, family_index: u32) -> VkResult<Self> { pub fn new(device: Device, queue: Queue) -> VkResult<Arc<Self>> {
let pool_info = vk::CommandPoolCreateInfo::default() let pool_info = vk::CommandPoolCreateInfo::default()
.queue_family_index(family_index) .queue_family_index(queue.family())
.flags(vk::CommandPoolCreateFlags::TRANSIENT); .flags(vk::CommandPoolCreateFlags::TRANSIENT);
let pool = let pool =
unsafe { device.dev().create_command_pool(&pool_info, None)? }; unsafe { device.dev().create_command_pool(&pool_info, None)? };
Ok(Self { device, pool }) Ok(Arc::new(Self {
device,
pool,
queue,
}))
}
pub fn alloc(self: &Arc<Self>) -> VkResult<SingleUseCommand> {
SingleUseCommand::new(self.device.clone(), self.clone())
}
pub fn queue(&self) -> &Queue {
&self.queue
} }
pub fn pool(&self) -> vk::CommandPool { pub fn pool(&self) -> vk::CommandPool {
@ -88,7 +50,7 @@ impl SingleUseCommandPool {
pub struct SingleUseCommand { pub struct SingleUseCommand {
device: Device, device: Device,
pool: vk::CommandPool, pool: Arc<SingleUseCommandPool>,
buffer: vk::CommandBuffer, buffer: vk::CommandBuffer,
} }
@ -97,23 +59,28 @@ impl Drop for SingleUseCommand {
unsafe { unsafe {
self.device self.device
.dev() .dev()
.free_command_buffers(self.pool, &[self.buffer]) .free_command_buffers(self.pool.pool(), &[self.buffer])
}; };
} }
} }
impl SingleUseCommand { impl SingleUseCommand {
pub fn new(device: Device, pool: vk::CommandPool) -> VkResult<Self> { pub fn new(
device: Device,
pool: Arc<SingleUseCommandPool>,
) -> VkResult<Self> {
let buffer = unsafe { let buffer = unsafe {
let alloc_info = vk::CommandBufferAllocateInfo::default() let alloc_info = vk::CommandBufferAllocateInfo::default()
.command_buffer_count(1) .command_buffer_count(1)
.command_pool(pool) .command_pool(pool.pool())
.level(vk::CommandBufferLevel::PRIMARY); .level(vk::CommandBufferLevel::PRIMARY);
let buffer = device.dev().allocate_command_buffers(&alloc_info)?[0]; let buffer = device.dev().allocate_command_buffers(&alloc_info)?[0];
let begin_info = vk::CommandBufferBeginInfo::default() device.dev().begin_command_buffer(
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); buffer,
device.dev().begin_command_buffer(buffer, &begin_info)?; &vk::CommandBufferBeginInfo::default()
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
)?;
buffer buffer
}; };
@ -124,16 +91,16 @@ impl SingleUseCommand {
}) })
} }
pub fn command_buffer(&self) -> vk::CommandBuffer { pub fn buffer(&self) -> vk::CommandBuffer {
self.buffer self.buffer
} }
fn submit_fence( pub fn submit_fence(
&self, &self,
queue: Queue,
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
signal: Option<vk::Semaphore>, signal: Option<vk::Semaphore>,
) -> VkResult<vk::Fence> { fence: Option<vk::Fence>,
) -> VkResult<()> {
unsafe { self.device.dev().end_command_buffer(self.buffer)? }; unsafe { self.device.dev().end_command_buffer(self.buffer)? };
let buffers = [self.buffer]; let buffers = [self.buffer];
@ -154,43 +121,39 @@ impl SingleUseCommand {
.wait_dst_stage_mask(core::slice::from_ref(stage)); .wait_dst_stage_mask(core::slice::from_ref(stage));
} }
let fence = { let fence = fence.unwrap_or(vk::Fence::null());
let create_info = vk::FenceCreateInfo::default(); self.pool.queue().with_locked(|queue| unsafe {
unsafe { self.device.dev().create_fence(&create_info, None)? }
};
queue.with_locked(|queue| unsafe {
self.device.dev().queue_submit(queue, &[submit_info], fence) self.device.dev().queue_submit(queue, &[submit_info], fence)
})?; })?;
tracing::info!(
"submitted queue {:?} and fence {:?}",
self.pool.queue(),
fence
);
Ok(fence) Ok(())
} }
pub fn submit_async<'a>( pub fn submit_async<'a>(
&'a self, &'a self,
queue: Queue,
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
signal: Option<vk::Semaphore>, signal: Option<vk::Semaphore>,
fence: Arc<sync::Fence>,
) -> VkResult<FenceFuture<'a>> { ) -> VkResult<FenceFuture<'a>> {
let device = self.device.clone(); let device = self.device.clone();
let fence = self.submit_fence(queue, wait, signal)?; self.submit_fence(wait, signal, Some(fence.fence()))?;
Ok(unsafe { FenceFuture::from_fence(device, fence) }) Ok(unsafe { FenceFuture::new(fence) })
} }
pub fn submit_blocking( pub fn submit_blocking(
self, self,
queue: Queue,
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
signal: Option<vk::Semaphore>, signal: Option<vk::Semaphore>,
) -> VkResult<()> { ) -> VkResult<()> {
let device = self.device.clone(); let fence = Arc::new(sync::Fence::create(self.device.clone())?);
let fence = self.submit_fence(queue, wait, signal)?; let future = self.submit_async(wait, signal, fence)?;
future.block();
unsafe {
device.dev().wait_for_fences(&[fence], true, u64::MAX)?;
device.dev().destroy_fence(fence, None);
}
Ok(()) Ok(())
} }
} }
@ -200,6 +163,12 @@ mod tests {
use super::*; use super::*;
async fn async_submit(cmd: SingleUseCommand, queue: Queue) { async fn async_submit(cmd: SingleUseCommand, queue: Queue) {
cmd.submit_async(queue, None, None).unwrap().await; cmd.submit_async(
None,
None,
Arc::new(sync::Fence::create(cmd.device.clone()).unwrap()),
)
.unwrap()
.await;
} }
} }

View file

@ -1,4 +1,4 @@
use super::{Device2 as Device, Queue, VkAllocator}; use super::{Device, Queue, VkAllocator};
use ash::{prelude::*, vk}; use ash::{prelude::*, vk};
use vk_mem::Alloc; use vk_mem::Alloc;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,27 @@
use ash::vk;
struct Rgba([f32;4]);
enum LoadOp {
Clear(Rgba),
Load,
DontCare,
}
enum StoreOp {
DontCare,Store,
}
struct AttachmentInfo {
size: glam::UVec2,
format: vk::Format,
load: LoadOp,
store: StoreOp,
}
struct Texture {
texture: vk::Image,
}
pub struct RenderGraph {
}

View file

@ -1,11 +1,239 @@
use super::Device2 as Device; use std::{
future::Future,
marker::PhantomData,
sync::{atomic::AtomicU32, Arc},
time::Duration,
};
use super::Device;
use ash::{prelude::*, vk}; use ash::{prelude::*, vk};
use crossbeam::channel::{Receiver, Sender};
type Message = (SyncPrimitive, std::task::Waker);
pub struct SyncThreadpool {
channel: (Sender<Message>, Receiver<Message>),
timeout: u64,
thread_dies_after: Duration,
max_threads: u32,
num_threads: Arc<AtomicU32>,
}
#[derive(Debug)]
enum SyncPrimitive {
Fence(Arc<Fence>),
// actually, I think this is an awful idea because I would have to hold a
// lock on all queues.
DeviceIdle(Device),
}
impl SyncThreadpool {
pub fn new() -> SyncThreadpool {
Self::with_max_threads(512)
}
pub fn with_max_threads(max_threads: u32) -> SyncThreadpool {
Self {
// 0-capacity channel to ensure immediate consumption of fences
channel: crossbeam::channel::bounded(0),
max_threads,
num_threads: Arc::new(AtomicU32::new(0)),
timeout: u64::MAX,
thread_dies_after: Duration::from_secs(5),
}
}
fn try_spawn_thread(&self) -> Option<()> {
use std::sync::atomic::Ordering;
match self.num_threads.fetch_update(
Ordering::Release,
Ordering::Acquire,
|i| {
if i < self.max_threads {
Some(i + 1)
} else {
None
}
},
) {
Ok(tid) => {
struct SyncThread {
timeout: u64,
thread_dies_after: Duration,
num_threads: Arc<AtomicU32>,
rx: Receiver<Message>,
}
impl SyncThread {
fn run(self, barrier: Arc<std::sync::Barrier>) {
tracing::info!("spawned new sync thread");
barrier.wait();
while let Ok((sync, waker)) =
self.rx.recv_timeout(self.thread_dies_after)
{
tracing::info!(
"received ({:?}, {:?})",
sync,
waker
);
loop {
let wait_result = match &sync {
SyncPrimitive::Fence(fence) => {
fence.wait_on(Some(self.timeout))
}
SyncPrimitive::DeviceIdle(device) => {
device.wait_idle()
}
};
match wait_result {
Ok(_) => {
waker.wake();
break;
}
Err(vk::Result::TIMEOUT) => {}
Err(err) => {
tracing::error!(
"failed to wait on {sync:?} in waiter thread: {err}"
);
break;
}
}
}
}
// because I don't want some thread to not spawn as soon as this one exists
self.num_threads.fetch_sub(1, Ordering::AcqRel);
}
}
let thread = SyncThread {
timeout: self.timeout,
thread_dies_after: self.thread_dies_after,
num_threads: self.num_threads.clone(),
rx: self.channel.1.clone(),
};
let barrier = Arc::new(std::sync::Barrier::new(2));
std::thread::Builder::new()
.name(format!("fence-waiter-{tid}"))
.spawn({
let barrier = barrier.clone();
move || {
thread.run(barrier);
}
});
barrier.wait();
Some(())
}
Err(_) => {
tracing::error!(
"sync-threadpool exceeded local thread limit ({})",
self.max_threads
);
None
}
}
}
fn spawn_waiter(&self, fence: Arc<Fence>, waker: std::task::Waker) {
use std::sync::atomic::Ordering;
let mut msg = (SyncPrimitive::Fence(fence), waker);
while let Err(err) = self.channel.0.try_send(msg) {
match err {
crossbeam::channel::TrySendError::Full(msg2) => {
msg = msg2;
self.try_spawn_thread();
}
crossbeam::channel::TrySendError::Disconnected(_) => {
//tracing::error!("sync-threadpool channel disconnected?");
unreachable!()
}
}
}
}
}
pub struct Semaphore { pub struct Semaphore {
device: Device, device: Device,
inner: vk::Semaphore, inner: vk::Semaphore,
} }
pub struct Fence {
dev: Device,
fence: vk::Fence,
}
impl std::fmt::Debug for Fence {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Fence").field("fence", &self.fence).finish()
}
}
impl Drop for Fence {
fn drop(&mut self) {
unsafe {
self.dev.dev().destroy_fence(self.fence, None);
}
}
}
impl Fence {
unsafe fn new(dev: Device, fence: vk::Fence) -> Fence {
Self { dev, fence }
}
pub fn create(dev: Device) -> VkResult<Fence> {
unsafe {
Ok(Self::new(
dev.clone(),
dev.dev()
.create_fence(&vk::FenceCreateInfo::default(), None)?,
))
}
}
pub fn create_signaled(dev: Device) -> VkResult<Fence> {
unsafe {
Ok(Self::new(
dev.clone(),
dev.dev().create_fence(
&vk::FenceCreateInfo::default()
.flags(vk::FenceCreateFlags::SIGNALED),
None,
)?,
))
}
}
pub fn wait_on(&self, timeout: Option<u64>) -> Result<(), vk::Result> {
use core::slice::from_ref;
unsafe {
self.dev.dev().wait_for_fences(
from_ref(&self.fence),
true,
timeout.unwrap_or(u64::MAX),
)
}
}
pub fn fence(&self) -> vk::Fence {
self.fence
}
pub fn is_signaled(&self) -> bool {
unsafe { self.dev.dev().get_fence_status(self.fence).unwrap_or(false) }
}
pub fn reset(&self) -> Result<(), vk::Result> {
unsafe {
self.dev
.dev()
.reset_fences(core::slice::from_ref(&self.fence))
}
}
}
impl AsRef<vk::Fence> for Fence {
fn as_ref(&self) -> &vk::Fence {
todo!()
}
}
impl Semaphore { impl Semaphore {
pub fn new(device: Device) -> VkResult<Self> { pub fn new(device: Device) -> VkResult<Self> {
let mut type_info = vk::SemaphoreTypeCreateInfo::default() let mut type_info = vk::SemaphoreTypeCreateInfo::default()
@ -40,3 +268,50 @@ impl Drop for Semaphore {
} }
} }
} }
pub struct FenceFuture<'a> {
fence: Arc<Fence>,
// lifetime parameter to prevent release of resources while future is pendign
_pd: PhantomData<&'a ()>,
}
impl FenceFuture<'_> {
/// Unsafe because `fence` must not be destroyed while this future is live.
pub unsafe fn from_fence(device: Device, fence: vk::Fence) -> Self {
Self {
fence: Arc::new(Fence::new(device, fence)),
_pd: PhantomData,
}
}
pub fn new(fence: Arc<Fence>) -> Self {
Self {
fence,
_pd: PhantomData,
}
}
pub fn block(&self) -> VkResult<()> {
self.fence.wait_on(None)?;
self.fence.reset()
}
}
impl Future for FenceFuture<'_> {
type Output = ();
fn poll(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
if self.fence.is_signaled() {
tracing::info!("fence ({:?}) is signaled", self.fence);
_ = self.fence.reset();
std::task::Poll::Ready(())
} else {
self.fence
.dev
.sync_threadpool()
.spawn_waiter(self.fence.clone(), cx.waker().clone());
std::task::Poll::Pending
}
}
}