almost done!

This commit is contained in:
janis 2026-04-04 00:28:43 +02:00
parent ac7e8889db
commit 60760ba67f
19 changed files with 323 additions and 128 deletions

View file

@ -1,9 +0,0 @@
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = [
"-Clink-arg=-fuse-ld=mold",
# Nightly
"-Zshare-generics=y",
"-Zthreads=0",
]

View file

@ -1,7 +1,10 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
use renderer::{Renderer2, render_graph, swapchain::WindowSurface}; use renderer::{
Renderer2, render_graph,
swapchain::{Surface, SwapchainConfiguration},
};
use tracing::info; use tracing::info;
use tracing_subscriber::EnvFilter; use tracing_subscriber::EnvFilter;
use winit::{ use winit::{
@ -18,7 +21,7 @@ struct WindowState {
egui_platform: egui_winit_platform::Platform, egui_platform: egui_winit_platform::Platform,
demo_app: egui_demo_lib::DemoWindows, demo_app: egui_demo_lib::DemoWindows,
scale_factor: f64, scale_factor: f64,
surface: WindowSurface, surface: Surface,
} }
struct WinitState { struct WinitState {
@ -53,13 +56,24 @@ impl WinitState {
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); _ = (window_id, new_size);
info!("TODO: implement resize events"); info!("TODO: implement resize events");
if let Some(window) = self.windows.get(&window_id) { if let Some(WindowState { surface, .. }) = self.windows.get(&window_id) {
window let config = surface
.surface .swapchain()
.recreate_with(Some(renderer::Extent2D { .as_ref()
width: new_size.width, .map(|swapchain| swapchain.config().clone())
height: new_size.height, .unwrap_or(SwapchainConfiguration::default());
}))
surface
.configure(
self.renderer.device(),
SwapchainConfiguration {
extent: renderer::Extent2D {
width: new_size.width,
height: new_size.height,
},
..config
},
)
.expect("swapchain recreation"); .expect("swapchain recreation");
} }
} }
@ -91,10 +105,8 @@ impl WinitState {
let dev = renderer.device().clone(); let dev = renderer.device().clone();
use renderer::ash::vk::Handle; use renderer::ash::vk::Handle;
use renderer::device::DeviceOwned;
let [r, g, b]: [f32; 3] = let [r, g, b]: [f32; 3] =
rand::prelude::StdRng::seed_from_u64(window.surface.surface.handle().as_raw()) rand::prelude::StdRng::seed_from_u64(window.surface.raw().as_raw()).random();
.random();
render_graph::clear_pass(rg, renderer::util::Rgba([r, g, b, 1.0]), framebuffer); render_graph::clear_pass(rg, renderer::util::Rgba([r, g, b, 1.0]), framebuffer);
egui_pre_pass( egui_pre_pass(
&dev, &dev,
@ -290,7 +302,7 @@ impl ApplicationHandler for WinitState {
} }
fn main() { fn main() {
let _ = tracing_subscriber::fmt() _ = tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env()) .with_env_filter(EnvFilter::from_default_env())
.init(); .init();
let ev = EventLoop::new().unwrap(); let ev = EventLoop::new().unwrap();

View file

@ -1,12 +1,13 @@
#!/bin/bash #! /usr/bin/env nix-shell
#! nix-shell -i bash -p shader-slang
set -e set -e
SLANGC="/opt/shader-slang-bin/bin/slangc" SLANGC="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_vert.spv -entry vertex
$SLANGC egui.slang -profile glsl_450 -target spirv -o egui_frag.spv -entry fragment $SLANGC egui.slang -profile glsl_450 -target spirv -o egui_frag.spv -entry fragment
$SLANGC egui.slang -profile glsl_450 -target spirv -entry vertex -entry fragment -o egui.spv $SLANGC egui.slang -profile glsl_450 -target spirv -entry vertex -entry fragment -o egui.spv
$SLANGC wireframe.slang -profile glsl_450 -target spirv -entry vertex -entry fragment -o wireframe.spv $SLANGC wireframe.slang -profile glsl_450 -target spirv -entry vertex -entry fragment -o wireframe.spv
$SLANGC font.slang -profile glsl_450 -target spirv -entry vertex -entry fragment -o font.spv $SLANGC font.slang -profile glsl_450 -target spirv -entry vertex -entry fragment -o font.spv
$SLANGC font.slang -profile glsl_450 -target spirv -entry mesh -entry task -entry fragment_barycentric -o font_mesh.spv # $SLANGC font.slang -profile glsl_450 -target spirv -entry mesh -entry task -entry fragment_barycentric -o font_mesh.spv

View file

@ -3,14 +3,14 @@ struct Fragment {
} }
struct VertexIn { struct VertexIn {
[[vk::layout(0)]] float2 pos; [[vk::location(0)]] float2 pos;
[[vk::layout(1)]] float2 uv; [[vk::location(1)]] float2 uv;
[[vk::layout(2)]] float4 color; [[vk::location(2)]] float4 color;
} }
struct VertexOut { struct VertexOut {
[[vk::layout(0)]] float4 color; [[vk::location(0)]] float4 color;
[[vk::layout(1)]] float2 uv; [[vk::location(1)]] float2 uv;
[[vk::layout(2), flat]] uint draw_id; nointerpolation [[vk::location(2)]] uint draw_id;
float4 position : SV_Position; float4 position : SV_Position;
} }

Binary file not shown.

View file

@ -1,9 +1,9 @@
struct VertexIn { struct VertexIn {
[[vk::layout(0)]] float2 pos; [[vk::location(0)]] float2 pos;
} }
struct VertexOut { struct VertexOut {
[[vk::layout(0)]] float4 color; [[vk::location(0)]] float4 color;
float4 position : SV_Position; float4 position : SV_Position;
} }

Binary file not shown.

View file

@ -1,9 +1,9 @@
struct VertexIn { struct VertexIn {
[[vk::layout(0)]] float2 pos; [[vk::location(0)]] float2 pos;
[[vk::layout(1)]] float4 color; [[vk::location(1)]] float4 color;
} }
struct VertexOut { struct VertexOut {
[[vk::layout(0)]] float4 color; [[vk::location(0)]] float4 color;
float4 position : SV_Position; float4 position : SV_Position;
} }

View file

@ -74,7 +74,7 @@ impl Default for BufferDesc {
#[derive(Debug)] #[derive(Debug)]
pub struct Buffer { pub struct Buffer {
buffer: DeviceObject<vk::Buffer>, buffer: DeviceObject<vk::Buffer>,
desc: BufferDesc, pub desc: BufferDesc,
alloc: Allocation, alloc: Allocation,
} }
@ -106,6 +106,12 @@ impl Buffer {
}, },
})?; })?;
unsafe {
device
.raw
.bind_buffer_memory(buffer, alloc.memory(), alloc.offset())?;
}
Ok(Self { Ok(Self {
buffer: DeviceObject::new(buffer, device.clone(), desc.name.clone()), buffer: DeviceObject::new(buffer, device.clone(), desc.name.clone()),
desc, desc,
@ -138,7 +144,9 @@ impl Buffer {
pub fn map(&mut self) -> Option<&[u8]> { pub fn map(&mut self) -> Option<&[u8]> {
if let Some(alloc) = self.alloc.allocation() { if let Some(alloc) = self.alloc.allocation() {
alloc.mapped_slice() alloc
.mapped_slice()
.map(|slice| &slice[..self.desc.size as usize])
} else { } else {
None None
} }
@ -146,7 +154,9 @@ impl Buffer {
pub fn map_mut(&mut self) -> Option<&mut [u8]> { pub fn map_mut(&mut self) -> Option<&mut [u8]> {
if let Some(alloc) = self.alloc.allocation_mut() { if let Some(alloc) = self.alloc.allocation_mut() {
alloc.mapped_slice_mut() alloc
.mapped_slice_mut()
.map(|slice| &mut slice[..self.desc.size as usize])
} else { } else {
None None
} }

View file

@ -568,7 +568,7 @@ impl DeviceInner {
let buffer_vec: Vec<u8>; let buffer_vec: Vec<u8>;
let name_bytes = if name.is_empty() { let name_bytes = if name.is_empty() {
&[] &[0]
} else if name.len() < buffer.len() { } else if name.len() < buffer.len() {
buffer[..name.len()].copy_from_slice(name.as_bytes()); buffer[..name.len()].copy_from_slice(name.as_bytes());
&buffer[..] &buffer[..]
@ -582,8 +582,14 @@ impl DeviceInner {
&buffer_vec &buffer_vec
}; };
let name = CStr::from_bytes_with_nul(name_bytes) let name = CStr::from_bytes_until_nul(name_bytes)
.expect("there is always a nul terminator because we added one"); .inspect_err(|_| {
panic!(
"debug name {:?} contains interior nul byte, which is not allowed",
name_bytes
)
})
.expect("{name_bytes:?} there is always a nul terminator because we added one");
unsafe { unsafe {
_ = self _ = self

View file

@ -373,7 +373,7 @@ pub fn egui_pre_pass(
extent: vk::Extent3D { extent: vk::Extent3D {
width: size.0, width: size.0,
height: size.1, height: size.1,
depth: 0, depth: 1,
}, },
}], }],
); );
@ -508,7 +508,8 @@ pub fn egui_pass(
let map = staging.map_mut().unwrap(); let map = staging.map_mut().unwrap();
let (st_vertices, rest) = map.split_at_mut(vertices_size); let (st_vertices, rest) = map.split_at_mut(vertices_size);
let (st_indices, st_drawcalls) = rest.split_at_mut(indices_size); let (st_indices, rest) = rest.split_at_mut(indices_size);
let (st_drawcalls, _rest) = rest.split_at_mut(draw_calls_size);
st_vertices.copy_from_slice(bytemuck::cast_slice(&vertices)); st_vertices.copy_from_slice(bytemuck::cast_slice(&vertices));
st_indices.copy_from_slice(bytemuck::cast_slice(&indices)); st_indices.copy_from_slice(bytemuck::cast_slice(&indices));
st_drawcalls.copy_from_slice(bytemuck::cast_slice(&draw_calls)); st_drawcalls.copy_from_slice(bytemuck::cast_slice(&draw_calls));

View file

@ -7,7 +7,7 @@ use crate::{
}; };
use super::Device; use super::Device;
use ash::vk; use ash::vk::{self, Handle};
use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme}; use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme};
use parking_lot::Mutex; use parking_lot::Mutex;
@ -172,10 +172,33 @@ impl Image {
}, },
})?; })?;
Self::new_with_allocation_unchecked(
device.clone(),
image,
Allocation::Owned(DeviceObject::new_without_name(alloc, device)),
desc,
)
}
fn new_with_allocation_unchecked(
device: Device,
image: vk::Image,
allocation: Allocation,
desc: ImageDesc,
) -> crate::Result<Self> {
// bind memory
if let Some(alloc) = allocation.allocation() {
unsafe {
device
.raw
.bind_image_memory(image, alloc.memory(), alloc.offset())?;
}
}
Ok(Self { Ok(Self {
image: ImageInner::Allocated( image: ImageInner::Allocated(
DeviceObject::new(image, device.clone(), desc.name.clone()), DeviceObject::new(image, device, desc.name.clone()),
Allocation::Owned(DeviceObject::new_without_name(alloc, device)), allocation,
), ),
desc, desc,
views: Default::default(), views: Default::default(),
@ -213,14 +236,7 @@ impl Image {
return Err(crate::Error::Unspecified); return Err(crate::Error::Unspecified);
} }
Ok(Self { Self::new_with_allocation_unchecked(device, image, allocation, desc)
image: ImageInner::Allocated(
DeviceObject::new(image, device.clone(), desc.name.clone()),
allocation,
),
desc,
views: Default::default(),
})
} }
pub fn from_swapchain_image(image: vk::Image, swapchain: &Swapchain) -> Self { pub fn from_swapchain_image(image: vk::Image, swapchain: &Swapchain) -> Self {
@ -252,7 +268,7 @@ impl Image {
device: Device, device: Device,
desc: &ImageDesc, desc: &ImageDesc,
) -> crate::Result<(vk::Image, vk::MemoryRequirements)> { ) -> crate::Result<(vk::Image, vk::MemoryRequirements)> {
tracing::trace!("allocate new image with desc={desc:?}"); tracing::trace!("new image with desc={desc:?}");
let ImageDesc { let ImageDesc {
flags, flags,
format, format,
@ -406,7 +422,7 @@ impl Image {
}); });
} }
if desc.mip_range.0 > self.desc.mip_levels || desc.mip_range.1 > self.desc.mip_levels { if !desc.mip_range.fits_in(self.desc.mip_levels) {
tracing::error!( tracing::error!(
"image view mip range {:?} exceeds image mip levels {}", "image view mip range {:?} exceeds image mip levels {}",
desc.mip_range, desc.mip_range,
@ -432,6 +448,11 @@ impl Image {
)); ));
} }
tracing::trace!(
"new image view with desc={desc:?} for image {:x}",
self.image().as_raw()
);
let create_info = vk::ImageViewCreateInfo::default() let create_info = vk::ImageViewCreateInfo::default()
.flags(desc.flags) .flags(desc.flags)
.image(self.image()) .image(self.image())
@ -441,9 +462,9 @@ impl Image {
.subresource_range( .subresource_range(
vk::ImageSubresourceRange::default() vk::ImageSubresourceRange::default()
.aspect_mask(desc.aspect) .aspect_mask(desc.aspect)
.base_mip_level(desc.mip_range.0) .base_mip_level(desc.mip_range.start)
.level_count(desc.mip_range.count()) .level_count(desc.mip_range.count())
.base_array_layer(desc.layer_range.0) .base_array_layer(desc.layer_range.start)
.layer_count(desc.layer_range.count()), .layer_count(desc.layer_range.count()),
); );
@ -497,6 +518,8 @@ impl ImageViewDesc {
Self { Self {
kind: vk::ImageViewType::TYPE_2D, kind: vk::ImageViewType::TYPE_2D,
aspect: vk::ImageAspectFlags::COLOR, aspect: vk::ImageAspectFlags::COLOR,
mip_range: MipRange { start: 0, end: 1 },
layer_range: MipRange { start: 0, end: 1 },
..Default::default() ..Default::default()
} }
} }
@ -557,17 +580,35 @@ impl ImageViewDesc {
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MipRange(u32, u32); pub struct MipRange {
start: u32,
end: u32,
}
impl Default for MipRange { impl Default for MipRange {
fn default() -> Self { fn default() -> Self {
Self(0, vk::REMAINING_ARRAY_LAYERS) Self {
start: 0,
end: vk::REMAINING_ARRAY_LAYERS,
}
} }
} }
impl MipRange { impl MipRange {
fn count(&self) -> u32 { pub fn count(&self) -> u32 {
self.1 self.end
}
pub fn fits_in(&self, total_mips: u32) -> bool {
self.start < total_mips && (self.end == vk::REMAINING_MIP_LEVELS || self.end <= total_mips)
}
pub(crate) fn end(&self) -> Option<u32> {
if self.end == vk::REMAINING_ARRAY_LAYERS {
None
} else {
Some(self.end)
}
} }
} }
@ -584,7 +625,7 @@ impl<R: core::ops::RangeBounds<u32>> From<R> for MipRange {
std::ops::Bound::Unbounded => vk::REMAINING_MIP_LEVELS, std::ops::Bound::Unbounded => vk::REMAINING_MIP_LEVELS,
}; };
Self(start, count) Self { start, end: count }
} }
} }

View file

@ -1,6 +1,7 @@
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
ffi::{CStr, CString}, ffi::{CStr, CString},
mem::MaybeUninit,
ops::Deref, ops::Deref,
sync::Arc, sync::Arc,
}; };
@ -20,8 +21,14 @@ pub struct DebugUtilsCreateInfo {
pub message_type: vk::DebugUtilsMessageTypeFlagsEXT, pub message_type: vk::DebugUtilsMessageTypeFlagsEXT,
} }
pub(crate) struct InstanceExtensions {
pub(crate) get_surface_capabilities2: Option<khr::get_surface_capabilities2::Instance>,
pub(crate) surface_maintenance1: Option<()>,
}
pub struct InstanceInner { pub struct InstanceInner {
pub raw: ash::Instance, pub raw: ash::Instance,
pub(crate) extensions: InstanceExtensions,
pub entry: Entry, pub entry: Entry,
pub(crate) _debug_utils: DebugUtils, pub(crate) _debug_utils: DebugUtils,
} }
@ -65,7 +72,7 @@ const DEBUG_LAYER_SETTINGS: &[vk::LayerSettingEXT] = &[];
const DEBUG_LAYER_SETTINGS: &[vk::LayerSettingEXT] = &[ const DEBUG_LAYER_SETTINGS: &[vk::LayerSettingEXT] = &[
vk::LayerSettingEXT { vk::LayerSettingEXT {
p_layer_name: VALIDATION_LAYER.as_ptr(), p_layer_name: VALIDATION_LAYER.as_ptr(),
p_setting_name: c"VK_KHRONOS_VALIDATION_VALIDATE_BEST_PRACTICES".as_ptr(), p_setting_name: c"validate_best_practices".as_ptr(),
value_count: 1, value_count: 1,
p_values: &[1u8; 1] as *const u8 as _, p_values: &[1u8; 1] as *const u8 as _,
ty: vk::LayerSettingTypeEXT::BOOL32, ty: vk::LayerSettingTypeEXT::BOOL32,
@ -73,7 +80,7 @@ const DEBUG_LAYER_SETTINGS: &[vk::LayerSettingEXT] = &[
}, },
vk::LayerSettingEXT { vk::LayerSettingEXT {
p_layer_name: VALIDATION_LAYER.as_ptr(), p_layer_name: VALIDATION_LAYER.as_ptr(),
p_setting_name: c"VK_KHRONOS_VALIDATION_VALIDATE_BEST_PRACTICES_AMD".as_ptr(), p_setting_name: c"validate_best_practices_amd".as_ptr(),
value_count: 1, value_count: 1,
p_values: &[1u8; 1] as *const u8 as _, p_values: &[1u8; 1] as *const u8 as _,
ty: vk::LayerSettingTypeEXT::BOOL32, ty: vk::LayerSettingTypeEXT::BOOL32,
@ -81,7 +88,7 @@ const DEBUG_LAYER_SETTINGS: &[vk::LayerSettingEXT] = &[
}, },
vk::LayerSettingEXT { vk::LayerSettingEXT {
p_layer_name: VALIDATION_LAYER.as_ptr(), p_layer_name: VALIDATION_LAYER.as_ptr(),
p_setting_name: c"VK_KHRONOS_VALIDATION_VALIDATE_SYNC".as_ptr(), p_setting_name: c"validate_sync".as_ptr(),
value_count: 1, value_count: 1,
p_values: &[1u8; 1] as *const u8 as _, p_values: &[1u8; 1] as *const u8 as _,
ty: vk::LayerSettingTypeEXT::BOOL32, ty: vk::LayerSettingTypeEXT::BOOL32,
@ -147,6 +154,12 @@ impl Instance {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
requested_extensions.push(make_extension!(ext::layer_settings)); requested_extensions.push(make_extension!(ext::layer_settings));
// These are wanted for surface capabilities querying, but not strictly
// required, and are only supported on newer devices, especially on
// NVIDIA cards.
requested_extensions.push(make_extension!(khr::get_surface_capabilities2));
requested_extensions.push(make_extension!(ext::surface_maintenance1));
let (extensions, unsupported_extensions) = let (extensions, unsupported_extensions) =
get_extensions(&entry, &layers, requested_extensions, desc.display_handle)?; get_extensions(&entry, &layers, requested_extensions, desc.display_handle)?;
@ -161,14 +174,14 @@ impl Instance {
.iter() .iter()
.map(|layer| layer.as_ptr()) .map(|layer| layer.as_ptr())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let extensions = extensions let extension_names = extensions
.iter() .iter()
.map(|ext| ext.name.as_ptr()) .map(|ext| ext.name.as_ptr())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let create_info = vk::InstanceCreateInfo::default() let create_info = vk::InstanceCreateInfo::default()
.application_info(&app_info) .application_info(&app_info)
.enabled_extension_names(&extensions) .enabled_extension_names(&extension_names)
.enabled_layer_names(&layers) .enabled_layer_names(&layers)
.push_next(&mut validation_info); .push_next(&mut validation_info);
@ -201,8 +214,18 @@ impl Instance {
} }
}; };
let extensions = InstanceExtensions {
get_surface_capabilities2: extensions
.contains(&make_extension!(khr::get_surface_capabilities2))
.then(|| khr::get_surface_capabilities2::Instance::new(&entry, &instance)),
surface_maintenance1: extensions
.contains(&make_extension!(ext::surface_maintenance1))
.then_some(()),
};
let instance = Arc::new(InstanceInner { let instance = Arc::new(InstanceInner {
raw: instance, raw: instance,
extensions,
_debug_utils: debug_utils, _debug_utils: debug_utils,
entry, entry,
}); });
@ -225,17 +248,19 @@ impl Instance {
pdevs.pop().ok_or(Error::NoAdapter) pdevs.pop().ok_or(Error::NoAdapter)
} }
/// Queries the surface capabilities of the given physical device and
/// surface, including supported formats and present modes.
/// If a present mode is provided, and the
/// `VK_KHR_get_surface_capabilities2` and `VK_EXT_surface_maintenance1`
/// extensions are available, it will also query present mode compatibility
/// information to filter the present modes based on the provided initial
/// mode.
pub(crate) fn get_adapter_surface_capabilities( pub(crate) fn get_adapter_surface_capabilities(
&self, &self,
pdev: vk::PhysicalDevice, pdev: vk::PhysicalDevice,
surface: &Surface, surface: &Surface,
present_mode: Option<vk::PresentModeKHR>,
) -> crate::Result<SurfaceCapabilities> { ) -> crate::Result<SurfaceCapabilities> {
let capabilities = unsafe {
surface
.functor
.get_physical_device_surface_capabilities(pdev, surface.raw)?
};
let formats = unsafe { let formats = unsafe {
surface surface
.functor .functor
@ -248,8 +273,52 @@ impl Instance {
.get_physical_device_surface_present_modes(pdev, surface.raw)? .get_physical_device_surface_present_modes(pdev, surface.raw)?
}; };
let generic_caps = unsafe {
surface
.functor
.get_physical_device_surface_capabilities(pdev, surface.raw)?
};
let (capabilities, present_modes) = if let Some(ref functor) =
self.extensions.get_surface_capabilities2
&& self.extensions.surface_maintenance1.is_some()
{
let mut capabilities = vk::SurfaceCapabilities2KHR::default();
let mut surface_info =
vk::PhysicalDeviceSurfaceInfo2KHR::default().surface(surface.raw());
let present_mode = present_mode.unwrap_or(present_modes[0]);
let mut surface_present_mode =
vk::SurfacePresentModeEXT::default().present_mode(present_mode);
surface_info = surface_info.push_next(&mut surface_present_mode);
let mut present_modes = MaybeUninit::<[vk::PresentModeKHR; 8]>::uninit();
let mut present_mode_info = vk::SurfacePresentModeCompatibilityEXT::default()
.present_modes(unsafe { present_modes.assume_init_mut() });
capabilities = capabilities.push_next(&mut present_mode_info);
unsafe {
functor.get_physical_device_surface_capabilities2(
pdev,
&surface_info,
&mut capabilities,
)?;
}
(capabilities.surface_capabilities, unsafe {
let num_present_modes = present_mode_info.present_mode_count as usize;
present_modes.assume_init()[..num_present_modes].to_vec()
})
} else {
(generic_caps, present_modes)
};
// surface.functor
// .get_physical_device_surface_capabilities2(pdev, surface.raw, present_mode)?;
Ok(SurfaceCapabilities { Ok(SurfaceCapabilities {
capabilities, capabilities,
min_image_count: generic_caps.min_image_count,
formats, formats,
present_modes, present_modes,
}) })
@ -260,7 +329,7 @@ impl Instance {
pdev: vk::PhysicalDevice, pdev: vk::PhysicalDevice,
) -> crate::Result<PhysicalDeviceInfo> { ) -> crate::Result<PhysicalDeviceInfo> {
let properties = get_physical_device_properties(&self.inner, pdev)?; let properties = get_physical_device_properties(&self.inner, pdev)?;
let features = get_physical_device_features(&self.inner, pdev, &properties)?; let features = get_physical_device_features(&self.inner, pdev, &properties);
Ok(PhysicalDeviceInfo { Ok(PhysicalDeviceInfo {
pdev, pdev,

View file

@ -5,7 +5,7 @@
slice_partition_dedup slice_partition_dedup
)] )]
use std::{collections::HashMap, fmt::Debug, sync::Arc}; use std::{collections::HashMap, fmt::Debug, path::PathBuf, sync::Arc};
use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
@ -115,6 +115,8 @@ pub enum Error {
view_kind: vk::ImageViewType, view_kind: vk::ImageViewType,
image_kind: vk::ImageType, image_kind: vk::ImageType,
}, },
#[error("Asset missing at path {0:?}")]
AssetMissing(PathBuf),
#[error("Unspecified Error")] #[error("Unspecified Error")]
Unspecified, Unspecified,
#[error("BEEP BOOP create an error variant for {0} BEEP BOOP")] #[error("BEEP BOOP create an error variant for {0} BEEP BOOP")]
@ -181,6 +183,9 @@ pub struct PhysicalDeviceInfo {
#[derive(Debug)] #[derive(Debug)]
pub struct SurfaceCapabilities { pub struct SurfaceCapabilities {
pub capabilities: SurfaceCapabilitiesKHR, pub capabilities: SurfaceCapabilitiesKHR,
/// The minimum number of images that must be present in the swapchain for
/// the surface for any present mode supported by the surface.
pub min_image_count: u32,
pub formats: Vec<vk::SurfaceFormatKHR>, pub formats: Vec<vk::SurfaceFormatKHR>,
pub present_modes: Vec<vk::PresentModeKHR>, pub present_modes: Vec<vk::PresentModeKHR>,
} }
@ -558,10 +563,15 @@ fn get_physical_device_features(
instance: &InstanceInner, instance: &InstanceInner,
pdev: vk::PhysicalDevice, pdev: vk::PhysicalDevice,
properties: &PhysicalDeviceProperties, properties: &PhysicalDeviceProperties,
) -> Result<PhysicalDeviceFeatures> { ) -> PhysicalDeviceFeatures {
let mut features = PhysicalDeviceFeatures::default(); let mut features = PhysicalDeviceFeatures::default();
let mut features2 = vk::PhysicalDeviceFeatures2::default(); let mut features2 = vk::PhysicalDeviceFeatures2::default();
features2 = features2
.push_next(&mut features.core11)
.push_next(&mut features.core12)
.push_next(&mut features.core13);
if properties.supports_extension(make_extension!(ext::mesh_shader)) { if properties.supports_extension(make_extension!(ext::mesh_shader)) {
features2 = features2.push_next( features2 = features2.push_next(
features features
@ -576,7 +586,9 @@ fn get_physical_device_features(
.get_physical_device_features2(pdev, &mut features2); .get_physical_device_features2(pdev, &mut features2);
} }
todo!() features.core = features2.features;
features
} }
fn get_physical_device_properties( fn get_physical_device_properties(
@ -738,7 +750,6 @@ impl EguiState {
}, },
], ],
max_sets: 1, max_sets: 1,
..Default::default()
}, },
)?; )?;
let descriptor_layout = pipeline::DescriptorSetLayout::new( let descriptor_layout = pipeline::DescriptorSetLayout::new(
@ -931,7 +942,25 @@ impl Renderer2 {
let device = adapter.create_logical_device( let device = adapter.create_logical_device(
&instance, &instance,
&[], &[],
PhysicalDeviceFeatures::default(), PhysicalDeviceFeatures {
core11: vk::PhysicalDeviceVulkan11Features {
shader_draw_parameters: vk::TRUE,
..Default::default()
},
core12: vk::PhysicalDeviceVulkan12Features {
descriptor_binding_partially_bound: vk::TRUE,
descriptor_binding_sampled_image_update_after_bind: vk::TRUE,
runtime_descriptor_array: vk::TRUE,
..Default::default()
},
core13: vk::PhysicalDeviceVulkan13Features {
synchronization2: vk::TRUE,
dynamic_rendering: vk::TRUE,
maintenance4: vk::TRUE,
..Default::default()
},
..Default::default()
},
Some(display), Some(display),
)?; )?;
@ -975,13 +1004,8 @@ impl Renderer2 {
surface.configure( surface.configure(
&self.device, &self.device,
swapchain::SwapchainConfiguration { swapchain::SwapchainConfiguration {
present_mode: vk::PresentModeKHR::MAILBOX,
format: vk::Format::R8G8B8A8_UNORM,
color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR,
image_count: 3,
extent, extent,
composite_alpha_mode: vk::CompositeAlphaFlagsKHR::OPAQUE, ..Default::default()
usage: vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSFER_DST,
}, },
)?; )?;
Ok(surface) Ok(surface)

View file

@ -449,13 +449,14 @@ impl ShaderModule {
pub fn new_from_path<P: AsRef<Path>>(device: Device, path: P) -> crate::Result<Self> { pub fn new_from_path<P: AsRef<Path>>(device: Device, path: P) -> crate::Result<Self> {
use std::io::{BufReader, Read, Seek}; use std::io::{BufReader, Read, Seek};
let mut file = std::fs::File::open(path)?; let path = path.as_ref();
let mut file =
std::fs::File::open(path).map_err(|_| crate::Error::AssetMissing(path.to_owned()))?;
let size = file.seek(std::io::SeekFrom::End(0))? / 4; let size = file.seek(std::io::SeekFrom::End(0))? / 4;
file.seek(std::io::SeekFrom::Start(0))?; file.seek(std::io::SeekFrom::Start(0))?;
let mut reader = BufReader::new(file); let mut reader = BufReader::new(file);
let mut buffer = Vec::<u32>::with_capacity(size as usize); let mut buffer = vec![0; size as usize];
buffer.resize(size as usize, 0);
let size = reader.read(bytemuck::cast_slice_mut(buffer.as_mut_slice()))?; let size = reader.read(bytemuck::cast_slice_mut(buffer.as_mut_slice()))?;
buffer.resize(size / 4, 0); buffer.resize(size / 4, 0);

View file

@ -1,5 +1,6 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
num::NonZero,
ops::Deref, ops::Deref,
sync::{ sync::{
Arc, Arc,
@ -30,7 +31,6 @@ pub struct Surface {
#[debug(skip)] #[debug(skip)]
pub(crate) functor: khr::surface::Instance, pub(crate) functor: khr::surface::Instance,
pub(crate) instance: Instance, pub(crate) instance: Instance,
pub(crate) capabilities: Mutex<HashMap<vk::PhysicalDevice, SurfaceCapabilities>>,
pub(crate) swapchain: RwLock<Option<Arc<Swapchain>>>, pub(crate) swapchain: RwLock<Option<Arc<Swapchain>>>,
} }
@ -43,23 +43,6 @@ impl Drop for Surface {
} }
impl Surface { impl Surface {
pub fn get_capabilities<'a>(
&'a self,
adapter: vk::PhysicalDevice,
) -> parking_lot::MappedMutexGuard<'a, SurfaceCapabilities> {
let lock = self.capabilities.lock();
parking_lot::MutexGuard::map(
lock,
|caps: &mut HashMap<vk::PhysicalDevice, SurfaceCapabilities>| {
caps.entry(adapter).or_insert_with(|| {
self.instance
.get_adapter_surface_capabilities(adapter, self)
.expect("surface is not compatible with the adapter")
})
},
)
}
pub fn configure(&self, device: &Device, config: SwapchainConfiguration) -> Result<()> { pub fn configure(&self, device: &Device, config: SwapchainConfiguration) -> Result<()> {
let guard = self.swapchain.read(); let guard = self.swapchain.read();
if let Some(swapchain) = guard.as_ref() if let Some(swapchain) = guard.as_ref()
@ -80,7 +63,7 @@ impl Surface {
drop(guard); drop(guard);
self.swapchain.write().replace(Arc::new(new_swapchain)); self.swapchain.write().replace(Arc::new(new_swapchain));
todo!() Ok(())
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -100,7 +83,6 @@ impl Surface {
Ok(Self { Ok(Self {
raw, raw,
functor, functor,
capabilities: Mutex::new(HashMap::new()),
swapchain: RwLock::new(None), swapchain: RwLock::new(None),
instance: instance.clone(), instance: instance.clone(),
}) })
@ -136,7 +118,6 @@ impl Surface {
Ok(Self { Ok(Self {
raw, raw,
functor, functor,
capabilities: Mutex::new(HashMap::new()),
swapchain: RwLock::new(None), swapchain: RwLock::new(None),
instance: instance.clone(), instance: instance.clone(),
}) })
@ -150,8 +131,8 @@ impl Surface {
instance: &Instance, instance: &Instance,
adapter: &PhysicalDeviceInfo, adapter: &PhysicalDeviceInfo,
config: &mut SwapchainConfiguration, config: &mut SwapchainConfiguration,
) -> Result<()> { ) -> Result<SurfaceCapabilities> {
let surface_caps = instance.get_adapter_surface_capabilities(adapter.pdev, self)?; let surface_caps = instance.get_adapter_surface_capabilities(adapter.pdev, self, None)?;
let max_image_dim = adapter.properties.core.limits.max_image_dimension2_d; let max_image_dim = adapter.properties.core.limits.max_image_dimension2_d;
if config.extent.width > max_image_dim || config.extent.height > max_image_dim { if config.extent.width > max_image_dim || config.extent.height > max_image_dim {
@ -182,10 +163,39 @@ impl Surface {
config.present_mode = fallback_mode; config.present_mode = fallback_mode;
} }
// We do this calculation with the original surface capabilities, which
// gives us the maximum min_image_count of all present modes, which
// allows us to switch present modes without needing to recreate the
// swapchain.
let max_image_count = NonZero::new(surface_caps.capabilities.max_image_count)
.map(|n| n.get())
.unwrap_or(u32::MAX);
let min_image_count = surface_caps.min_image_count;
// we want `config.image_count` images acquired at the same time, but we
// also need to respect the surface's minimum and maximum image count
// limits.
let image_count = (min_image_count + config.image_count).min(max_image_count);
// in case the surface's image count limits prevent us from having the
// desired number of images in flight, reduce the requested image count
// to fit within the limits.
// this value corresponds to `S-M` in the Vulkan spec.
config.image_count = image_count - min_image_count;
// different present modes have different image counts/extents: now that
// we know we have a supported present mode, re-query the surface
// capabilities to get the correct image counts.
let surface_caps = instance.get_adapter_surface_capabilities(
adapter.pdev,
self,
Some(config.present_mode),
)?;
if !surface_caps.formats.iter().any(|&format| { if !surface_caps.formats.iter().any(|&format| {
format.format == config.format && format.color_space == config.color_space format.format == config.format && format.color_space == config.color_space
}) { }) {
// wgpu just rejects the swapchain if the format is not supported. is that smarter? // (note): wgpu just rejects the swapchain if the format is not supported. is that smarter?
// find a fallback format // find a fallback format
let format = surface_caps let format = surface_caps
.formats .formats
@ -206,16 +216,16 @@ impl Surface {
config.color_space = format.color_space; config.color_space = format.color_space;
} }
Ok(()) Ok(surface_caps)
} }
#[allow(dead_code)] #[allow(dead_code)]
fn get_fallback_swapchain_configuration( pub fn get_fallback_swapchain_configuration(
&self, &self,
instance: &Instance, instance: &Instance,
adapter: &PhysicalDeviceInfo, adapter: &PhysicalDeviceInfo,
) -> Result<SwapchainConfiguration> { ) -> Result<SwapchainConfiguration> {
let surface_caps = instance.get_adapter_surface_capabilities(adapter.pdev, self)?; let surface_caps = instance.get_adapter_surface_capabilities(adapter.pdev, self, None)?;
let present_mode = surface_caps let present_mode = surface_caps
.present_modes .present_modes
@ -279,11 +289,15 @@ impl Surface {
) -> Option<impl std::future::Future<Output = crate::Result<(SwapchainImage, bool)>>> { ) -> Option<impl std::future::Future<Output = crate::Result<(SwapchainImage, bool)>>> {
// ensure lock does not block for the entire duration of the async image acquisition. // ensure lock does not block for the entire duration of the async image acquisition.
let swapchain = self.swapchain.read().as_ref().cloned(); let swapchain = self.swapchain.read().as_ref().cloned();
if let Some(swapchain) = swapchain { swapchain.map(|swapchain| swapchain.acquire_image())
Some(swapchain.acquire_image()) }
} else {
None pub fn swapchain(&self) -> parking_lot::RwLockReadGuard<'_, Option<Arc<Swapchain>>> {
} self.swapchain.read()
}
pub fn raw(&self) -> vk::SurfaceKHR {
self.raw
} }
} }
@ -364,10 +378,13 @@ impl Swapchain {
mut config: SwapchainConfiguration, mut config: SwapchainConfiguration,
old_swapchain: Option<&Self>, old_swapchain: Option<&Self>,
) -> Result<Self> { ) -> Result<Self> {
surface.validate_swapchain_configuration(&device.instance, &device.adapter, &mut config)?; let surface_caps = surface.validate_swapchain_configuration(
let surface_caps = device &device.instance,
.instance &device.adapter,
.get_adapter_surface_capabilities(device.adapter.pdev, surface)?; &mut config,
)?;
tracing::trace!("surface capabilities: {surface_caps:#?}");
let functor = device let functor = device
.device_extensions .device_extensions
@ -388,7 +405,7 @@ impl Swapchain {
.present_mode(config.present_mode) .present_mode(config.present_mode)
.image_color_space(config.color_space) .image_color_space(config.color_space)
.image_format(config.format) .image_format(config.format)
.min_image_count(surface_caps.capabilities.min_image_count) .min_image_count(surface_caps.min_image_count + config.image_count)
.image_usage(config.usage) .image_usage(config.usage)
.image_array_layers(1) .image_array_layers(1)
.image_extent(config.extent) .image_extent(config.extent)
@ -463,7 +480,11 @@ impl Swapchain {
.collect::<VkResult<Vec<_>>>()? .collect::<VkResult<Vec<_>>>()?
}; };
tracing::trace!("fences: {fences:?}"); tracing::trace!(
image_count = images.len(),
min_image_count = surface_caps.capabilities.min_image_count,
config = ?config,
"created swapchain:");
Ok(Swapchain { Ok(Swapchain {
functor: device functor: device
@ -590,6 +611,10 @@ impl Swapchain {
Ok(()) Ok(())
}) })
} }
pub fn config(&self) -> &SwapchainConfiguration {
&self.config
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -691,6 +716,20 @@ pub struct SwapchainConfiguration {
pub usage: vk::ImageUsageFlags, pub usage: vk::ImageUsageFlags,
} }
impl Default for SwapchainConfiguration {
fn default() -> Self {
Self {
present_mode: vk::PresentModeKHR::MAILBOX,
format: vk::Format::R8G8B8A8_UNORM,
color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR,
image_count: 3,
extent: vk::Extent2D::default().width(1).height(1),
composite_alpha_mode: vk::CompositeAlphaFlagsKHR::OPAQUE,
usage: vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::COLOR_ATTACHMENT,
}
}
}
impl Swapchain { impl Swapchain {
pub fn with_locked<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { pub fn with_locked<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T {
let _lock = self.guard.lock(); let _lock = self.guard.lock();