Compare commits
No commits in common. "7b7badd4d41109777a000357ef64b42d05691b84" and "29258aed7b5b496d6a005f764ef1f0916afc8d94" have entirely different histories.
7b7badd4d4
...
29258aed7b
|
@ -17,6 +17,7 @@ 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"
|
||||||
|
@ -24,9 +25,3 @@ 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"
|
|
|
@ -7,7 +7,4 @@ 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 }
|
|
|
@ -12,33 +12,25 @@ use winit::{
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WindowState {
|
struct WindowState {
|
||||||
window: Window,
|
|
||||||
egui_platform: egui_winit_platform::Platform,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WinitState {
|
|
||||||
last_resize_events: BTreeMap<WindowId, PhysicalSize<u32>>,
|
last_resize_events: BTreeMap<WindowId, PhysicalSize<u32>>,
|
||||||
window_attrs: WindowAttributes,
|
window_attrs: WindowAttributes,
|
||||||
windows2: BTreeMap<WindowId, WindowState>,
|
window: Option<Window>,
|
||||||
renderer: Renderer<WindowId>,
|
windows: BTreeMap<WindowId, Window>,
|
||||||
|
renderer: Renderer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WinitState {
|
impl WindowState {
|
||||||
const BASE_WIDTH: u32 = 800;
|
fn new(window_title: String, display: DisplayHandle) -> WindowState {
|
||||||
const BASE_HEIGHT: u32 = 600;
|
|
||||||
fn new(window_title: String, display: DisplayHandle) -> WinitState {
|
|
||||||
Self {
|
Self {
|
||||||
windows2: BTreeMap::new(),
|
window: None,
|
||||||
|
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(
|
.with_inner_size(LogicalSize::new(800, 600)),
|
||||||
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.as_raw()).expect("renderer"),
|
renderer: Renderer::new(display).expect("renderer"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,10 +42,10 @@ impl WinitState {
|
||||||
_ = (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_with(Some(renderer::Extent2D {
|
ctx.recreate_swapchain(renderer::Extent2D {
|
||||||
width: new_size.width,
|
width: new_size.width,
|
||||||
height: new_size.height,
|
height: new_size.height,
|
||||||
}))
|
})
|
||||||
.expect("swapchain recreation");
|
.expect("swapchain recreation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,30 +56,7 @@ impl WinitState {
|
||||||
window_id = u64::from(window_id),
|
window_id = u64::from(window_id),
|
||||||
"TODO: implement draw request"
|
"TODO: implement draw request"
|
||||||
);
|
);
|
||||||
if let Some(window) = self.windows2.get_mut(&window_id) {
|
self.renderer.debug_draw().expect("drawing");
|
||||||
// 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(
|
||||||
|
@ -120,43 +89,25 @@ impl WinitState {
|
||||||
window.window_handle().expect("window handle"),
|
window.window_handle().expect("window handle"),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.windows2.insert(
|
self.windows.insert(window.id(), window);
|
||||||
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 WinitState {
|
impl ApplicationHandler for WindowState {
|
||||||
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||||
tracing::info!("winit::resumed");
|
tracing::debug!("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(
|
||||||
|
@ -165,35 +116,27 @@ impl ApplicationHandler for WinitState {
|
||||||
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 => {
|
||||||
| winit::event::WindowEvent::KeyboardInput {
|
tracing::info!(
|
||||||
event:
|
window = u64::from(window_id),
|
||||||
winit::event::KeyEvent {
|
"window close requested"
|
||||||
physical_key:
|
);
|
||||||
winit::keyboard::PhysicalKey::Code(
|
|
||||||
winit::keyboard::KeyCode::KeyQ,
|
self.windows.remove(&window_id);
|
||||||
),
|
self.renderer.window_contexts.remove(&window_id);
|
||||||
state: ElementState::Pressed,
|
|
||||||
repeat: false,
|
if self.windows.is_empty() {
|
||||||
..
|
event_loop.exit();
|
||||||
},
|
}
|
||||||
..
|
|
||||||
} => {
|
|
||||||
self.remove_window(window_id);
|
|
||||||
}
|
}
|
||||||
winit::event::WindowEvent::KeyboardInput {
|
winit::event::WindowEvent::KeyboardInput {
|
||||||
event:
|
event:
|
||||||
|
@ -232,6 +175,9 @@ impl ApplicationHandler for WinitState {
|
||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -241,9 +187,8 @@ impl ApplicationHandler for WinitState {
|
||||||
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 = WinitState::new("Vidya".to_owned(), display);
|
let mut game = WindowState::new("Vidya".to_owned(), display);
|
||||||
ev.run_app(&mut game).unwrap();
|
ev.run_app(&mut game).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,4 @@ 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"
|
||||||
crossbeam = "0.8.4"
|
winit = "0.30.5"
|
||||||
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 }
|
|
||||||
|
|
|
@ -1,14 +1,64 @@
|
||||||
use std::{future::Future, marker::PhantomData, sync::Arc};
|
use std::{future::Future, marker::PhantomData};
|
||||||
|
|
||||||
use crate::sync::{self, FenceFuture};
|
use super::{Device2 as Device, Queue};
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -20,27 +70,15 @@ impl Drop for SingleUseCommandPool {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SingleUseCommandPool {
|
impl SingleUseCommandPool {
|
||||||
pub fn new(device: Device, queue: Queue) -> VkResult<Arc<Self>> {
|
pub fn new(device: Device, family_index: u32) -> VkResult<Self> {
|
||||||
let pool_info = vk::CommandPoolCreateInfo::default()
|
let pool_info = vk::CommandPoolCreateInfo::default()
|
||||||
.queue_family_index(queue.family())
|
.queue_family_index(family_index)
|
||||||
.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(Arc::new(Self {
|
Ok(Self { device, pool })
|
||||||
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 {
|
||||||
|
@ -50,7 +88,7 @@ impl SingleUseCommandPool {
|
||||||
|
|
||||||
pub struct SingleUseCommand {
|
pub struct SingleUseCommand {
|
||||||
device: Device,
|
device: Device,
|
||||||
pool: Arc<SingleUseCommandPool>,
|
pool: vk::CommandPool,
|
||||||
buffer: vk::CommandBuffer,
|
buffer: vk::CommandBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,28 +97,23 @@ impl Drop for SingleUseCommand {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device
|
self.device
|
||||||
.dev()
|
.dev()
|
||||||
.free_command_buffers(self.pool.pool(), &[self.buffer])
|
.free_command_buffers(self.pool, &[self.buffer])
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SingleUseCommand {
|
impl SingleUseCommand {
|
||||||
pub fn new(
|
pub fn new(device: Device, pool: vk::CommandPool) -> VkResult<Self> {
|
||||||
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.pool())
|
.command_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];
|
||||||
|
|
||||||
device.dev().begin_command_buffer(
|
let begin_info = vk::CommandBufferBeginInfo::default()
|
||||||
buffer,
|
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
|
||||||
&vk::CommandBufferBeginInfo::default()
|
device.dev().begin_command_buffer(buffer, &begin_info)?;
|
||||||
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
};
|
};
|
||||||
|
@ -91,16 +124,16 @@ impl SingleUseCommand {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer(&self) -> vk::CommandBuffer {
|
pub fn command_buffer(&self) -> vk::CommandBuffer {
|
||||||
self.buffer
|
self.buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn submit_fence(
|
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>,
|
||||||
fence: Option<vk::Fence>,
|
) -> VkResult<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];
|
||||||
|
@ -121,39 +154,43 @@ impl SingleUseCommand {
|
||||||
.wait_dst_stage_mask(core::slice::from_ref(stage));
|
.wait_dst_stage_mask(core::slice::from_ref(stage));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fence = fence.unwrap_or(vk::Fence::null());
|
let fence = {
|
||||||
self.pool.queue().with_locked(|queue| unsafe {
|
let create_info = vk::FenceCreateInfo::default();
|
||||||
|
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(())
|
Ok(fence)
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
self.submit_fence(wait, signal, Some(fence.fence()))?;
|
let fence = self.submit_fence(queue, wait, signal)?;
|
||||||
|
|
||||||
Ok(unsafe { FenceFuture::new(fence) })
|
Ok(unsafe { FenceFuture::from_fence(device, 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 fence = Arc::new(sync::Fence::create(self.device.clone())?);
|
let device = self.device.clone();
|
||||||
let future = self.submit_async(wait, signal, fence)?;
|
let fence = self.submit_fence(queue, wait, signal)?;
|
||||||
future.block();
|
|
||||||
|
unsafe {
|
||||||
|
device.dev().wait_for_fences(&[fence], true, u64::MAX)?;
|
||||||
|
device.dev().destroy_fence(fence, None);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,12 +200,6 @@ 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(
|
cmd.submit_async(queue, None, None).unwrap().await;
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Arc::new(sync::Fence::create(cmd.device.clone()).unwrap()),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{Device, Queue, VkAllocator};
|
use super::{Device2 as 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
|
@ -1,27 +0,0 @@
|
||||||
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 {
|
|
||||||
}
|
|
|
@ -1,239 +1,11 @@
|
||||||
use std::{
|
use super::Device2 as Device;
|
||||||
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()
|
||||||
|
@ -268,50 +40,3 @@ 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue