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 rand::{Rng, SeedableRng};
use renderer::{Renderer2, render_graph, swapchain::WindowSurface};
use renderer::{
Renderer2, render_graph,
swapchain::{Surface, SwapchainConfiguration},
};
use tracing::info;
use tracing_subscriber::EnvFilter;
use winit::{
@ -18,7 +21,7 @@ struct WindowState {
egui_platform: egui_winit_platform::Platform,
demo_app: egui_demo_lib::DemoWindows,
scale_factor: f64,
surface: WindowSurface,
surface: Surface,
}
struct WinitState {
@ -53,13 +56,24 @@ impl WinitState {
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(window) = self.windows.get(&window_id) {
window
.surface
.recreate_with(Some(renderer::Extent2D {
if let Some(WindowState { surface, .. }) = self.windows.get(&window_id) {
let config = surface
.swapchain()
.as_ref()
.map(|swapchain| swapchain.config().clone())
.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");
}
}
@ -91,10 +105,8 @@ impl WinitState {
let dev = renderer.device().clone();
use renderer::ash::vk::Handle;
use renderer::device::DeviceOwned;
let [r, g, b]: [f32; 3] =
rand::prelude::StdRng::seed_from_u64(window.surface.surface.handle().as_raw())
.random();
rand::prelude::StdRng::seed_from_u64(window.surface.raw().as_raw()).random();
render_graph::clear_pass(rg, renderer::util::Rgba([r, g, b, 1.0]), framebuffer);
egui_pre_pass(
&dev,
@ -290,7 +302,7 @@ impl ApplicationHandler for WinitState {
}
fn main() {
let _ = tracing_subscriber::fmt()
_ = tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.init();
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
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_frag.spv -entry fragment
$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 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 {
[[vk::layout(0)]] float2 pos;
[[vk::layout(1)]] float2 uv;
[[vk::layout(2)]] float4 color;
[[vk::location(0)]] float2 pos;
[[vk::location(1)]] float2 uv;
[[vk::location(2)]] float4 color;
}
struct VertexOut {
[[vk::layout(0)]] float4 color;
[[vk::layout(1)]] float2 uv;
[[vk::layout(2), flat]] uint draw_id;
[[vk::location(0)]] float4 color;
[[vk::location(1)]] float2 uv;
nointerpolation [[vk::location(2)]] uint draw_id;
float4 position : SV_Position;
}

Binary file not shown.

View file

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

Binary file not shown.

View file

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

View file

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

View file

@ -568,7 +568,7 @@ impl DeviceInner {
let buffer_vec: Vec<u8>;
let name_bytes = if name.is_empty() {
&[]
&[0]
} else if name.len() < buffer.len() {
buffer[..name.len()].copy_from_slice(name.as_bytes());
&buffer[..]
@ -582,8 +582,14 @@ impl DeviceInner {
&buffer_vec
};
let name = CStr::from_bytes_with_nul(name_bytes)
.expect("there is always a nul terminator because we added one");
let name = CStr::from_bytes_until_nul(name_bytes)
.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 {
_ = self

View file

@ -373,7 +373,7 @@ pub fn egui_pre_pass(
extent: vk::Extent3D {
width: size.0,
height: size.1,
depth: 0,
depth: 1,
},
}],
);
@ -508,7 +508,8 @@ pub fn egui_pass(
let map = staging.map_mut().unwrap();
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_indices.copy_from_slice(bytemuck::cast_slice(&indices));
st_drawcalls.copy_from_slice(bytemuck::cast_slice(&draw_calls));

View file

@ -7,7 +7,7 @@ use crate::{
};
use super::Device;
use ash::vk;
use ash::vk::{self, Handle};
use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme};
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 {
image: ImageInner::Allocated(
DeviceObject::new(image, device.clone(), desc.name.clone()),
Allocation::Owned(DeviceObject::new_without_name(alloc, device)),
DeviceObject::new(image, device, desc.name.clone()),
allocation,
),
desc,
views: Default::default(),
@ -213,14 +236,7 @@ impl Image {
return Err(crate::Error::Unspecified);
}
Ok(Self {
image: ImageInner::Allocated(
DeviceObject::new(image, device.clone(), desc.name.clone()),
allocation,
),
desc,
views: Default::default(),
})
Self::new_with_allocation_unchecked(device, image, allocation, desc)
}
pub fn from_swapchain_image(image: vk::Image, swapchain: &Swapchain) -> Self {
@ -252,7 +268,7 @@ impl Image {
device: Device,
desc: &ImageDesc,
) -> crate::Result<(vk::Image, vk::MemoryRequirements)> {
tracing::trace!("allocate new image with desc={desc:?}");
tracing::trace!("new image with desc={desc:?}");
let ImageDesc {
flags,
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!(
"image view mip range {:?} exceeds image mip levels {}",
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()
.flags(desc.flags)
.image(self.image())
@ -441,9 +462,9 @@ impl Image {
.subresource_range(
vk::ImageSubresourceRange::default()
.aspect_mask(desc.aspect)
.base_mip_level(desc.mip_range.0)
.base_mip_level(desc.mip_range.start)
.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()),
);
@ -497,6 +518,8 @@ impl ImageViewDesc {
Self {
kind: vk::ImageViewType::TYPE_2D,
aspect: vk::ImageAspectFlags::COLOR,
mip_range: MipRange { start: 0, end: 1 },
layer_range: MipRange { start: 0, end: 1 },
..Default::default()
}
}
@ -557,17 +580,35 @@ impl ImageViewDesc {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MipRange(u32, u32);
pub struct MipRange {
start: u32,
end: u32,
}
impl Default for MipRange {
fn default() -> Self {
Self(0, vk::REMAINING_ARRAY_LAYERS)
Self {
start: 0,
end: vk::REMAINING_ARRAY_LAYERS,
}
}
}
impl MipRange {
fn count(&self) -> u32 {
self.1
pub fn count(&self) -> u32 {
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,
};
Self(start, count)
Self { start, end: count }
}
}

View file

@ -1,6 +1,7 @@
use std::{
cmp::Ordering,
ffi::{CStr, CString},
mem::MaybeUninit,
ops::Deref,
sync::Arc,
};
@ -20,8 +21,14 @@ pub struct DebugUtilsCreateInfo {
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 raw: ash::Instance,
pub(crate) extensions: InstanceExtensions,
pub entry: Entry,
pub(crate) _debug_utils: DebugUtils,
}
@ -65,7 +72,7 @@ const DEBUG_LAYER_SETTINGS: &[vk::LayerSettingEXT] = &[];
const DEBUG_LAYER_SETTINGS: &[vk::LayerSettingEXT] = &[
vk::LayerSettingEXT {
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,
p_values: &[1u8; 1] as *const u8 as _,
ty: vk::LayerSettingTypeEXT::BOOL32,
@ -73,7 +80,7 @@ const DEBUG_LAYER_SETTINGS: &[vk::LayerSettingEXT] = &[
},
vk::LayerSettingEXT {
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,
p_values: &[1u8; 1] as *const u8 as _,
ty: vk::LayerSettingTypeEXT::BOOL32,
@ -81,7 +88,7 @@ const DEBUG_LAYER_SETTINGS: &[vk::LayerSettingEXT] = &[
},
vk::LayerSettingEXT {
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,
p_values: &[1u8; 1] as *const u8 as _,
ty: vk::LayerSettingTypeEXT::BOOL32,
@ -147,6 +154,12 @@ impl Instance {
#[cfg(debug_assertions)]
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) =
get_extensions(&entry, &layers, requested_extensions, desc.display_handle)?;
@ -161,14 +174,14 @@ impl Instance {
.iter()
.map(|layer| layer.as_ptr())
.collect::<Vec<_>>();
let extensions = extensions
let extension_names = extensions
.iter()
.map(|ext| ext.name.as_ptr())
.collect::<Vec<_>>();
let create_info = vk::InstanceCreateInfo::default()
.application_info(&app_info)
.enabled_extension_names(&extensions)
.enabled_extension_names(&extension_names)
.enabled_layer_names(&layers)
.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 {
raw: instance,
extensions,
_debug_utils: debug_utils,
entry,
});
@ -225,17 +248,19 @@ impl Instance {
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(
&self,
pdev: vk::PhysicalDevice,
surface: &Surface,
present_mode: Option<vk::PresentModeKHR>,
) -> crate::Result<SurfaceCapabilities> {
let capabilities = unsafe {
surface
.functor
.get_physical_device_surface_capabilities(pdev, surface.raw)?
};
let formats = unsafe {
surface
.functor
@ -248,8 +273,52 @@ impl Instance {
.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 {
capabilities,
min_image_count: generic_caps.min_image_count,
formats,
present_modes,
})
@ -260,7 +329,7 @@ impl Instance {
pdev: vk::PhysicalDevice,
) -> crate::Result<PhysicalDeviceInfo> {
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 {
pdev,

View file

@ -5,7 +5,7 @@
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};
@ -115,6 +115,8 @@ pub enum Error {
view_kind: vk::ImageViewType,
image_kind: vk::ImageType,
},
#[error("Asset missing at path {0:?}")]
AssetMissing(PathBuf),
#[error("Unspecified Error")]
Unspecified,
#[error("BEEP BOOP create an error variant for {0} BEEP BOOP")]
@ -181,6 +183,9 @@ pub struct PhysicalDeviceInfo {
#[derive(Debug)]
pub struct SurfaceCapabilities {
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 present_modes: Vec<vk::PresentModeKHR>,
}
@ -558,10 +563,15 @@ fn get_physical_device_features(
instance: &InstanceInner,
pdev: vk::PhysicalDevice,
properties: &PhysicalDeviceProperties,
) -> Result<PhysicalDeviceFeatures> {
) -> PhysicalDeviceFeatures {
let mut features = PhysicalDeviceFeatures::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)) {
features2 = features2.push_next(
features
@ -576,7 +586,9 @@ fn get_physical_device_features(
.get_physical_device_features2(pdev, &mut features2);
}
todo!()
features.core = features2.features;
features
}
fn get_physical_device_properties(
@ -738,7 +750,6 @@ impl EguiState {
},
],
max_sets: 1,
..Default::default()
},
)?;
let descriptor_layout = pipeline::DescriptorSetLayout::new(
@ -931,7 +942,25 @@ impl Renderer2 {
let device = adapter.create_logical_device(
&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),
)?;
@ -975,13 +1004,8 @@ impl Renderer2 {
surface.configure(
&self.device,
swapchain::SwapchainConfiguration {
present_mode: vk::PresentModeKHR::MAILBOX,
format: vk::Format::R8G8B8A8_UNORM,
color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR,
image_count: 3,
extent,
composite_alpha_mode: vk::CompositeAlphaFlagsKHR::OPAQUE,
usage: vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSFER_DST,
..Default::default()
},
)?;
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> {
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;
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 mut buffer = vec![0; size as usize];
let size = reader.read(bytemuck::cast_slice_mut(buffer.as_mut_slice()))?;
buffer.resize(size / 4, 0);

View file

@ -1,5 +1,6 @@
use std::{
collections::HashMap,
num::NonZero,
ops::Deref,
sync::{
Arc,
@ -30,7 +31,6 @@ pub struct Surface {
#[debug(skip)]
pub(crate) functor: khr::surface::Instance,
pub(crate) instance: Instance,
pub(crate) capabilities: Mutex<HashMap<vk::PhysicalDevice, SurfaceCapabilities>>,
pub(crate) swapchain: RwLock<Option<Arc<Swapchain>>>,
}
@ -43,23 +43,6 @@ impl Drop for 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<()> {
let guard = self.swapchain.read();
if let Some(swapchain) = guard.as_ref()
@ -80,7 +63,7 @@ impl Surface {
drop(guard);
self.swapchain.write().replace(Arc::new(new_swapchain));
todo!()
Ok(())
}
#[allow(dead_code)]
@ -100,7 +83,6 @@ impl Surface {
Ok(Self {
raw,
functor,
capabilities: Mutex::new(HashMap::new()),
swapchain: RwLock::new(None),
instance: instance.clone(),
})
@ -136,7 +118,6 @@ impl Surface {
Ok(Self {
raw,
functor,
capabilities: Mutex::new(HashMap::new()),
swapchain: RwLock::new(None),
instance: instance.clone(),
})
@ -150,8 +131,8 @@ impl Surface {
instance: &Instance,
adapter: &PhysicalDeviceInfo,
config: &mut SwapchainConfiguration,
) -> Result<()> {
let surface_caps = instance.get_adapter_surface_capabilities(adapter.pdev, self)?;
) -> Result<SurfaceCapabilities> {
let surface_caps = instance.get_adapter_surface_capabilities(adapter.pdev, self, None)?;
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 {
@ -182,10 +163,39 @@ impl Surface {
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| {
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
let format = surface_caps
.formats
@ -206,16 +216,16 @@ impl Surface {
config.color_space = format.color_space;
}
Ok(())
Ok(surface_caps)
}
#[allow(dead_code)]
fn get_fallback_swapchain_configuration(
pub fn get_fallback_swapchain_configuration(
&self,
instance: &Instance,
adapter: &PhysicalDeviceInfo,
) -> 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
.present_modes
@ -279,11 +289,15 @@ impl Surface {
) -> Option<impl std::future::Future<Output = crate::Result<(SwapchainImage, bool)>>> {
// ensure lock does not block for the entire duration of the async image acquisition.
let swapchain = self.swapchain.read().as_ref().cloned();
if let Some(swapchain) = swapchain {
Some(swapchain.acquire_image())
} else {
None
swapchain.map(|swapchain| swapchain.acquire_image())
}
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,
old_swapchain: Option<&Self>,
) -> Result<Self> {
surface.validate_swapchain_configuration(&device.instance, &device.adapter, &mut config)?;
let surface_caps = device
.instance
.get_adapter_surface_capabilities(device.adapter.pdev, surface)?;
let surface_caps = surface.validate_swapchain_configuration(
&device.instance,
&device.adapter,
&mut config,
)?;
tracing::trace!("surface capabilities: {surface_caps:#?}");
let functor = device
.device_extensions
@ -388,7 +405,7 @@ impl Swapchain {
.present_mode(config.present_mode)
.image_color_space(config.color_space)
.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_array_layers(1)
.image_extent(config.extent)
@ -463,7 +480,11 @@ impl Swapchain {
.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 {
functor: device
@ -590,6 +611,10 @@ impl Swapchain {
Ok(())
})
}
pub fn config(&self) -> &SwapchainConfiguration {
&self.config
}
}
#[derive(Debug)]
@ -691,6 +716,20 @@ pub struct SwapchainConfiguration {
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 {
pub fn with_locked<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T {
let _lock = self.guard.lock();