Compare commits
	
		
			3 commits
		
	
	
		
			f7e6a92018
			...
			e8bcaae2a7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							
							
								
								 | 
						e8bcaae2a7 | ||
| 
							
							
								
								 | 
						81f1ee1f96 | ||
| 
							
							
								
								 | 
						f0fff72bce | 
| 
						 | 
				
			
			@ -11,8 +11,8 @@ members = [
 | 
			
		|||
anyhow = "1.0.89"
 | 
			
		||||
ash = "0.38.0"
 | 
			
		||||
ash-window = "0.13.0"
 | 
			
		||||
glam = "0.29.0"
 | 
			
		||||
thiserror = "1.0.64"
 | 
			
		||||
glam = {version = "0.29.0", features = ["bytemuck"]}
 | 
			
		||||
thiserror = "2.0"
 | 
			
		||||
tracing = "0.1.40"
 | 
			
		||||
tracing-subscriber = "0.3.18"
 | 
			
		||||
vk-mem = "0.4.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -28,5 +28,5 @@ 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"
 | 
			
		||||
egui = "0.30"
 | 
			
		||||
egui_winit_platform = "0.25"
 | 
			
		||||
| 
						 | 
				
			
			@ -11,3 +11,4 @@ renderer = { path = "../renderer" }
 | 
			
		|||
 | 
			
		||||
egui = { workspace = true }
 | 
			
		||||
egui_winit_platform = { workspace = true }
 | 
			
		||||
egui_demo_lib = "0.30.0"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ use std::collections::BTreeMap;
 | 
			
		|||
 | 
			
		||||
use renderer::Renderer;
 | 
			
		||||
use tracing::info;
 | 
			
		||||
use tracing_subscriber::EnvFilter;
 | 
			
		||||
use winit::{
 | 
			
		||||
    application::ApplicationHandler,
 | 
			
		||||
    dpi::{LogicalSize, PhysicalSize},
 | 
			
		||||
| 
						 | 
				
			
			@ -14,10 +15,7 @@ use winit::{
 | 
			
		|||
struct WindowState {
 | 
			
		||||
    window: Window,
 | 
			
		||||
    egui_platform: egui_winit_platform::Platform,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct EguiRenderState {
 | 
			
		||||
    textures: BTreeMap<u64, ()>,
 | 
			
		||||
    demo_app: egui_demo_lib::DemoWindows,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct WinitState {
 | 
			
		||||
| 
						 | 
				
			
			@ -37,20 +35,13 @@ impl WinitState {
 | 
			
		|||
            window_attrs: WindowAttributes::default()
 | 
			
		||||
                .with_title(window_title)
 | 
			
		||||
                .with_resizable(true)
 | 
			
		||||
                .with_inner_size(LogicalSize::new(
 | 
			
		||||
                    Self::BASE_WIDTH,
 | 
			
		||||
                    Self::BASE_HEIGHT,
 | 
			
		||||
                )),
 | 
			
		||||
                .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
 | 
			
		||||
            renderer: Renderer::new(display.as_raw()).expect("renderer"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn handle_final_resize(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        window_id: WindowId,
 | 
			
		||||
        new_size: PhysicalSize<u32>,
 | 
			
		||||
    ) {
 | 
			
		||||
    fn handle_final_resize(&mut self, window_id: WindowId, new_size: PhysicalSize<u32>) {
 | 
			
		||||
        _ = (window_id, new_size);
 | 
			
		||||
        info!("TODO: implement resize events");
 | 
			
		||||
        if let Some(ctx) = self.renderer.window_contexts.get_mut(&window_id) {
 | 
			
		||||
| 
						 | 
				
			
			@ -71,21 +62,19 @@ impl WinitState {
 | 
			
		|||
        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));
 | 
			
		||||
            window.egui_platform.begin_pass();
 | 
			
		||||
            window.demo_app.ui(&window.egui_platform.context());
 | 
			
		||||
            let output = window.egui_platform.end_pass(Some(&window.window));
 | 
			
		||||
 | 
			
		||||
            let _draw_data = window
 | 
			
		||||
                .egui_platform
 | 
			
		||||
                .context()
 | 
			
		||||
                .tessellate(output.shapes, output.pixels_per_point);
 | 
			
		||||
            let egui_state = self
 | 
			
		||||
                .renderer
 | 
			
		||||
                .draw_egui(&window.egui_platform.context(), output)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
 | 
			
		||||
            // rendering
 | 
			
		||||
            self.renderer
 | 
			
		||||
                .debug_draw(
 | 
			
		||||
                    &window_id,
 | 
			
		||||
                    || { // window.window.pre_present_notify()
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
                .debug_draw(&window_id, || { // window.window.pre_present_notify()
 | 
			
		||||
                })
 | 
			
		||||
                .expect("drawing");
 | 
			
		||||
            window.window.request_redraw();
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -98,10 +87,7 @@ impl WinitState {
 | 
			
		|||
        self.renderer.window_contexts.remove(&window_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn create_window(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        event_loop: &winit::event_loop::ActiveEventLoop,
 | 
			
		||||
    ) {
 | 
			
		||||
    fn create_window(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
 | 
			
		||||
        let window = event_loop
 | 
			
		||||
            .create_window(
 | 
			
		||||
                self.window_attrs
 | 
			
		||||
| 
						 | 
				
			
			@ -132,6 +118,7 @@ impl WinitState {
 | 
			
		|||
            window_id,
 | 
			
		||||
            WindowState {
 | 
			
		||||
                window,
 | 
			
		||||
                demo_app: egui_demo_lib::DemoWindows::default(),
 | 
			
		||||
                egui_platform: egui_winit_platform::Platform::new(
 | 
			
		||||
                    egui_winit_platform::PlatformDescriptor {
 | 
			
		||||
                        physical_width: size.width,
 | 
			
		||||
| 
						 | 
				
			
			@ -152,10 +139,7 @@ impl ApplicationHandler for WinitState {
 | 
			
		|||
        self.create_window(event_loop);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn about_to_wait(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        event_loop: &winit::event_loop::ActiveEventLoop,
 | 
			
		||||
    ) {
 | 
			
		||||
    fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
 | 
			
		||||
        tracing::info!("winit::about_to_wait");
 | 
			
		||||
        for (&window, &resize) in self.last_resize_events.clone().iter() {
 | 
			
		||||
            self.handle_final_resize(window, resize);
 | 
			
		||||
| 
						 | 
				
			
			@ -192,9 +176,7 @@ impl ApplicationHandler for WinitState {
 | 
			
		|||
                event:
 | 
			
		||||
                    winit::event::KeyEvent {
 | 
			
		||||
                        physical_key:
 | 
			
		||||
                            winit::keyboard::PhysicalKey::Code(
 | 
			
		||||
                                winit::keyboard::KeyCode::KeyQ,
 | 
			
		||||
                            ),
 | 
			
		||||
                            winit::keyboard::PhysicalKey::Code(winit::keyboard::KeyCode::KeyQ),
 | 
			
		||||
                        state: ElementState::Pressed,
 | 
			
		||||
                        repeat: false,
 | 
			
		||||
                        ..
 | 
			
		||||
| 
						 | 
				
			
			@ -207,9 +189,7 @@ impl ApplicationHandler for WinitState {
 | 
			
		|||
                event:
 | 
			
		||||
                    winit::event::KeyEvent {
 | 
			
		||||
                        physical_key:
 | 
			
		||||
                            winit::keyboard::PhysicalKey::Code(
 | 
			
		||||
                                winit::keyboard::KeyCode::Space,
 | 
			
		||||
                            ),
 | 
			
		||||
                            winit::keyboard::PhysicalKey::Code(winit::keyboard::KeyCode::Space),
 | 
			
		||||
                        state: ElementState::Pressed,
 | 
			
		||||
                        repeat: false,
 | 
			
		||||
                        ..
 | 
			
		||||
| 
						 | 
				
			
			@ -247,7 +227,9 @@ impl ApplicationHandler for WinitState {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    tracing_subscriber::fmt().init();
 | 
			
		||||
    let _ = tracing_subscriber::fmt()
 | 
			
		||||
        .with_env_filter(EnvFilter::from_default_env())
 | 
			
		||||
        .init();
 | 
			
		||||
    let ev = EventLoop::new().unwrap();
 | 
			
		||||
    ev.set_control_flow(winit::event_loop::ControlFlow::Poll);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,8 +11,8 @@ dyn-clone = "1"
 | 
			
		|||
anyhow = "1.0.89"
 | 
			
		||||
ash = "0.38.0"
 | 
			
		||||
ash-window = "0.13.0"
 | 
			
		||||
glam = "0.29.0"
 | 
			
		||||
thiserror = "1.0.64"
 | 
			
		||||
glam = {workspace = true}
 | 
			
		||||
thiserror = {workspace = true} 
 | 
			
		||||
tracing = "0.1.40"
 | 
			
		||||
tracing-subscriber = "0.3.18"
 | 
			
		||||
vk-mem = "0.4.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -23,5 +23,7 @@ smol.workspace = true
 | 
			
		|||
tracing-test = "0.2.5"
 | 
			
		||||
 | 
			
		||||
raw-window-handle = { workspace = true }
 | 
			
		||||
egui = { workspace = true }
 | 
			
		||||
egui = { workspace = true , features = ["bytemuck"]}
 | 
			
		||||
egui_winit_platform = { workspace = true }
 | 
			
		||||
bytemuck = { version = "1.21.0", features = ["derive"] }
 | 
			
		||||
indexmap = "2.7.0"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										8
									
								
								crates/renderer/shaders/compile.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										8
									
								
								crates/renderer/shaders/compile.sh
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
SLANGC="/opt/shader-slang-bin/bin/slangc"
 | 
			
		||||
 | 
			
		||||
$SLANGC egui.slang -profile glsl_450 -target spirv -o egui_vert.spv -entry vertex
 | 
			
		||||
$SLANGC egui.slang -profile glsl_450 -target spirv -o egui_frag.spv -entry fragment
 | 
			
		||||
							
								
								
									
										55
									
								
								crates/renderer/shaders/egui.slang
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								crates/renderer/shaders/egui.slang
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
struct Fragment {
 | 
			
		||||
    float4 color : SV_Target;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct VertexIn {
 | 
			
		||||
	[[vk::layout(0)]] float2 pos;
 | 
			
		||||
	[[vk::layout(1)]] float2 uv;
 | 
			
		||||
	[[vk::layout(2)]] float4 color;
 | 
			
		||||
}
 | 
			
		||||
struct VertexOut {
 | 
			
		||||
	[[vk::layout(0)]] float4 color;
 | 
			
		||||
	[[vk::layout(1)]] float2 uv;
 | 
			
		||||
	[[vk::layout(2), flat]] uint draw_id;
 | 
			
		||||
	float4 position : SV_Position;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct PushConstant {
 | 
			
		||||
	float2 screen_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[vk::push_constant]]
 | 
			
		||||
ConstantBuffer<PushConstant> push_constant;
 | 
			
		||||
 | 
			
		||||
[shader("vertex")]
 | 
			
		||||
VertexOut vertex(VertexIn vertex, uint draw_id : SV_DrawIndex) {
 | 
			
		||||
    VertexOut output;
 | 
			
		||||
 | 
			
		||||
    output.position = float4(
 | 
			
		||||
	    2.0 * vertex.pos.x / push_constant.screen_size.x - 1.0,
 | 
			
		||||
	    2.0 * vertex.pos.y / push_constant.screen_size.y - 1.0,
 | 
			
		||||
               0.0,
 | 
			
		||||
1.0,
 | 
			
		||||
    );
 | 
			
		||||
    output.color = vertex.color;
 | 
			
		||||
    output.uv = vertex.uv;
 | 
			
		||||
    output.draw_id = draw_id;
 | 
			
		||||
 | 
			
		||||
	return output;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[vk::binding(0)]]
 | 
			
		||||
Sampler2D texture[];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[[vk::binding(1)]]
 | 
			
		||||
StructuredBuffer<uint> texture_ids;
 | 
			
		||||
 | 
			
		||||
[shader("fragment")]
 | 
			
		||||
Fragment fragment(VertexOut input) {
 | 
			
		||||
    Fragment output;
 | 
			
		||||
 | 
			
		||||
    uint texture_id = texture_ids[input.draw_id];
 | 
			
		||||
    output.color = input.color * texture[texture_id].Sample(input.uv);
 | 
			
		||||
	return output;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								crates/renderer/shaders/egui_frag.spv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								crates/renderer/shaders/egui_frag.spv
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								crates/renderer/shaders/egui_vert.spv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								crates/renderer/shaders/egui_vert.spv
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -1,25 +1,21 @@
 | 
			
		|||
use std::{ops::Deref, sync::Arc};
 | 
			
		||||
use std::{
 | 
			
		||||
    ops::{Deref, DerefMut},
 | 
			
		||||
    sync::Arc,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use ash::{prelude::VkResult, vk};
 | 
			
		||||
use vk_mem::Alloc;
 | 
			
		||||
 | 
			
		||||
use crate::Device;
 | 
			
		||||
use crate::{define_device_owned_handle, device::DeviceOwned, Device};
 | 
			
		||||
 | 
			
		||||
pub struct Buffer {
 | 
			
		||||
    device: Device,
 | 
			
		||||
    buffer: vk::Buffer,
 | 
			
		||||
    allocation: vk_mem::Allocation,
 | 
			
		||||
define_device_owned_handle! {
 | 
			
		||||
    #[derive(Debug)]
 | 
			
		||||
    pub Buffer(vk::Buffer) {
 | 
			
		||||
        alloc: vk_mem::Allocation,
 | 
			
		||||
        usage: vk::BufferUsageFlags,
 | 
			
		||||
        size: u64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for Buffer {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device
 | 
			
		||||
                .alloc()
 | 
			
		||||
                .destroy_buffer(self.buffer, &mut self.allocation);
 | 
			
		||||
        }
 | 
			
		||||
    } => |this|  unsafe {
 | 
			
		||||
        this.device().clone().alloc().destroy_buffer(this.handle(), &mut this.alloc);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -31,16 +27,18 @@ impl Buffer {
 | 
			
		|||
        queue_families: &[u32],
 | 
			
		||||
        memory_usage: vk_mem::MemoryUsage,
 | 
			
		||||
        alloc_flags: vk_mem::AllocationCreateFlags,
 | 
			
		||||
        name: Option<std::borrow::Cow<'static, str>>,
 | 
			
		||||
    ) -> VkResult<Arc<Self>> {
 | 
			
		||||
        let sharing_mode = if queue_families.len() > 1 {
 | 
			
		||||
            vk::SharingMode::EXCLUSIVE
 | 
			
		||||
        } else {
 | 
			
		||||
            vk::SharingMode::CONCURRENT
 | 
			
		||||
        } else {
 | 
			
		||||
            vk::SharingMode::EXCLUSIVE
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let (buffer, allocation) = unsafe {
 | 
			
		||||
            device.alloc().create_buffer(
 | 
			
		||||
                &vk::BufferCreateInfo::default()
 | 
			
		||||
                    .size(size as u64)
 | 
			
		||||
                    .usage(usage)
 | 
			
		||||
                    .queue_family_indices(queue_families)
 | 
			
		||||
                    .sharing_mode(sharing_mode),
 | 
			
		||||
| 
						 | 
				
			
			@ -52,31 +50,53 @@ impl Buffer {
 | 
			
		|||
            )?
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let buffer = Self {
 | 
			
		||||
        Ok(Arc::new(Self::construct(
 | 
			
		||||
            device,
 | 
			
		||||
            buffer,
 | 
			
		||||
            name,
 | 
			
		||||
            allocation,
 | 
			
		||||
            usage,
 | 
			
		||||
            size: size as u64,
 | 
			
		||||
        };
 | 
			
		||||
            size as u64,
 | 
			
		||||
        )?))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        Ok(Arc::new(buffer))
 | 
			
		||||
    pub fn map_arc(self: &mut Arc<Self>) -> VkResult<MappedBuffer<'_>> {
 | 
			
		||||
        Arc::get_mut(self).map(Self::map).unwrap()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn map(&mut self) -> VkResult<MappedBuffer<'_>> {
 | 
			
		||||
        let bytes = unsafe {
 | 
			
		||||
            let data = self.device.alloc().map_memory(&mut self.allocation)?;
 | 
			
		||||
            let slice = core::slice::from_raw_parts(data, self.size as usize);
 | 
			
		||||
            let data = self.inner.dev().alloc().map_memory(&mut self.alloc)?;
 | 
			
		||||
            let slice = core::slice::from_raw_parts_mut(data, self.size as usize);
 | 
			
		||||
 | 
			
		||||
            slice
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Ok(MappedBuffer { bytes })
 | 
			
		||||
        Ok(MappedBuffer { inner: self, bytes })
 | 
			
		||||
    }
 | 
			
		||||
    pub fn buffer(&self) -> vk::Buffer {
 | 
			
		||||
        self.handle()
 | 
			
		||||
    }
 | 
			
		||||
    pub fn len(&self) -> u64 {
 | 
			
		||||
        self.size
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct MappedBuffer<'a> {
 | 
			
		||||
    bytes: &'a [u8],
 | 
			
		||||
    bytes: &'a mut [u8],
 | 
			
		||||
    inner: &'a mut Buffer,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for MappedBuffer<'_> {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.inner
 | 
			
		||||
                .inner
 | 
			
		||||
                .dev()
 | 
			
		||||
                .alloc()
 | 
			
		||||
                .unmap_memory(&mut self.inner.alloc);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Deref for MappedBuffer<'_> {
 | 
			
		||||
| 
						 | 
				
			
			@ -86,3 +106,9 @@ impl Deref for MappedBuffer<'_> {
 | 
			
		|||
        self.bytes
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DerefMut for MappedBuffer<'_> {
 | 
			
		||||
    fn deref_mut(&mut self) -> &mut Self::Target {
 | 
			
		||||
        self.bytes
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,9 +2,11 @@ use std::{future::Future, marker::PhantomData, sync::Arc};
 | 
			
		|||
 | 
			
		||||
use crate::{
 | 
			
		||||
    buffers::Buffer,
 | 
			
		||||
    device::DeviceOwned,
 | 
			
		||||
    images::{Image2D, QueueOwnership},
 | 
			
		||||
    pipeline::{Pipeline, PipelineLayout},
 | 
			
		||||
    sync::{self, FenceFuture},
 | 
			
		||||
    util::{FormatExt, MutexExt},
 | 
			
		||||
    util::{self, FormatExt, MutexExt},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::{Device, Queue};
 | 
			
		||||
| 
						 | 
				
			
			@ -33,8 +35,7 @@ impl SingleUseCommandPool {
 | 
			
		|||
            .queue_family_index(queue.family())
 | 
			
		||||
            .flags(vk::CommandPoolCreateFlags::TRANSIENT);
 | 
			
		||||
 | 
			
		||||
        let pool =
 | 
			
		||||
            unsafe { device.dev().create_command_pool(&pool_info, None)? };
 | 
			
		||||
        let pool = unsafe { device.dev().create_command_pool(&pool_info, None)? };
 | 
			
		||||
 | 
			
		||||
        Ok(Arc::new(Self {
 | 
			
		||||
            device,
 | 
			
		||||
| 
						 | 
				
			
			@ -68,18 +69,15 @@ impl !Sync for SingleUseCommand {}
 | 
			
		|||
impl Drop for SingleUseCommand {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.pool.pool.with_locked(|&pool| {
 | 
			
		||||
                self.device.dev().free_command_buffers(pool, &[self.buffer])
 | 
			
		||||
            })
 | 
			
		||||
            self.pool
 | 
			
		||||
                .pool
 | 
			
		||||
                .with_locked(|&pool| self.device.dev().free_command_buffers(pool, &[self.buffer]))
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SingleUseCommand {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        device: Device,
 | 
			
		||||
        pool: Arc<SingleUseCommandPool>,
 | 
			
		||||
    ) -> VkResult<Self> {
 | 
			
		||||
    pub fn new(device: Device, pool: Arc<SingleUseCommandPool>) -> VkResult<Self> {
 | 
			
		||||
        let buffer = unsafe {
 | 
			
		||||
            let alloc_info = vk::CommandBufferAllocateInfo::default()
 | 
			
		||||
                .command_buffer_count(1)
 | 
			
		||||
| 
						 | 
				
			
			@ -154,6 +152,60 @@ impl SingleUseCommand {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn blit_images(
 | 
			
		||||
        &self,
 | 
			
		||||
        src: &Image2D,
 | 
			
		||||
        src_region: util::Rect2D,
 | 
			
		||||
        dst: &Image2D,
 | 
			
		||||
        dst_region: util::Rect2D,
 | 
			
		||||
    ) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device.dev().cmd_blit_image(
 | 
			
		||||
                self.buffer,
 | 
			
		||||
                src.image(),
 | 
			
		||||
                vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
 | 
			
		||||
                dst.image(),
 | 
			
		||||
                vk::ImageLayout::TRANSFER_DST_OPTIMAL,
 | 
			
		||||
                &[vk::ImageBlit::default()
 | 
			
		||||
                    .src_subresource(
 | 
			
		||||
                        vk::ImageSubresourceLayers::default()
 | 
			
		||||
                            .aspect_mask(vk::ImageAspectFlags::COLOR)
 | 
			
		||||
                            .layer_count(1),
 | 
			
		||||
                    )
 | 
			
		||||
                    .dst_subresource(
 | 
			
		||||
                        vk::ImageSubresourceLayers::default()
 | 
			
		||||
                            .aspect_mask(vk::ImageAspectFlags::COLOR)
 | 
			
		||||
                            .layer_count(1),
 | 
			
		||||
                    )
 | 
			
		||||
                    .src_offsets(src_region.into_offsets_3d())
 | 
			
		||||
                    .dst_offsets(dst_region.into_offsets_3d())],
 | 
			
		||||
                vk::Filter::LINEAR,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn copy_buffer_to_image(
 | 
			
		||||
        &self,
 | 
			
		||||
        buffer: vk::Buffer,
 | 
			
		||||
        image: vk::Image,
 | 
			
		||||
        layout: vk::ImageLayout,
 | 
			
		||||
        regions: &[vk::BufferImageCopy],
 | 
			
		||||
    ) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device
 | 
			
		||||
                .dev()
 | 
			
		||||
                .cmd_copy_buffer_to_image(self.buffer, buffer, image, layout, regions);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn copy_buffers(&self, src: vk::Buffer, dst: vk::Buffer, regions: &[vk::BufferCopy]) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device
 | 
			
		||||
                .dev()
 | 
			
		||||
                .cmd_copy_buffer(self.buffer, src, dst, regions);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn clear_color_image(
 | 
			
		||||
        &self,
 | 
			
		||||
        image: vk::Image,
 | 
			
		||||
| 
						 | 
				
			
			@ -185,6 +237,125 @@ impl SingleUseCommand {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn begin_rendering(&self, rendering_info: vk::RenderingInfo<'_>) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device
 | 
			
		||||
                .dev()
 | 
			
		||||
                .cmd_begin_rendering(self.buffer(), &rendering_info);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_viewport(&self, viewports: &[vk::Viewport]) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device
 | 
			
		||||
                .dev()
 | 
			
		||||
                .cmd_set_viewport(self.buffer(), 0, viewports);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn set_scissors(&self, scissors: &[vk::Rect2D]) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device
 | 
			
		||||
                .dev()
 | 
			
		||||
                .cmd_set_scissor(self.buffer(), 0, scissors);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn push_constants(
 | 
			
		||||
        &self,
 | 
			
		||||
        layout: &PipelineLayout,
 | 
			
		||||
        stage: vk::ShaderStageFlags,
 | 
			
		||||
        offset: u32,
 | 
			
		||||
        bytes: &[u8],
 | 
			
		||||
    ) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device.dev().cmd_push_constants(
 | 
			
		||||
                self.buffer,
 | 
			
		||||
                layout.handle(),
 | 
			
		||||
                stage,
 | 
			
		||||
                offset,
 | 
			
		||||
                bytes,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn bind_pipeline(&self, pipeline: &Pipeline) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device.dev().cmd_bind_pipeline(
 | 
			
		||||
                self.buffer(),
 | 
			
		||||
                pipeline.bind_point(),
 | 
			
		||||
                pipeline.handle(),
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn bind_vertices(&self, buffer: vk::Buffer, offset: u64) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device
 | 
			
		||||
                .dev()
 | 
			
		||||
                .cmd_bind_vertex_buffers(self.buffer(), 0, &[buffer], &[offset]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn bind_indices(&self, buffer: vk::Buffer, offset: u64, kind: vk::IndexType) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device
 | 
			
		||||
                .dev()
 | 
			
		||||
                .cmd_bind_index_buffer(self.buffer(), buffer, offset, kind);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn bind_descriptor_sets(
 | 
			
		||||
        &self,
 | 
			
		||||
        layout: &PipelineLayout,
 | 
			
		||||
        bind_point: vk::PipelineBindPoint,
 | 
			
		||||
        descriptor_sets: &[vk::DescriptorSet],
 | 
			
		||||
    ) {
 | 
			
		||||
        use crate::device::DeviceOwned;
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device.dev().cmd_bind_descriptor_sets(
 | 
			
		||||
                self.buffer(),
 | 
			
		||||
                bind_point,
 | 
			
		||||
                layout.handle(),
 | 
			
		||||
                0,
 | 
			
		||||
                descriptor_sets,
 | 
			
		||||
                &[],
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn draw_indexed(
 | 
			
		||||
        &self,
 | 
			
		||||
        indices: u32,
 | 
			
		||||
        instances: u32,
 | 
			
		||||
        index_offset: u32,
 | 
			
		||||
        vertex_offset: i32,
 | 
			
		||||
        instance_offset: u32,
 | 
			
		||||
    ) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device.dev().cmd_draw_indexed(
 | 
			
		||||
                self.buffer(),
 | 
			
		||||
                indices,
 | 
			
		||||
                instances,
 | 
			
		||||
                index_offset,
 | 
			
		||||
                vertex_offset,
 | 
			
		||||
                instance_offset,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn draw_indexed_indirect(&self, buffer: vk::Buffer, offset: u64, count: u32, stride: u32) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device.dev().cmd_draw_indexed_indirect(
 | 
			
		||||
                self.buffer(),
 | 
			
		||||
                buffer,
 | 
			
		||||
                offset,
 | 
			
		||||
                count,
 | 
			
		||||
                stride,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn end_rendering(&self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device.dev().cmd_end_rendering(self.buffer());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn submit_fence(
 | 
			
		||||
        &self,
 | 
			
		||||
        wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
 | 
			
		||||
| 
						 | 
				
			
			@ -194,15 +365,12 @@ impl SingleUseCommand {
 | 
			
		|||
        unsafe { self.device.dev().end_command_buffer(self.buffer)? };
 | 
			
		||||
 | 
			
		||||
        let buffers = [self.buffer];
 | 
			
		||||
        let mut submit_info =
 | 
			
		||||
            vk::SubmitInfo::default().command_buffers(&buffers);
 | 
			
		||||
        let mut submit_info = vk::SubmitInfo::default().command_buffers(&buffers);
 | 
			
		||||
 | 
			
		||||
        if let Some(semaphore) = signal.as_ref() {
 | 
			
		||||
            // SAFETY: T and [T;1] have the same layout
 | 
			
		||||
            submit_info = submit_info.signal_semaphores(unsafe {
 | 
			
		||||
                core::mem::transmute::<&vk::Semaphore, &[vk::Semaphore; 1]>(
 | 
			
		||||
                    semaphore,
 | 
			
		||||
                )
 | 
			
		||||
                core::mem::transmute::<&vk::Semaphore, &[vk::Semaphore; 1]>(semaphore)
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        if let Some((semaphore, stage)) = wait.as_ref() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										404
									
								
								crates/renderer/src/device.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										404
									
								
								crates/renderer/src/device.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,404 @@
 | 
			
		|||
use std::{
 | 
			
		||||
    borrow::Cow,
 | 
			
		||||
    collections::{BTreeMap, HashMap},
 | 
			
		||||
    ops::Deref,
 | 
			
		||||
    sync::Arc,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use ash::{
 | 
			
		||||
    khr,
 | 
			
		||||
    prelude::VkResult,
 | 
			
		||||
    vk::{self, Handle},
 | 
			
		||||
};
 | 
			
		||||
use parking_lot::Mutex;
 | 
			
		||||
use tinyvec::{array_vec, ArrayVec};
 | 
			
		||||
 | 
			
		||||
use crate::{sync, Instance, PhysicalDevice, Queue};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct DeviceQueueFamilies {
 | 
			
		||||
    pub(crate) families: Vec<(u32, u32)>,
 | 
			
		||||
    pub(crate) graphics: (u32, u32),
 | 
			
		||||
    pub(crate) present: (u32, u32),
 | 
			
		||||
    pub(crate) async_compute: (u32, u32),
 | 
			
		||||
    pub(crate) transfer: (u32, u32),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DeviceQueueFamilies {
 | 
			
		||||
    pub fn swapchain_family_indices(&self) -> ArrayVec<[u32; 2]> {
 | 
			
		||||
        let mut indices = array_vec!([u32; 2] => self.graphics.0);
 | 
			
		||||
 | 
			
		||||
        if self.present.0 != self.graphics.0 {
 | 
			
		||||
            indices.push(self.present.0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        indices
 | 
			
		||||
    }
 | 
			
		||||
    pub fn graphics_familty(&self) -> u32 {
 | 
			
		||||
        self.graphics.0
 | 
			
		||||
    }
 | 
			
		||||
    pub fn present_familty(&self) -> u32 {
 | 
			
		||||
        self.present.0
 | 
			
		||||
    }
 | 
			
		||||
    pub fn async_compute_familty(&self) -> u32 {
 | 
			
		||||
        self.async_compute.0
 | 
			
		||||
    }
 | 
			
		||||
    pub fn transfer_familty(&self) -> u32 {
 | 
			
		||||
        self.transfer.0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[repr(transparent)]
 | 
			
		||||
struct DeviceWrapper(ash::Device);
 | 
			
		||||
 | 
			
		||||
impl Deref for DeviceWrapper {
 | 
			
		||||
    type Target = ash::Device;
 | 
			
		||||
 | 
			
		||||
    fn deref(&self) -> &Self::Target {
 | 
			
		||||
        &self.0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for DeviceWrapper {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            _ = self.0.device_wait_idle();
 | 
			
		||||
            self.0.destroy_device(None);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct DeviceInner {
 | 
			
		||||
    alloc: vk_mem::Allocator,
 | 
			
		||||
    device: DeviceWrapper,
 | 
			
		||||
    physical: PhysicalDevice,
 | 
			
		||||
    instance: Arc<Instance>,
 | 
			
		||||
    swapchain: khr::swapchain::Device,
 | 
			
		||||
    debug_utils: ash::ext::debug_utils::Device,
 | 
			
		||||
    allocated_queues: BTreeMap<(u32, u32), Queue>,
 | 
			
		||||
    // these are resident in allocated_queues, and may in fact be clones of each
 | 
			
		||||
    // other, for ease of access
 | 
			
		||||
    main_queue: Queue,
 | 
			
		||||
    compute_queue: Queue,
 | 
			
		||||
    transfer_queue: Queue,
 | 
			
		||||
    present_queue: Queue,
 | 
			
		||||
    sync_threadpool: sync::SyncThreadpool,
 | 
			
		||||
    features: crate::PhysicalDeviceFeatures,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl core::fmt::Debug for DeviceInner {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        f.debug_struct("DeviceInner")
 | 
			
		||||
            .field("device", &self.device.handle())
 | 
			
		||||
            .finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct Device(Arc<DeviceInner>);
 | 
			
		||||
pub type WeakDevice = std::sync::Weak<DeviceInner>;
 | 
			
		||||
 | 
			
		||||
impl Device {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        instance: Arc<Instance>,
 | 
			
		||||
        physical: PhysicalDevice,
 | 
			
		||||
        mut features: crate::PhysicalDeviceFeatures,
 | 
			
		||||
    ) -> VkResult<Self> {
 | 
			
		||||
        // we have 4 queues at most: graphics, compute, transfer, present
 | 
			
		||||
        let priorities = [1.0f32; 4];
 | 
			
		||||
 | 
			
		||||
        let queue_infos = physical
 | 
			
		||||
            .queue_families
 | 
			
		||||
            .families
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|&(family, queues)| {
 | 
			
		||||
                vk::DeviceQueueCreateInfo::default()
 | 
			
		||||
                    .queue_family_index(family)
 | 
			
		||||
                    .queue_priorities(&priorities[..queues as usize])
 | 
			
		||||
            })
 | 
			
		||||
            .collect::<Vec<_>>();
 | 
			
		||||
 | 
			
		||||
        let extensions = features
 | 
			
		||||
            .device_extensions
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|ext| ext.extension_name.as_ptr())
 | 
			
		||||
            .collect::<Vec<_>>();
 | 
			
		||||
 | 
			
		||||
        let mut features2 = features.features2();
 | 
			
		||||
        let device_info = vk::DeviceCreateInfo::default()
 | 
			
		||||
            .queue_create_infos(&queue_infos)
 | 
			
		||||
            .enabled_extension_names(&extensions)
 | 
			
		||||
            .push_next(&mut features2);
 | 
			
		||||
 | 
			
		||||
        let device = unsafe {
 | 
			
		||||
            let device = instance
 | 
			
		||||
                .instance
 | 
			
		||||
                .create_device(physical.pdev, &device_info, None)?;
 | 
			
		||||
 | 
			
		||||
            let allocated_queues = queue_infos
 | 
			
		||||
                .iter()
 | 
			
		||||
                .flat_map(|info| {
 | 
			
		||||
                    (0..info.queue_count).map(|i| {
 | 
			
		||||
                        (
 | 
			
		||||
                            (info.queue_family_index, i),
 | 
			
		||||
                            Queue::new(&device, info.queue_family_index, i),
 | 
			
		||||
                        )
 | 
			
		||||
                    })
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<BTreeMap<_, _>>();
 | 
			
		||||
 | 
			
		||||
            let get_queue =
 | 
			
		||||
                |(family, index)| allocated_queues.get(&(family, index)).cloned().unwrap();
 | 
			
		||||
 | 
			
		||||
            let main_queue = get_queue(physical.queue_families.graphics);
 | 
			
		||||
            let present_queue = get_queue(physical.queue_families.present);
 | 
			
		||||
            let compute_queue = get_queue(physical.queue_families.async_compute);
 | 
			
		||||
            let transfer_queue = get_queue(physical.queue_families.transfer);
 | 
			
		||||
 | 
			
		||||
            let alloc_info =
 | 
			
		||||
                vk_mem::AllocatorCreateInfo::new(&instance.instance, &device, physical.pdev);
 | 
			
		||||
 | 
			
		||||
            let alloc = unsafe { vk_mem::Allocator::new(alloc_info)? };
 | 
			
		||||
 | 
			
		||||
            DeviceInner {
 | 
			
		||||
                device: DeviceWrapper(device.clone()),
 | 
			
		||||
                physical,
 | 
			
		||||
                swapchain: khr::swapchain::Device::new(&instance.instance, &device),
 | 
			
		||||
                debug_utils: ash::ext::debug_utils::Device::new(&instance.instance, &device),
 | 
			
		||||
                instance,
 | 
			
		||||
                alloc,
 | 
			
		||||
                allocated_queues,
 | 
			
		||||
                main_queue,
 | 
			
		||||
                present_queue,
 | 
			
		||||
                compute_queue,
 | 
			
		||||
                transfer_queue,
 | 
			
		||||
                features,
 | 
			
		||||
                sync_threadpool: sync::SyncThreadpool::new(),
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Ok(Self(Arc::new(device)))
 | 
			
		||||
    }
 | 
			
		||||
    pub fn sync_threadpool(&self) -> &sync::SyncThreadpool {
 | 
			
		||||
        &self.0.sync_threadpool
 | 
			
		||||
    }
 | 
			
		||||
    pub fn weak(&self) -> WeakDevice {
 | 
			
		||||
        Arc::downgrade(&self.0)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn alloc(&self) -> &vk_mem::Allocator {
 | 
			
		||||
        &self.0.alloc
 | 
			
		||||
    }
 | 
			
		||||
    pub fn dev(&self) -> &ash::Device {
 | 
			
		||||
        &self.0.device
 | 
			
		||||
    }
 | 
			
		||||
    pub fn swapchain(&self) -> &khr::swapchain::Device {
 | 
			
		||||
        &self.0.swapchain
 | 
			
		||||
    }
 | 
			
		||||
    pub fn debug_utils(&self) -> &ash::ext::debug_utils::Device {
 | 
			
		||||
        &self.0.debug_utils
 | 
			
		||||
    }
 | 
			
		||||
    pub fn queue_families(&self) -> &DeviceQueueFamilies {
 | 
			
		||||
        &self.0.physical.queue_families
 | 
			
		||||
    }
 | 
			
		||||
    pub fn phy(&self) -> vk::PhysicalDevice {
 | 
			
		||||
        self.0.physical.pdev
 | 
			
		||||
    }
 | 
			
		||||
    pub fn features(&self) -> &crate::PhysicalDeviceFeatures {
 | 
			
		||||
        &self.0.features
 | 
			
		||||
    }
 | 
			
		||||
    pub fn physical_device(&self) -> &PhysicalDevice {
 | 
			
		||||
        &self.0.physical
 | 
			
		||||
    }
 | 
			
		||||
    pub fn graphics_queue(&self) -> &Queue {
 | 
			
		||||
        &self.0.main_queue
 | 
			
		||||
    }
 | 
			
		||||
    pub fn present_queue(&self) -> &Queue {
 | 
			
		||||
        &self.0.present_queue
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub unsafe fn lock_queues(&self) {
 | 
			
		||||
        // this is obviously awful, allocating for this
 | 
			
		||||
        self.0
 | 
			
		||||
            .allocated_queues
 | 
			
		||||
            .values()
 | 
			
		||||
            .for_each(|q| core::mem::forget(q.lock()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub unsafe fn unlock_queues(&self) {
 | 
			
		||||
        self.0
 | 
			
		||||
            .allocated_queues
 | 
			
		||||
            .values()
 | 
			
		||||
            .for_each(|q| unsafe { q.0.force_unlock() });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn wait_queue_idle(&self, queue: &Queue) -> VkResult<()> {
 | 
			
		||||
        tracing::warn!("locking queue {queue:?} and waiting for idle");
 | 
			
		||||
 | 
			
		||||
        queue.with_locked(|q| unsafe { self.dev().queue_wait_idle(q) })?;
 | 
			
		||||
 | 
			
		||||
        tracing::warn!("finished waiting: unlocking queue {queue:?}.");
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn wait_idle(&self) -> VkResult<()> {
 | 
			
		||||
        tracing::warn!("locking all queues and waiting for device to idle");
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.lock_queues();
 | 
			
		||||
            self.dev().device_wait_idle()?;
 | 
			
		||||
            self.unlock_queues();
 | 
			
		||||
        }
 | 
			
		||||
        tracing::warn!("finished waiting: unlocking all queues.");
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn debug_name_object<T: vk::Handle>(&self, handle: T, name: &str) -> VkResult<()> {
 | 
			
		||||
        let name = std::ffi::CString::new(name.as_bytes()).unwrap_or(c"invalid name".to_owned());
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.debug_utils().set_debug_utils_object_name(
 | 
			
		||||
                &vk::DebugUtilsObjectNameInfoEXT::default()
 | 
			
		||||
                    .object_handle(handle)
 | 
			
		||||
                    .object_name(&name),
 | 
			
		||||
            )?;
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AsRef<khr::swapchain::Device> for Device {
 | 
			
		||||
    fn as_ref(&self) -> &khr::swapchain::Device {
 | 
			
		||||
        &self.0.swapchain
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AsRef<ash::Device> for Device {
 | 
			
		||||
    fn as_ref(&self) -> &ash::Device {
 | 
			
		||||
        &self.0.device
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct DeviceAndQueues {
 | 
			
		||||
    pub(crate) device: Device,
 | 
			
		||||
    pub(crate) main_queue: Queue,
 | 
			
		||||
    pub(crate) compute_queue: Queue,
 | 
			
		||||
    pub(crate) transfer_queue: Queue,
 | 
			
		||||
    pub(crate) present_queue: Queue,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AsRef<ash::Device> for DeviceAndQueues {
 | 
			
		||||
    fn as_ref(&self) -> &ash::Device {
 | 
			
		||||
        &self.device.as_ref()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl AsRef<ash::khr::swapchain::Device> for DeviceAndQueues {
 | 
			
		||||
    fn as_ref(&self) -> &ash::khr::swapchain::Device {
 | 
			
		||||
        &self.device.as_ref()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct DeviceOwnedDebugObject<T> {
 | 
			
		||||
    device: Device,
 | 
			
		||||
    object: T,
 | 
			
		||||
    name: Option<Cow<'static, str>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: std::fmt::Debug + vk::Handle + Copy> std::fmt::Debug for DeviceOwnedDebugObject<T> {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        f.debug_struct(core::any::type_name::<T>())
 | 
			
		||||
            .field_with("device", |f| {
 | 
			
		||||
                write!(f, "0x{:x}", self.device.0.device.handle().as_raw())
 | 
			
		||||
            })
 | 
			
		||||
            .field_with("handle", |f| write!(f, "0x{:x}", &self.object.as_raw()))
 | 
			
		||||
            .field("name", &self.name)
 | 
			
		||||
            .finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> DeviceOwnedDebugObject<T> {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        device: crate::Device,
 | 
			
		||||
        object: T,
 | 
			
		||||
        name: Option<Cow<'static, str>>,
 | 
			
		||||
    ) -> ash::prelude::VkResult<Self>
 | 
			
		||||
    where
 | 
			
		||||
        T: vk::Handle + Copy,
 | 
			
		||||
    {
 | 
			
		||||
        if let Some(name) = name.as_ref() {
 | 
			
		||||
            device.debug_name_object(object, name);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            device,
 | 
			
		||||
            object,
 | 
			
		||||
            name,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn dev(&self) -> &crate::Device {
 | 
			
		||||
        &self.device
 | 
			
		||||
    }
 | 
			
		||||
    pub fn handle(&self) -> T
 | 
			
		||||
    where
 | 
			
		||||
        T: Copy,
 | 
			
		||||
    {
 | 
			
		||||
        self.object
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait DeviceOwned<T> {
 | 
			
		||||
    fn device(&self) -> &Device;
 | 
			
		||||
    fn handle(&self) -> T;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! define_device_owned_handle {
 | 
			
		||||
    ($(#[$attr:meta])*
 | 
			
		||||
        $ty_vis:vis $ty:ident($handle:ty) {
 | 
			
		||||
        $($field_vis:vis $field_name:ident : $field_ty:ty),*
 | 
			
		||||
        $(,)?
 | 
			
		||||
    } $(=> |$this:ident| $dtor:stmt)?) => {
 | 
			
		||||
        $(#[$attr])*
 | 
			
		||||
            $ty_vis struct $ty {
 | 
			
		||||
                inner: crate::device::DeviceOwnedDebugObject<$handle>,
 | 
			
		||||
                $(
 | 
			
		||||
                    $field_vis $field_name: $field_ty,
 | 
			
		||||
                )*
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        impl crate::device::DeviceOwned<$handle> for $ty {
 | 
			
		||||
            fn device(&self) -> &Device {
 | 
			
		||||
                self.inner.dev()
 | 
			
		||||
            }
 | 
			
		||||
            fn handle(&self) -> $handle {
 | 
			
		||||
                self.inner.handle()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl $ty {
 | 
			
		||||
            fn construct(
 | 
			
		||||
                device: crate::device::Device,
 | 
			
		||||
                handle: $handle,
 | 
			
		||||
                name: Option<::std::borrow::Cow<'static, str>>,
 | 
			
		||||
                $($field_name: $field_ty,)*
 | 
			
		||||
            ) -> ::ash::prelude::VkResult<Self> {
 | 
			
		||||
                Ok(Self {
 | 
			
		||||
                    inner: crate::device::DeviceOwnedDebugObject::new(
 | 
			
		||||
                        device,
 | 
			
		||||
                        handle,
 | 
			
		||||
                        name,
 | 
			
		||||
                    )?,
 | 
			
		||||
                    $($field_name,)*
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $(
 | 
			
		||||
            impl Drop for $ty {
 | 
			
		||||
                fn drop(&mut self) {
 | 
			
		||||
                    let mut $this = self;
 | 
			
		||||
                    $dtor
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        )?
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,11 +1,97 @@
 | 
			
		|||
use std::sync::Arc;
 | 
			
		||||
use std::{borrow::Cow, sync::Arc};
 | 
			
		||||
 | 
			
		||||
use crate::buffers::Buffer;
 | 
			
		||||
use crate::{buffers::Buffer, define_device_owned_handle, device::DeviceOwned};
 | 
			
		||||
 | 
			
		||||
use super::{Device, Queue};
 | 
			
		||||
use ash::{prelude::*, vk};
 | 
			
		||||
use vk_mem::Alloc;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default, Clone)]
 | 
			
		||||
pub struct ImageViewDesc {
 | 
			
		||||
    pub flags: vk::ImageViewCreateFlags,
 | 
			
		||||
    pub name: Option<Cow<'static, str>>,
 | 
			
		||||
    pub kind: vk::ImageViewType,
 | 
			
		||||
    pub format: vk::Format,
 | 
			
		||||
    pub components: vk::ComponentMapping,
 | 
			
		||||
    pub aspect: vk::ImageAspectFlags,
 | 
			
		||||
    pub mip_range: MipRange,
 | 
			
		||||
    pub layer_range: MipRange,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
pub struct MipRange(u32, u32);
 | 
			
		||||
 | 
			
		||||
impl Default for MipRange {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self(0, vk::REMAINING_ARRAY_LAYERS)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MipRange {
 | 
			
		||||
    fn count(&self) -> u32 {
 | 
			
		||||
        self.1
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<R: core::ops::RangeBounds<u32>> From<R> for MipRange {
 | 
			
		||||
    fn from(value: R) -> Self {
 | 
			
		||||
        let start = match value.start_bound() {
 | 
			
		||||
            std::ops::Bound::Included(v) => *v,
 | 
			
		||||
            std::ops::Bound::Excluded(v) => *v + 1,
 | 
			
		||||
            std::ops::Bound::Unbounded => 0,
 | 
			
		||||
        };
 | 
			
		||||
        let count = match value.end_bound() {
 | 
			
		||||
            std::ops::Bound::Included(v) => *v + 1 - start,
 | 
			
		||||
            std::ops::Bound::Excluded(v) => *v - start,
 | 
			
		||||
            std::ops::Bound::Unbounded => vk::REMAINING_MIP_LEVELS,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Self(start, count)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::hash::Hash for ImageViewDesc {
 | 
			
		||||
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 | 
			
		||||
        self.flags.hash(state);
 | 
			
		||||
        self.kind.hash(state);
 | 
			
		||||
        self.format.hash(state);
 | 
			
		||||
        (
 | 
			
		||||
            self.components.r,
 | 
			
		||||
            self.components.g,
 | 
			
		||||
            self.components.b,
 | 
			
		||||
            self.components.a,
 | 
			
		||||
        )
 | 
			
		||||
            .hash(state);
 | 
			
		||||
        self.aspect.hash(state);
 | 
			
		||||
        self.layer_range.hash(state);
 | 
			
		||||
        self.mip_range.hash(state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Eq for ImageViewDesc {}
 | 
			
		||||
impl PartialEq for ImageViewDesc {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        self.flags == other.flags
 | 
			
		||||
            && self.kind == other.kind
 | 
			
		||||
            && self.format == other.format
 | 
			
		||||
            && (
 | 
			
		||||
                self.components.r,
 | 
			
		||||
                self.components.g,
 | 
			
		||||
                self.components.b,
 | 
			
		||||
                self.components.a,
 | 
			
		||||
            ) == (
 | 
			
		||||
                other.components.r,
 | 
			
		||||
                other.components.g,
 | 
			
		||||
                other.components.b,
 | 
			
		||||
                other.components.a,
 | 
			
		||||
            )
 | 
			
		||||
            && self.aspect == other.aspect
 | 
			
		||||
            && self.mip_range == other.mip_range
 | 
			
		||||
            && self.layer_range == other.layer_range
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct Image2D {
 | 
			
		||||
    device: Device,
 | 
			
		||||
    size: vk::Extent2D,
 | 
			
		||||
| 
						 | 
				
			
			@ -13,10 +99,12 @@ pub struct Image2D {
 | 
			
		|||
    format: vk::Format,
 | 
			
		||||
    image: vk::Image,
 | 
			
		||||
    allocation: vk_mem::Allocation,
 | 
			
		||||
    name: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for Image2D {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        tracing::debug!("destroying image {:?}", self);
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device
 | 
			
		||||
                .alloc()
 | 
			
		||||
| 
						 | 
				
			
			@ -36,6 +124,7 @@ impl Image2D {
 | 
			
		|||
        usage: vk::ImageUsageFlags,
 | 
			
		||||
        memory_usage: vk_mem::MemoryUsage,
 | 
			
		||||
        alloc_flags: vk_mem::AllocationCreateFlags,
 | 
			
		||||
        name: Option<&str>,
 | 
			
		||||
    ) -> VkResult<Arc<Self>> {
 | 
			
		||||
        let create_info = vk::ImageCreateInfo::default()
 | 
			
		||||
            .array_layers(array_layers)
 | 
			
		||||
| 
						 | 
				
			
			@ -62,6 +151,19 @@ impl Image2D {
 | 
			
		|||
        let (image, allocation) =
 | 
			
		||||
            unsafe { device.alloc().create_image(&create_info, &alloc_info)? };
 | 
			
		||||
 | 
			
		||||
        if let Some(name) = name {
 | 
			
		||||
            let info = device.alloc().get_allocation_info(&allocation);
 | 
			
		||||
 | 
			
		||||
            let name = std::ffi::CString::new(name).unwrap_or(c"invalid name".to_owned());
 | 
			
		||||
            unsafe {
 | 
			
		||||
                device.debug_utils().set_debug_utils_object_name(
 | 
			
		||||
                    &vk::DebugUtilsObjectNameInfoEXT::default()
 | 
			
		||||
                        .object_handle(info.device_memory)
 | 
			
		||||
                        .object_name(&name),
 | 
			
		||||
                )?;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(Arc::new(Self {
 | 
			
		||||
            size: extent,
 | 
			
		||||
            mip_levels,
 | 
			
		||||
| 
						 | 
				
			
			@ -69,6 +171,7 @@ impl Image2D {
 | 
			
		|||
            device: device.clone(),
 | 
			
		||||
            image,
 | 
			
		||||
            allocation,
 | 
			
		||||
            name: name.map(|s| s.to_owned()),
 | 
			
		||||
        }))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -76,61 +179,51 @@ impl Image2D {
 | 
			
		|||
        self.format
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn copy_from_buffer(&self, buffer: &Buffer) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            // self.device.dev().cmd_copy_buffer_to_image(command_buffer, src_buffer, dst_image, dst_image_layout, regions);
 | 
			
		||||
        }
 | 
			
		||||
    pub fn device(&self) -> Device {
 | 
			
		||||
        self.device.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn view(
 | 
			
		||||
        self: &Arc<Self>,
 | 
			
		||||
        device: &Device,
 | 
			
		||||
        aspect: vk::ImageAspectFlags,
 | 
			
		||||
    ) -> VkResult<Arc<ImageView2D>> {
 | 
			
		||||
    pub fn view(&self, desc: ImageViewDesc) -> VkResult<ImageView> {
 | 
			
		||||
        let create_info = vk::ImageViewCreateInfo::default()
 | 
			
		||||
            .flags(desc.flags)
 | 
			
		||||
            .image(self.image())
 | 
			
		||||
            .view_type(vk::ImageViewType::TYPE_2D)
 | 
			
		||||
            .format(self.format)
 | 
			
		||||
            .components(vk::ComponentMapping::default())
 | 
			
		||||
            .format(desc.format)
 | 
			
		||||
            .components(desc.components)
 | 
			
		||||
            .subresource_range(
 | 
			
		||||
                vk::ImageSubresourceRange::default()
 | 
			
		||||
                    .aspect_mask(aspect)
 | 
			
		||||
                    .base_mip_level(0)
 | 
			
		||||
                    .level_count(self.mip_levels)
 | 
			
		||||
                    .base_array_layer(0)
 | 
			
		||||
                    .layer_count(1),
 | 
			
		||||
                    .aspect_mask(desc.aspect)
 | 
			
		||||
                    .base_mip_level(desc.mip_range.0)
 | 
			
		||||
                    .level_count(desc.mip_range.count())
 | 
			
		||||
                    .base_array_layer(desc.layer_range.0)
 | 
			
		||||
                    .layer_count(desc.layer_range.count()),
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        let view =
 | 
			
		||||
            unsafe { device.dev().create_image_view(&create_info, None)? };
 | 
			
		||||
        let view = unsafe { self.device.dev().create_image_view(&create_info, None)? };
 | 
			
		||||
 | 
			
		||||
        Ok(Arc::new(ImageView2D {
 | 
			
		||||
            view,
 | 
			
		||||
            image: self.clone(),
 | 
			
		||||
            aspect,
 | 
			
		||||
        }))
 | 
			
		||||
        ImageView::construct(self.device.clone(), view, desc.name)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn image(&self) -> vk::Image {
 | 
			
		||||
        self.image
 | 
			
		||||
    }
 | 
			
		||||
    pub fn size(&self) -> vk::Extent2D {
 | 
			
		||||
        self.size
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
pub struct ImageView2D {
 | 
			
		||||
    view: vk::ImageView,
 | 
			
		||||
    aspect: vk::ImageAspectFlags,
 | 
			
		||||
    image: Arc<Image2D>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for ImageView2D {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.image.device.dev().destroy_image_view(self.view, None);
 | 
			
		||||
    pub fn width(&self) -> u32 {
 | 
			
		||||
        self.size.width
 | 
			
		||||
    }
 | 
			
		||||
    pub fn height(&self) -> u32 {
 | 
			
		||||
        self.size.height
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ImageView2D {}
 | 
			
		||||
define_device_owned_handle! {
 | 
			
		||||
    #[derive(Debug)]
 | 
			
		||||
    pub ImageView(vk::ImageView) {} => |this|  unsafe {
 | 
			
		||||
        this.device().dev().destroy_image_view(this.handle(), None);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct QueueOwnership {
 | 
			
		||||
    pub src: u32,
 | 
			
		||||
| 
						 | 
				
			
			@ -172,8 +265,7 @@ pub fn image_barrier<'a>(
 | 
			
		|||
        .new_layout(new_layout)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub const SUBRESOURCERANGE_COLOR_ALL: vk::ImageSubresourceRange =
 | 
			
		||||
    vk::ImageSubresourceRange {
 | 
			
		||||
pub const SUBRESOURCERANGE_COLOR_ALL: vk::ImageSubresourceRange = vk::ImageSubresourceRange {
 | 
			
		||||
    aspect_mask: vk::ImageAspectFlags::COLOR,
 | 
			
		||||
    base_mip_level: 0,
 | 
			
		||||
    level_count: vk::REMAINING_MIP_LEVELS,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										719
									
								
								crates/renderer/src/pipeline.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										719
									
								
								crates/renderer/src/pipeline.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,719 @@
 | 
			
		|||
use std::{borrow::Cow, path::Path, sync::Arc};
 | 
			
		||||
 | 
			
		||||
use ash::{prelude::*, vk};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    define_device_owned_handle,
 | 
			
		||||
    device::{Device, DeviceOwnedDebugObject},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct ShaderStageDesc<'a> {
 | 
			
		||||
    pub flags: vk::PipelineShaderStageCreateFlags,
 | 
			
		||||
    pub module: &'a ShaderModule,
 | 
			
		||||
    pub stage: vk::ShaderStageFlags,
 | 
			
		||||
    pub entry: Cow<'a, std::ffi::CStr>,
 | 
			
		||||
    // specialization: Option<vk::SpecializationInfo>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct DescriptorSetLayoutBindingDesc {
 | 
			
		||||
    pub binding: u32,
 | 
			
		||||
    pub count: u32,
 | 
			
		||||
    pub kind: vk::DescriptorType,
 | 
			
		||||
    pub stage: vk::ShaderStageFlags,
 | 
			
		||||
    pub flags: Option<vk::DescriptorBindingFlags>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct DescriptorSetLayoutDesc<'a> {
 | 
			
		||||
    pub flags: vk::DescriptorSetLayoutCreateFlags,
 | 
			
		||||
    pub bindings: &'a [DescriptorSetLayoutBindingDesc],
 | 
			
		||||
    pub name: Option<Cow<'static, str>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct PipelineLayoutDesc<'a> {
 | 
			
		||||
    pub descriptor_set_layouts: &'a [&'a DescriptorSetLayout],
 | 
			
		||||
    pub push_constant_ranges: &'a [vk::PushConstantRange],
 | 
			
		||||
    pub name: Option<Cow<'static, str>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum PipelineDesc<'a> {
 | 
			
		||||
    Compute(ComputePipelineDesc<'a>),
 | 
			
		||||
    Graphics(GraphicsPipelineDesc<'a>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PipelineDesc<'_> {
 | 
			
		||||
    fn name(self) -> Option<Cow<'static, str>> {
 | 
			
		||||
        match self {
 | 
			
		||||
            PipelineDesc::Compute(desc) => desc.name,
 | 
			
		||||
            PipelineDesc::Graphics(desc) => desc.name,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct ComputePipelineDesc<'a> {
 | 
			
		||||
    pub flags: vk::PipelineCreateFlags,
 | 
			
		||||
    pub name: Option<Cow<'static, str>>,
 | 
			
		||||
    pub shader_stage: ShaderStageDesc<'a>,
 | 
			
		||||
    pub layout: Arc<PipelineLayout>,
 | 
			
		||||
    pub base_pipeline: Option<Arc<Pipeline>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct VertexInputState<'a> {
 | 
			
		||||
    // pub flags: vk::PipelineVertexInputStateCreateFlags,
 | 
			
		||||
    pub bindings: &'a [vk::VertexInputBindingDescription],
 | 
			
		||||
    pub attributes: &'a [vk::VertexInputAttributeDescription],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct TessellationState {
 | 
			
		||||
    pub flags: vk::PipelineTessellationStateCreateFlags,
 | 
			
		||||
    pub patch_control_points: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct InputAssemblyState {
 | 
			
		||||
    // pub flags: vk::PipelineInputAssemblyStateCreateFlags,
 | 
			
		||||
    pub topology: vk::PrimitiveTopology,
 | 
			
		||||
    pub primitive_restart: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct ViewportState<'a> {
 | 
			
		||||
    pub num_scissors: u32,
 | 
			
		||||
    pub scissors: Option<&'a [vk::Rect2D]>,
 | 
			
		||||
    pub num_viewports: u32,
 | 
			
		||||
    pub viewports: Option<&'a [vk::Viewport]>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct DepthBiasState {
 | 
			
		||||
    pub clamp: f32,
 | 
			
		||||
    pub constant_factor: f32,
 | 
			
		||||
    pub slope_factor: f32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct RasterizationState {
 | 
			
		||||
    pub depth_clamp_enable: bool,
 | 
			
		||||
    pub discard_enable: bool,
 | 
			
		||||
    pub line_width: f32,
 | 
			
		||||
    pub cull_mode: vk::CullModeFlags,
 | 
			
		||||
    pub depth_bias: Option<DepthBiasState>,
 | 
			
		||||
    pub polygon_mode: vk::PolygonMode,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for RasterizationState {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            depth_clamp_enable: false,
 | 
			
		||||
            line_width: 1.0,
 | 
			
		||||
            cull_mode: vk::CullModeFlags::BACK,
 | 
			
		||||
            depth_bias: Default::default(),
 | 
			
		||||
            polygon_mode: vk::PolygonMode::FILL,
 | 
			
		||||
            discard_enable: false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct MultisampleState<'a> {
 | 
			
		||||
    pub flags: vk::PipelineMultisampleStateCreateFlags,
 | 
			
		||||
    pub sample_shading_enable: bool,
 | 
			
		||||
    pub rasterization_samples: vk::SampleCountFlags,
 | 
			
		||||
    pub min_sample_shading: f32,
 | 
			
		||||
    pub sample_mask: &'a [vk::SampleMask],
 | 
			
		||||
    pub alpha_to_coverage_enable: bool,
 | 
			
		||||
    pub alpha_to_one_enable: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Default for MultisampleState<'a> {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            flags: Default::default(),
 | 
			
		||||
            sample_shading_enable: Default::default(),
 | 
			
		||||
            rasterization_samples: vk::SampleCountFlags::TYPE_1,
 | 
			
		||||
            min_sample_shading: 1.0,
 | 
			
		||||
            sample_mask: Default::default(),
 | 
			
		||||
            alpha_to_coverage_enable: Default::default(),
 | 
			
		||||
            alpha_to_one_enable: Default::default(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct DepthBounds {
 | 
			
		||||
    pub min: f32,
 | 
			
		||||
    pub max: f32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct DepthState {
 | 
			
		||||
    pub write_enable: bool,
 | 
			
		||||
    /// sets depthTestEnable to true when `Some`
 | 
			
		||||
    pub compare_op: Option<vk::CompareOp>,
 | 
			
		||||
    /// sets depthBoundsTestEnable to true when `Some`
 | 
			
		||||
    pub bounds: Option<DepthBounds>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct StencilState {
 | 
			
		||||
    pub front: vk::StencilOpState,
 | 
			
		||||
    pub back: vk::StencilOpState,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct DepthStencilState {
 | 
			
		||||
    pub flags: vk::PipelineDepthStencilStateCreateFlags,
 | 
			
		||||
    pub depth: Option<DepthState>,
 | 
			
		||||
    pub stencil: Option<StencilState>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct ColorBlendState<'a> {
 | 
			
		||||
    pub flags: vk::PipelineColorBlendStateCreateFlags,
 | 
			
		||||
    pub attachments: &'a [vk::PipelineColorBlendAttachmentState],
 | 
			
		||||
    pub logic_op: Option<vk::LogicOp>,
 | 
			
		||||
    pub blend_constants: [f32; 4],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct RenderingState<'a> {
 | 
			
		||||
    pub color_formats: &'a [vk::Format],
 | 
			
		||||
    pub depth_format: Option<vk::Format>,
 | 
			
		||||
    pub stencil_format: Option<vk::Format>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct DynamicState<'a> {
 | 
			
		||||
    pub flags: vk::PipelineDynamicStateCreateFlags,
 | 
			
		||||
    pub dynamic_states: &'a [vk::DynamicState],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct GraphicsPipelineDesc<'a> {
 | 
			
		||||
    pub flags: vk::PipelineCreateFlags,
 | 
			
		||||
    pub name: Option<Cow<'static, str>>,
 | 
			
		||||
    pub shader_stages: &'a [ShaderStageDesc<'a>],
 | 
			
		||||
    pub render_pass: Option<vk::RenderPass>,
 | 
			
		||||
    pub layout: &'a PipelineLayout,
 | 
			
		||||
    pub subpass: Option<u32>,
 | 
			
		||||
    pub base_pipeline: Option<Arc<Pipeline>>,
 | 
			
		||||
 | 
			
		||||
    pub vertex_input: Option<VertexInputState<'a>>,
 | 
			
		||||
    pub input_assembly: Option<InputAssemblyState>,
 | 
			
		||||
    pub tessellation: Option<TessellationState>,
 | 
			
		||||
    pub viewport: Option<ViewportState<'a>>,
 | 
			
		||||
    pub rasterization: Option<RasterizationState>,
 | 
			
		||||
    pub multisample: Option<MultisampleState<'a>>,
 | 
			
		||||
    pub depth_stencil: Option<DepthStencilState>,
 | 
			
		||||
    pub color_blend: Option<ColorBlendState<'a>>,
 | 
			
		||||
    pub dynamic: Option<DynamicState<'a>>,
 | 
			
		||||
    pub rendering: Option<RenderingState<'a>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct DescriptorPoolDesc<'a> {
 | 
			
		||||
    pub flags: vk::DescriptorPoolCreateFlags,
 | 
			
		||||
    pub name: Option<Cow<'static, str>>,
 | 
			
		||||
    pub sizes: &'a [vk::DescriptorPoolSize],
 | 
			
		||||
    pub max_sets: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct DescriptorSetAllocDesc<'a> {
 | 
			
		||||
    pub name: Option<Cow<'static, str>>,
 | 
			
		||||
    pub layout: &'a DescriptorSetLayout,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
define_device_owned_handle! {
 | 
			
		||||
    #[derive(Debug)]
 | 
			
		||||
    pub DescriptorPool(vk::DescriptorPool) {} => |this|  unsafe {
 | 
			
		||||
        this.device().dev().destroy_descriptor_pool(this.handle(), None);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DescriptorPool {
 | 
			
		||||
    pub fn new(device: Device, desc: DescriptorPoolDesc) -> VkResult<Self> {
 | 
			
		||||
        let info = &vk::DescriptorPoolCreateInfo::default()
 | 
			
		||||
            .flags(desc.flags)
 | 
			
		||||
            .max_sets(desc.max_sets)
 | 
			
		||||
            .pool_sizes(desc.sizes);
 | 
			
		||||
 | 
			
		||||
        let handle = unsafe { device.dev().create_descriptor_pool(info, None)? };
 | 
			
		||||
 | 
			
		||||
        Self::construct(device, handle, desc.name)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn allocate(&self, descs: &[DescriptorSetAllocDesc]) -> VkResult<Vec<vk::DescriptorSet>> {
 | 
			
		||||
        let layouts = descs
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|desc| desc.layout.handle())
 | 
			
		||||
            .collect::<Vec<_>>();
 | 
			
		||||
 | 
			
		||||
        let info = &vk::DescriptorSetAllocateInfo::default()
 | 
			
		||||
            .descriptor_pool(self.handle())
 | 
			
		||||
            .set_layouts(&layouts);
 | 
			
		||||
        let sets = unsafe { self.device().dev().allocate_descriptor_sets(&info)? };
 | 
			
		||||
 | 
			
		||||
        Ok(sets)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // pub fn free(&self) {}
 | 
			
		||||
    pub fn reset(&self) -> VkResult<()> {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device()
 | 
			
		||||
                .dev()
 | 
			
		||||
                .reset_descriptor_pool(self.handle(), vk::DescriptorPoolResetFlags::empty())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
define_device_owned_handle! {
 | 
			
		||||
    #[derive(Debug)]
 | 
			
		||||
    pub DescriptorSetLayout(vk::DescriptorSetLayout) {} => |this|  unsafe {
 | 
			
		||||
        this.device().dev().destroy_descriptor_set_layout(this.handle(), None);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DescriptorSetLayout {
 | 
			
		||||
    pub fn new(device: Device, desc: DescriptorSetLayoutDesc) -> VkResult<Self> {
 | 
			
		||||
        let (flags, bindings): (Vec<_>, Vec<_>) = desc
 | 
			
		||||
            .bindings
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|binding| {
 | 
			
		||||
                let flag = binding.flags.unwrap_or_default();
 | 
			
		||||
                let binding = vk::DescriptorSetLayoutBinding::default()
 | 
			
		||||
                    .binding(binding.binding)
 | 
			
		||||
                    .descriptor_count(binding.count)
 | 
			
		||||
                    .descriptor_type(binding.kind)
 | 
			
		||||
                    .stage_flags(binding.stage);
 | 
			
		||||
 | 
			
		||||
                (flag, binding)
 | 
			
		||||
            })
 | 
			
		||||
            .unzip();
 | 
			
		||||
 | 
			
		||||
        let flags =
 | 
			
		||||
            &mut vk::DescriptorSetLayoutBindingFlagsCreateInfo::default().binding_flags(&flags);
 | 
			
		||||
 | 
			
		||||
        let mut info = vk::DescriptorSetLayoutCreateInfo::default()
 | 
			
		||||
            .bindings(&bindings)
 | 
			
		||||
            .flags(desc.flags);
 | 
			
		||||
 | 
			
		||||
        if device.features().version >= vk::API_VERSION_1_2
 | 
			
		||||
            || device
 | 
			
		||||
                .features()
 | 
			
		||||
                .supports_extension(&crate::make_extention_properties(
 | 
			
		||||
                    ash::ext::descriptor_indexing::NAME,
 | 
			
		||||
                    ash::ext::descriptor_indexing::SPEC_VERSION,
 | 
			
		||||
                ))
 | 
			
		||||
        {
 | 
			
		||||
            info = info.push_next(flags);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let layout = unsafe { device.dev().create_descriptor_set_layout(&info, None)? };
 | 
			
		||||
 | 
			
		||||
        Self::construct(device, layout, desc.name)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
use crate::device::DeviceOwned;
 | 
			
		||||
 | 
			
		||||
define_device_owned_handle! {
 | 
			
		||||
    #[derive(Debug)]
 | 
			
		||||
    pub PipelineLayout(vk::PipelineLayout) {} => |this|  unsafe {
 | 
			
		||||
        this.device().dev().destroy_pipeline_layout(this.handle(), None);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PipelineLayout {
 | 
			
		||||
    pub fn new(device: Device, desc: PipelineLayoutDesc) -> VkResult<Self> {
 | 
			
		||||
        let set_layouts = desc
 | 
			
		||||
            .descriptor_set_layouts
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|desc| desc.handle())
 | 
			
		||||
            .collect::<Vec<_>>();
 | 
			
		||||
        let info = &vk::PipelineLayoutCreateInfo::default()
 | 
			
		||||
            .set_layouts(&set_layouts)
 | 
			
		||||
            .push_constant_ranges(desc.push_constant_ranges);
 | 
			
		||||
        let layout = unsafe { device.dev().create_pipeline_layout(info, None)? };
 | 
			
		||||
 | 
			
		||||
        Self::construct(device, layout, desc.name)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct SamplerDesc {
 | 
			
		||||
    pub flags: vk::SamplerCreateFlags,
 | 
			
		||||
    pub min_filter: vk::Filter,
 | 
			
		||||
    pub mag_filter: vk::Filter,
 | 
			
		||||
    pub mipmap_mode: vk::SamplerMipmapMode,
 | 
			
		||||
    pub address_u: vk::SamplerAddressMode,
 | 
			
		||||
    pub address_v: vk::SamplerAddressMode,
 | 
			
		||||
    pub address_w: vk::SamplerAddressMode,
 | 
			
		||||
    pub mip_lod_bias: f32,
 | 
			
		||||
    pub anisotropy_enable: bool,
 | 
			
		||||
    pub max_anisotropy: f32,
 | 
			
		||||
    pub compare_op: Option<vk::CompareOp>,
 | 
			
		||||
    pub min_lod: f32,
 | 
			
		||||
    pub max_lod: f32,
 | 
			
		||||
    pub border_color: vk::BorderColor,
 | 
			
		||||
    pub unnormalized_coordinates: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Eq for SamplerDesc {}
 | 
			
		||||
impl PartialEq for SamplerDesc {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        use crate::util::eq_f32;
 | 
			
		||||
        self.flags == other.flags
 | 
			
		||||
            && self.min_filter == other.min_filter
 | 
			
		||||
            && self.mag_filter == other.mag_filter
 | 
			
		||||
            && self.mipmap_mode == other.mipmap_mode
 | 
			
		||||
            && self.address_u == other.address_u
 | 
			
		||||
            && self.address_v == other.address_v
 | 
			
		||||
            && self.address_w == other.address_w
 | 
			
		||||
            && self.anisotropy_enable == other.anisotropy_enable
 | 
			
		||||
            && self.compare_op == other.compare_op
 | 
			
		||||
            && eq_f32(self.mip_lod_bias, other.mip_lod_bias)
 | 
			
		||||
            && eq_f32(self.max_anisotropy, other.max_anisotropy)
 | 
			
		||||
            && eq_f32(self.min_lod, other.min_lod)
 | 
			
		||||
            && eq_f32(self.max_lod, other.max_lod)
 | 
			
		||||
            && self.border_color == other.border_color
 | 
			
		||||
            && self.unnormalized_coordinates == other.unnormalized_coordinates
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::hash::Hash for SamplerDesc {
 | 
			
		||||
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 | 
			
		||||
        use crate::util::hash_f32;
 | 
			
		||||
        self.flags.hash(state);
 | 
			
		||||
        self.min_filter.hash(state);
 | 
			
		||||
        self.mag_filter.hash(state);
 | 
			
		||||
        self.mipmap_mode.hash(state);
 | 
			
		||||
        self.address_u.hash(state);
 | 
			
		||||
        self.address_v.hash(state);
 | 
			
		||||
        self.address_w.hash(state);
 | 
			
		||||
        hash_f32(state, self.mip_lod_bias);
 | 
			
		||||
        hash_f32(state, self.max_anisotropy);
 | 
			
		||||
        hash_f32(state, self.min_lod);
 | 
			
		||||
        hash_f32(state, self.max_lod);
 | 
			
		||||
        self.anisotropy_enable.hash(state);
 | 
			
		||||
        self.compare_op.hash(state);
 | 
			
		||||
        self.border_color.hash(state);
 | 
			
		||||
        self.unnormalized_coordinates.hash(state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
define_device_owned_handle! {
 | 
			
		||||
    #[derive(Debug)]
 | 
			
		||||
    pub Sampler(vk::Sampler) {} => |this|  unsafe {
 | 
			
		||||
        this.device().dev().destroy_sampler(this.handle(), None);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Sampler {
 | 
			
		||||
    pub fn new(device: Device, desc: &SamplerDesc) -> VkResult<Self> {
 | 
			
		||||
        let info = &vk::SamplerCreateInfo::default()
 | 
			
		||||
            .flags(desc.flags)
 | 
			
		||||
            .min_filter(desc.min_filter)
 | 
			
		||||
            .mag_filter(desc.mag_filter)
 | 
			
		||||
            .mip_lod_bias(desc.mip_lod_bias)
 | 
			
		||||
            .mipmap_mode(desc.mipmap_mode)
 | 
			
		||||
            .address_mode_u(desc.address_u)
 | 
			
		||||
            .address_mode_v(desc.address_v)
 | 
			
		||||
            .address_mode_w(desc.address_w)
 | 
			
		||||
            .anisotropy_enable(desc.anisotropy_enable)
 | 
			
		||||
            .max_anisotropy(desc.max_anisotropy)
 | 
			
		||||
            .compare_enable(desc.compare_op.is_some())
 | 
			
		||||
            .compare_op(desc.compare_op.unwrap_or_default())
 | 
			
		||||
            .min_lod(desc.min_lod)
 | 
			
		||||
            .max_lod(desc.max_lod)
 | 
			
		||||
            .border_color(desc.border_color)
 | 
			
		||||
            .unnormalized_coordinates(desc.unnormalized_coordinates);
 | 
			
		||||
 | 
			
		||||
        let handle = unsafe { device.dev().create_sampler(info, None)? };
 | 
			
		||||
 | 
			
		||||
        Self::construct(device, handle, None)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
define_device_owned_handle! {
 | 
			
		||||
    #[derive(Debug)]
 | 
			
		||||
    pub ShaderModule(vk::ShaderModule) {} => |this|  unsafe {
 | 
			
		||||
        this.device().dev().destroy_shader_module(this.handle(), None);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ShaderModule {
 | 
			
		||||
    pub fn new_from_path<P: AsRef<Path>>(device: Device, path: P) -> crate::Result<Self> {
 | 
			
		||||
        use std::io::{BufReader, Read, Seek};
 | 
			
		||||
 | 
			
		||||
        let mut file = std::fs::File::open(path)?;
 | 
			
		||||
        let size = file.seek(std::io::SeekFrom::End(0))? / 4;
 | 
			
		||||
        file.seek(std::io::SeekFrom::Start(0))?;
 | 
			
		||||
        let mut reader = BufReader::new(file);
 | 
			
		||||
 | 
			
		||||
        let mut buffer = Vec::<u32>::with_capacity(size as usize);
 | 
			
		||||
        buffer.resize(size as usize, 0);
 | 
			
		||||
        let size = reader.read(bytemuck::cast_slice_mut(buffer.as_mut_slice()))?;
 | 
			
		||||
        buffer.resize(size / 4, 0);
 | 
			
		||||
 | 
			
		||||
        Ok(Self::new_from_memory(device, &buffer)?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn new_from_memory(device: Device, buffer: &[u32]) -> VkResult<Self> {
 | 
			
		||||
        let info = &vk::ShaderModuleCreateInfo::default().code(buffer);
 | 
			
		||||
 | 
			
		||||
        let module = unsafe { device.dev().create_shader_module(info, None)? };
 | 
			
		||||
 | 
			
		||||
        Self::construct(device, module, None)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct Pipeline {
 | 
			
		||||
    pipeline: DeviceOwnedDebugObject<vk::Pipeline>,
 | 
			
		||||
    bind_point: vk::PipelineBindPoint,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for Pipeline {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.pipeline
 | 
			
		||||
                .dev()
 | 
			
		||||
                .dev()
 | 
			
		||||
                .destroy_pipeline(self.pipeline.handle(), None);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ShaderStageDesc<'_> {
 | 
			
		||||
    fn into_create_info(&self) -> vk::PipelineShaderStageCreateInfo {
 | 
			
		||||
        vk::PipelineShaderStageCreateInfo::default()
 | 
			
		||||
            .module(self.module.handle())
 | 
			
		||||
            .flags(self.flags)
 | 
			
		||||
            .stage(self.stage)
 | 
			
		||||
            .name(&self.entry)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Pipeline {
 | 
			
		||||
    pub fn new(device: Device, desc: PipelineDesc) -> VkResult<Self> {
 | 
			
		||||
        let name: Option<Cow<'static, str>>;
 | 
			
		||||
        let bind_point: vk::PipelineBindPoint;
 | 
			
		||||
        let result = match desc {
 | 
			
		||||
            PipelineDesc::Compute(desc) => {
 | 
			
		||||
                name = desc.name;
 | 
			
		||||
                bind_point = vk::PipelineBindPoint::COMPUTE;
 | 
			
		||||
                let info = &vk::ComputePipelineCreateInfo::default()
 | 
			
		||||
                    .layout(desc.layout.handle())
 | 
			
		||||
                    .stage(desc.shader_stage.into_create_info());
 | 
			
		||||
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    device.dev().create_compute_pipelines(
 | 
			
		||||
                        vk::PipelineCache::null(),
 | 
			
		||||
                        core::slice::from_ref(info),
 | 
			
		||||
                        None,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            PipelineDesc::Graphics(desc) => {
 | 
			
		||||
                name = desc.name;
 | 
			
		||||
                bind_point = vk::PipelineBindPoint::GRAPHICS;
 | 
			
		||||
 | 
			
		||||
                let stages = desc
 | 
			
		||||
                    .shader_stages
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|stage| stage.into_create_info())
 | 
			
		||||
                    .collect::<Vec<_>>();
 | 
			
		||||
 | 
			
		||||
                let vertex_input = desc.vertex_input.map(|vertex| {
 | 
			
		||||
                    vk::PipelineVertexInputStateCreateInfo::default()
 | 
			
		||||
                        .vertex_attribute_descriptions(vertex.attributes)
 | 
			
		||||
                        .vertex_binding_descriptions(vertex.bindings)
 | 
			
		||||
                });
 | 
			
		||||
                let input_assembly = desc.input_assembly.map(|state| {
 | 
			
		||||
                    vk::PipelineInputAssemblyStateCreateInfo::default()
 | 
			
		||||
                        .primitive_restart_enable(state.primitive_restart)
 | 
			
		||||
                        .topology(state.topology)
 | 
			
		||||
                });
 | 
			
		||||
                let tessellation = desc.tessellation.map(|state| {
 | 
			
		||||
                    vk::PipelineTessellationStateCreateInfo::default()
 | 
			
		||||
                        .flags(state.flags)
 | 
			
		||||
                        .patch_control_points(state.patch_control_points)
 | 
			
		||||
                });
 | 
			
		||||
                let viewport = desc.viewport.map(|state| {
 | 
			
		||||
                    let mut info = vk::PipelineViewportStateCreateInfo::default()
 | 
			
		||||
                        .scissor_count(state.num_scissors)
 | 
			
		||||
                        .viewport_count(state.num_viewports);
 | 
			
		||||
                    if let Some(viewports) = state.viewports {
 | 
			
		||||
                        info = info.viewports(viewports);
 | 
			
		||||
                    }
 | 
			
		||||
                    if let Some(scissors) = state.scissors {
 | 
			
		||||
                        info = info.scissors(scissors);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    info
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                let rasterization = desc.rasterization.map(|state| {
 | 
			
		||||
                    let mut info = vk::PipelineRasterizationStateCreateInfo::default()
 | 
			
		||||
                        .line_width(state.line_width)
 | 
			
		||||
                        .cull_mode(state.cull_mode)
 | 
			
		||||
                        .polygon_mode(state.polygon_mode)
 | 
			
		||||
                        .rasterizer_discard_enable(state.discard_enable)
 | 
			
		||||
                        .depth_clamp_enable(state.depth_clamp_enable);
 | 
			
		||||
 | 
			
		||||
                    if let Some(depth_bias) = state.depth_bias {
 | 
			
		||||
                        info = info
 | 
			
		||||
                            .depth_bias_enable(true)
 | 
			
		||||
                            .depth_bias_clamp(depth_bias.clamp)
 | 
			
		||||
                            .depth_bias_constant_factor(depth_bias.constant_factor)
 | 
			
		||||
                            .depth_bias_slope_factor(depth_bias.slope_factor);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    info
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                let multisample = desc.multisample.map(|state| {
 | 
			
		||||
                    let mut info = vk::PipelineMultisampleStateCreateInfo::default()
 | 
			
		||||
                        .flags(state.flags)
 | 
			
		||||
                        .min_sample_shading(state.min_sample_shading)
 | 
			
		||||
                        .rasterization_samples(state.rasterization_samples)
 | 
			
		||||
                        .sample_mask(state.sample_mask)
 | 
			
		||||
                        .alpha_to_coverage_enable(state.alpha_to_coverage_enable)
 | 
			
		||||
                        .alpha_to_one_enable(state.alpha_to_one_enable);
 | 
			
		||||
 | 
			
		||||
                    info
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                let color_blend = desc.color_blend.map(|state| {
 | 
			
		||||
                    let mut info = vk::PipelineColorBlendStateCreateInfo::default()
 | 
			
		||||
                        .flags(state.flags)
 | 
			
		||||
                        .attachments(state.attachments)
 | 
			
		||||
                        .blend_constants(state.blend_constants)
 | 
			
		||||
                        .logic_op(state.logic_op.unwrap_or(Default::default()))
 | 
			
		||||
                        .logic_op_enable(state.logic_op.is_some());
 | 
			
		||||
 | 
			
		||||
                    info
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                let depth_stencil = desc.depth_stencil.map(|state| {
 | 
			
		||||
                    let mut info =
 | 
			
		||||
                        vk::PipelineDepthStencilStateCreateInfo::default().flags(state.flags);
 | 
			
		||||
 | 
			
		||||
                    if let Some(depth) = state.depth {
 | 
			
		||||
                        info = info
 | 
			
		||||
                            .depth_compare_op(depth.compare_op.unwrap_or(vk::CompareOp::default()))
 | 
			
		||||
                            .depth_test_enable(depth.compare_op.is_some())
 | 
			
		||||
                            .depth_write_enable(depth.write_enable)
 | 
			
		||||
                            .depth_bounds_test_enable(depth.bounds.is_some());
 | 
			
		||||
                        if let Some(bounds) = depth.bounds {
 | 
			
		||||
                            info = info
 | 
			
		||||
                                .max_depth_bounds(bounds.max)
 | 
			
		||||
                                .min_depth_bounds(bounds.min);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if let Some(stencil) = state.stencil {
 | 
			
		||||
                        info = info
 | 
			
		||||
                            .stencil_test_enable(true)
 | 
			
		||||
                            .front(stencil.front)
 | 
			
		||||
                            .back(stencil.back);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    info
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                let dynamic = desc.dynamic.map(|state| {
 | 
			
		||||
                    let mut info = vk::PipelineDynamicStateCreateInfo::default()
 | 
			
		||||
                        .flags(state.flags)
 | 
			
		||||
                        .dynamic_states(state.dynamic_states);
 | 
			
		||||
 | 
			
		||||
                    info
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                let mut rendering = desc.rendering.map(|state| {
 | 
			
		||||
                    let mut info = vk::PipelineRenderingCreateInfo::default()
 | 
			
		||||
                        .color_attachment_formats(state.color_formats)
 | 
			
		||||
                        .depth_attachment_format(state.depth_format.unwrap_or_default())
 | 
			
		||||
                        .stencil_attachment_format(state.stencil_format.unwrap_or_default());
 | 
			
		||||
 | 
			
		||||
                    info
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                fn option_to_ptr<T>(option: &Option<T>) -> *const T {
 | 
			
		||||
                    option
 | 
			
		||||
                        .as_ref()
 | 
			
		||||
                        .map(|t| t as *const T)
 | 
			
		||||
                        .unwrap_or(core::ptr::null())
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let mut info = vk::GraphicsPipelineCreateInfo {
 | 
			
		||||
                    flags: desc.flags,
 | 
			
		||||
                    stage_count: stages.len() as u32,
 | 
			
		||||
                    p_stages: stages.as_ptr(),
 | 
			
		||||
                    p_vertex_input_state: option_to_ptr(&vertex_input),
 | 
			
		||||
                    p_input_assembly_state: option_to_ptr(&input_assembly),
 | 
			
		||||
                    p_tessellation_state: option_to_ptr(&tessellation),
 | 
			
		||||
                    p_viewport_state: option_to_ptr(&viewport),
 | 
			
		||||
                    p_rasterization_state: option_to_ptr(&rasterization),
 | 
			
		||||
                    p_multisample_state: option_to_ptr(&multisample),
 | 
			
		||||
                    p_depth_stencil_state: option_to_ptr(&depth_stencil),
 | 
			
		||||
                    p_color_blend_state: option_to_ptr(&color_blend),
 | 
			
		||||
                    p_dynamic_state: option_to_ptr(&dynamic),
 | 
			
		||||
                    layout: desc.layout.handle(),
 | 
			
		||||
                    render_pass: desc.render_pass.unwrap_or(vk::RenderPass::null()),
 | 
			
		||||
                    subpass: desc.subpass.unwrap_or(0),
 | 
			
		||||
                    base_pipeline_handle: desc
 | 
			
		||||
                        .base_pipeline
 | 
			
		||||
                        .map(|piepline| piepline.pipeline.handle())
 | 
			
		||||
                        .unwrap_or(vk::Pipeline::null()),
 | 
			
		||||
                    ..Default::default()
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                if let Some(rendering) = rendering.as_mut() {
 | 
			
		||||
                    info = info.push_next(rendering)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    device.dev().create_graphics_pipelines(
 | 
			
		||||
                        vk::PipelineCache::null(),
 | 
			
		||||
                        core::slice::from_ref(&info),
 | 
			
		||||
                        None,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let pipeline = match result {
 | 
			
		||||
            Ok(pipelines) => pipelines[0],
 | 
			
		||||
            Err((pipelines, error)) => {
 | 
			
		||||
                tracing::error!("failed to create pipelines with :{error}");
 | 
			
		||||
                for pipeline in pipelines {
 | 
			
		||||
                    unsafe {
 | 
			
		||||
                        device.dev().destroy_pipeline(pipeline, None);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return Err(error.into());
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            pipeline: DeviceOwnedDebugObject::new(device, pipeline, name)?,
 | 
			
		||||
            bind_point,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn handle(&self) -> vk::Pipeline {
 | 
			
		||||
        self.pipeline.handle()
 | 
			
		||||
    }
 | 
			
		||||
    pub fn bind_point(&self) -> vk::PipelineBindPoint {
 | 
			
		||||
        self.bind_point
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
use std::hash::Hash;
 | 
			
		||||
 | 
			
		||||
use crate::util::hash_f32;
 | 
			
		||||
use ash::vk;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy)]
 | 
			
		||||
| 
						 | 
				
			
			@ -7,17 +8,6 @@ pub struct Rgba(pub [f32; 4]);
 | 
			
		|||
 | 
			
		||||
impl std::hash::Hash for Rgba {
 | 
			
		||||
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 | 
			
		||||
        fn hash_f32<H: std::hash::Hasher>(state: &mut H, f: f32) {
 | 
			
		||||
            let classify = f.classify();
 | 
			
		||||
            match classify {
 | 
			
		||||
                std::num::FpCategory::Nan => (classify as u8).hash(state),
 | 
			
		||||
                std::num::FpCategory::Infinite | std::num::FpCategory::Zero => {
 | 
			
		||||
                    (classify as u8, f.signum() as i8).hash(state)
 | 
			
		||||
                }
 | 
			
		||||
                std::num::FpCategory::Subnormal
 | 
			
		||||
                | std::num::FpCategory::Normal => f.to_bits().hash(state),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        self.0.map(|f| hash_f32(state, f));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,17 +45,15 @@ impl SyncThreadpool {
 | 
			
		|||
 | 
			
		||||
    fn try_spawn_thread(&self) -> Option<()> {
 | 
			
		||||
        use std::sync::atomic::Ordering;
 | 
			
		||||
        match self.num_threads.fetch_update(
 | 
			
		||||
            Ordering::Release,
 | 
			
		||||
            Ordering::Acquire,
 | 
			
		||||
            |i| {
 | 
			
		||||
        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,
 | 
			
		||||
| 
						 | 
				
			
			@ -68,22 +66,14 @@ impl SyncThreadpool {
 | 
			
		|||
                    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
 | 
			
		||||
                            );
 | 
			
		||||
                        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()
 | 
			
		||||
                                    }
 | 
			
		||||
                                    SyncPrimitive::DeviceIdle(device) => device.wait_idle(),
 | 
			
		||||
                                };
 | 
			
		||||
 | 
			
		||||
                                match wait_result {
 | 
			
		||||
| 
						 | 
				
			
			@ -197,8 +187,7 @@ impl Fence {
 | 
			
		|||
            Ok(Self::new(
 | 
			
		||||
                dev.clone(),
 | 
			
		||||
                dev.dev().create_fence(
 | 
			
		||||
                    &vk::FenceCreateInfo::default()
 | 
			
		||||
                        .flags(vk::FenceCreateFlags::SIGNALED),
 | 
			
		||||
                    &vk::FenceCreateInfo::default().flags(vk::FenceCreateFlags::SIGNALED),
 | 
			
		||||
                    None,
 | 
			
		||||
                )?,
 | 
			
		||||
            ))
 | 
			
		||||
| 
						 | 
				
			
			@ -207,11 +196,9 @@ impl Fence {
 | 
			
		|||
    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),
 | 
			
		||||
            )
 | 
			
		||||
            self.dev
 | 
			
		||||
                .dev()
 | 
			
		||||
                .wait_for_fences(from_ref(&self.fence), true, timeout.unwrap_or(u64::MAX))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn fence(&self) -> vk::Fence {
 | 
			
		||||
| 
						 | 
				
			
			@ -236,12 +223,10 @@ impl AsRef<vk::Fence> for Fence {
 | 
			
		|||
 | 
			
		||||
impl Semaphore {
 | 
			
		||||
    pub fn new(device: Device) -> VkResult<Self> {
 | 
			
		||||
        let mut type_info = vk::SemaphoreTypeCreateInfo::default()
 | 
			
		||||
            .semaphore_type(vk::SemaphoreType::BINARY);
 | 
			
		||||
        let create_info =
 | 
			
		||||
            vk::SemaphoreCreateInfo::default().push_next(&mut type_info);
 | 
			
		||||
        let inner =
 | 
			
		||||
            unsafe { device.dev().create_semaphore(&create_info, None)? };
 | 
			
		||||
        let mut type_info =
 | 
			
		||||
            vk::SemaphoreTypeCreateInfo::default().semaphore_type(vk::SemaphoreType::BINARY);
 | 
			
		||||
        let create_info = vk::SemaphoreCreateInfo::default().push_next(&mut type_info);
 | 
			
		||||
        let inner = unsafe { device.dev().create_semaphore(&create_info, None)? };
 | 
			
		||||
 | 
			
		||||
        Ok(Self { device, inner })
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -249,10 +234,8 @@ impl Semaphore {
 | 
			
		|||
        let mut type_info = vk::SemaphoreTypeCreateInfo::default()
 | 
			
		||||
            .semaphore_type(vk::SemaphoreType::TIMELINE)
 | 
			
		||||
            .initial_value(value);
 | 
			
		||||
        let create_info =
 | 
			
		||||
            vk::SemaphoreCreateInfo::default().push_next(&mut type_info);
 | 
			
		||||
        let inner =
 | 
			
		||||
            unsafe { device.dev().create_semaphore(&create_info, None)? };
 | 
			
		||||
        let create_info = vk::SemaphoreCreateInfo::default().push_next(&mut type_info);
 | 
			
		||||
        let inner = unsafe { device.dev().create_semaphore(&create_info, None)? };
 | 
			
		||||
 | 
			
		||||
        Ok(Self { device, inner })
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,32 @@
 | 
			
		|||
use std::ops::Deref;
 | 
			
		||||
use std::{borrow::Cow, ops::Deref};
 | 
			
		||||
 | 
			
		||||
use ash::vk;
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! def_monotonic_id {
 | 
			
		||||
    ($ty:ident) => {
 | 
			
		||||
        #[derive(Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord, Debug)]
 | 
			
		||||
        pub struct $ty(::core::num::NonZero<u32>);
 | 
			
		||||
 | 
			
		||||
        impl $ty {
 | 
			
		||||
            pub fn new() -> Self {
 | 
			
		||||
                use ::core::sync::atomic::{AtomicU32, Ordering};
 | 
			
		||||
                static COUNTER: AtomicU32 = AtomicU32::new(1);
 | 
			
		||||
 | 
			
		||||
                let inner = COUNTER.fetch_add(1, Ordering::Relaxed);
 | 
			
		||||
                Self(
 | 
			
		||||
                    ::core::num::NonZero::<u32>::new(inner)
 | 
			
		||||
                        .expect(&format!("integer overwarp for {}", stringify!($ty))),
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            pub fn as_u32(&self) -> u32 {
 | 
			
		||||
                self.0.get()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait MutexExt<'a, T: 'a> {
 | 
			
		||||
    type Guard: Deref<Target = T> + 'a;
 | 
			
		||||
    fn lock(&'a self) -> Self::Guard;
 | 
			
		||||
| 
						 | 
				
			
			@ -239,3 +264,62 @@ impl FormatExt for vk::Format {
 | 
			
		|||
        format_to_primitive(*self) == FormatComponentKind::SInt
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
 | 
			
		||||
pub struct Rect2D {
 | 
			
		||||
    top_left: glam::IVec2,
 | 
			
		||||
    bottom_right: glam::IVec2,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Rect2D {
 | 
			
		||||
    pub fn new(left: i32, top: i32, right: i32, bottom: i32) -> Self {
 | 
			
		||||
        use glam::ivec2;
 | 
			
		||||
        Self {
 | 
			
		||||
            top_left: ivec2(left, top),
 | 
			
		||||
            bottom_right: ivec2(right, bottom),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_from_size(pos: glam::IVec2, size: glam::IVec2) -> Self {
 | 
			
		||||
        use glam::ivec2;
 | 
			
		||||
        Self {
 | 
			
		||||
            top_left: pos,
 | 
			
		||||
            bottom_right: pos + size,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn into_offsets_3d(&self) -> [vk::Offset3D; 2] {
 | 
			
		||||
        [
 | 
			
		||||
            vk::Offset3D {
 | 
			
		||||
                x: self.top_left.x,
 | 
			
		||||
                y: self.top_left.y,
 | 
			
		||||
                z: 0,
 | 
			
		||||
            },
 | 
			
		||||
            vk::Offset3D {
 | 
			
		||||
                x: self.bottom_right.x,
 | 
			
		||||
                y: self.bottom_right.y,
 | 
			
		||||
                z: 1,
 | 
			
		||||
            },
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn eq_f32(lhs: f32, rhs: f32) -> bool {
 | 
			
		||||
    use core::num::FpCategory::*;
 | 
			
		||||
    match (lhs.classify(), rhs.classify()) {
 | 
			
		||||
        (Zero, Zero) | (Nan, Nan) => true,
 | 
			
		||||
        (Infinite, Infinite) => lhs.signum() == rhs.signum(),
 | 
			
		||||
        _ => lhs == rhs,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn hash_f32<H: std::hash::Hasher>(state: &mut H, f: f32) {
 | 
			
		||||
    use std::hash::Hash;
 | 
			
		||||
    let classify = f.classify();
 | 
			
		||||
    match classify {
 | 
			
		||||
        std::num::FpCategory::Nan => (classify as u8).hash(state),
 | 
			
		||||
        std::num::FpCategory::Infinite | std::num::FpCategory::Zero => {
 | 
			
		||||
            (classify as u8, f.signum() as i8).hash(state)
 | 
			
		||||
        }
 | 
			
		||||
        std::num::FpCategory::Subnormal | std::num::FpCategory::Normal => f.to_bits().hash(state),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue