render egui to the screen (finally)

This commit is contained in:
Janis 2024-12-31 11:19:23 +01:00
parent 81f1ee1f96
commit e8bcaae2a7
14 changed files with 1386 additions and 192 deletions

View file

@ -11,7 +11,7 @@ members = [
anyhow = "1.0.89" anyhow = "1.0.89"
ash = "0.38.0" ash = "0.38.0"
ash-window = "0.13.0" ash-window = "0.13.0"
glam = "0.29.0" glam = {version = "0.29.0", features = ["bytemuck"]}
thiserror = "2.0" thiserror = "2.0"
tracing = "0.1.40" tracing = "0.1.40"
tracing-subscriber = "0.3.18" tracing-subscriber = "0.3.18"

View file

@ -66,8 +66,10 @@ impl WinitState {
window.demo_app.ui(&window.egui_platform.context()); window.demo_app.ui(&window.egui_platform.context());
let output = window.egui_platform.end_pass(Some(&window.window)); let output = window.egui_platform.end_pass(Some(&window.window));
self.renderer let egui_state = self
.draw_egui(&window.egui_platform.context(), output); .renderer
.draw_egui(&window.egui_platform.context(), output)
.unwrap();
// rendering // rendering
self.renderer self.renderer

View file

@ -11,7 +11,7 @@ dyn-clone = "1"
anyhow = "1.0.89" anyhow = "1.0.89"
ash = "0.38.0" ash = "0.38.0"
ash-window = "0.13.0" ash-window = "0.13.0"
glam = "0.29.0" glam = {workspace = true}
thiserror = {workspace = true} thiserror = {workspace = true}
tracing = "0.1.40" tracing = "0.1.40"
tracing-subscriber = "0.3.18" tracing-subscriber = "0.3.18"
@ -23,5 +23,7 @@ smol.workspace = true
tracing-test = "0.2.5" tracing-test = "0.2.5"
raw-window-handle = { workspace = true } raw-window-handle = { workspace = true }
egui = { workspace = true } egui = { workspace = true , features = ["bytemuck"]}
egui_winit_platform = { workspace = true } egui_winit_platform = { workspace = true }
bytemuck = { version = "1.21.0", features = ["derive"] }
indexmap = "2.7.0"

View file

@ -10,6 +10,7 @@ struct VertexIn {
struct VertexOut { struct VertexOut {
[[vk::layout(0)]] float4 color; [[vk::layout(0)]] float4 color;
[[vk::layout(1)]] float2 uv; [[vk::layout(1)]] float2 uv;
[[vk::layout(2), flat]] uint draw_id;
float4 position : SV_Position; float4 position : SV_Position;
} }
@ -21,7 +22,7 @@ struct PushConstant {
ConstantBuffer<PushConstant> push_constant; ConstantBuffer<PushConstant> push_constant;
[shader("vertex")] [shader("vertex")]
VertexOut vertex(VertexIn vertex) { VertexOut vertex(VertexIn vertex, uint draw_id : SV_DrawIndex) {
VertexOut output; VertexOut output;
output.position = float4( output.position = float4(
@ -32,17 +33,23 @@ VertexOut vertex(VertexIn vertex) {
); );
output.color = vertex.color; output.color = vertex.color;
output.uv = vertex.uv; output.uv = vertex.uv;
output.draw_id = draw_id;
return output; return output;
} }
[[vk::binding(0)]] [[vk::binding(0)]]
Sampler2D texture; Sampler2D texture[];
[[vk::binding(1)]]
StructuredBuffer<uint> texture_ids;
[shader("fragment")] [shader("fragment")]
Fragment fragment(VertexOut input) { Fragment fragment(VertexOut input) {
Fragment output; Fragment output;
output.color = input.color * texture.Sample(input.uv); uint texture_id = texture_ids[input.draw_id];
output.color = input.color * texture[texture_id].Sample(input.uv);
return output; return output;
} }

View file

@ -6,23 +6,16 @@ use std::{
use ash::{prelude::VkResult, vk}; use ash::{prelude::VkResult, vk};
use vk_mem::Alloc; use vk_mem::Alloc;
use crate::Device; use crate::{define_device_owned_handle, device::DeviceOwned, Device};
pub struct Buffer { define_device_owned_handle! {
device: Device, #[derive(Debug)]
buffer: vk::Buffer, pub Buffer(vk::Buffer) {
allocation: vk_mem::Allocation, alloc: vk_mem::Allocation,
usage: vk::BufferUsageFlags, usage: vk::BufferUsageFlags,
size: u64, size: u64,
} } => |this| unsafe {
this.device().clone().alloc().destroy_buffer(this.handle(), &mut this.alloc);
impl Drop for Buffer {
fn drop(&mut self) {
unsafe {
self.device
.alloc()
.destroy_buffer(self.buffer, &mut self.allocation);
}
} }
} }
@ -34,6 +27,7 @@ impl Buffer {
queue_families: &[u32], queue_families: &[u32],
memory_usage: vk_mem::MemoryUsage, memory_usage: vk_mem::MemoryUsage,
alloc_flags: vk_mem::AllocationCreateFlags, alloc_flags: vk_mem::AllocationCreateFlags,
name: Option<std::borrow::Cow<'static, str>>,
) -> VkResult<Arc<Self>> { ) -> VkResult<Arc<Self>> {
let sharing_mode = if queue_families.len() > 1 { let sharing_mode = if queue_families.len() > 1 {
vk::SharingMode::CONCURRENT vk::SharingMode::CONCURRENT
@ -56,20 +50,23 @@ impl Buffer {
)? )?
}; };
let buffer = Self { Ok(Arc::new(Self::construct(
device, device,
buffer, buffer,
name,
allocation, allocation,
usage, 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<'_>> { pub fn map(&mut self) -> VkResult<MappedBuffer<'_>> {
let bytes = unsafe { let bytes = unsafe {
let data = self.device.alloc().map_memory(&mut self.allocation)?; let data = self.inner.dev().alloc().map_memory(&mut self.alloc)?;
let slice = core::slice::from_raw_parts_mut(data, self.size as usize); let slice = core::slice::from_raw_parts_mut(data, self.size as usize);
slice slice
@ -78,7 +75,10 @@ impl Buffer {
Ok(MappedBuffer { inner: self, bytes }) Ok(MappedBuffer { inner: self, bytes })
} }
pub fn buffer(&self) -> vk::Buffer { pub fn buffer(&self) -> vk::Buffer {
self.buffer self.handle()
}
pub fn len(&self) -> u64 {
self.size
} }
} }
@ -91,9 +91,10 @@ impl Drop for MappedBuffer<'_> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
self.inner self.inner
.device .inner
.dev()
.alloc() .alloc()
.unmap_memory(&mut self.inner.allocation); .unmap_memory(&mut self.inner.alloc);
} }
} }
} }

View file

@ -2,7 +2,9 @@ use std::{future::Future, marker::PhantomData, sync::Arc};
use crate::{ use crate::{
buffers::Buffer, buffers::Buffer,
device::DeviceOwned,
images::{Image2D, QueueOwnership}, images::{Image2D, QueueOwnership},
pipeline::{Pipeline, PipelineLayout},
sync::{self, FenceFuture}, sync::{self, FenceFuture},
util::{self, FormatExt, MutexExt}, util::{self, FormatExt, MutexExt},
}; };
@ -196,6 +198,14 @@ impl SingleUseCommand {
} }
} }
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( pub fn clear_color_image(
&self, &self,
image: vk::Image, image: vk::Image,
@ -235,7 +245,110 @@ impl SingleUseCommand {
} }
} }
// pub fn 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) { pub fn end_rendering(&self) {
unsafe { unsafe {

View file

@ -1,4 +1,9 @@
use std::{borrow::Cow, collections::BTreeMap, ops::Deref, sync::Arc}; use std::{
borrow::Cow,
collections::{BTreeMap, HashMap},
ops::Deref,
sync::Arc,
};
use ash::{ use ash::{
khr, khr,
@ -78,6 +83,7 @@ pub struct DeviceInner {
transfer_queue: Queue, transfer_queue: Queue,
present_queue: Queue, present_queue: Queue,
sync_threadpool: sync::SyncThreadpool, sync_threadpool: sync::SyncThreadpool,
features: crate::PhysicalDeviceFeatures,
} }
impl core::fmt::Debug for DeviceInner { impl core::fmt::Debug for DeviceInner {
@ -166,6 +172,7 @@ impl Device {
present_queue, present_queue,
compute_queue, compute_queue,
transfer_queue, transfer_queue,
features,
sync_threadpool: sync::SyncThreadpool::new(), sync_threadpool: sync::SyncThreadpool::new(),
} }
}; };
@ -196,6 +203,12 @@ impl Device {
pub fn phy(&self) -> vk::PhysicalDevice { pub fn phy(&self) -> vk::PhysicalDevice {
self.0.physical.pdev 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 { pub fn graphics_queue(&self) -> &Queue {
&self.0.main_queue &self.0.main_queue
} }
@ -237,6 +250,18 @@ impl Device {
tracing::warn!("finished waiting: unlocking all queues."); tracing::warn!("finished waiting: unlocking all queues.");
Ok(()) 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 { impl AsRef<khr::swapchain::Device> for Device {
@ -290,25 +315,16 @@ impl<T: std::fmt::Debug + vk::Handle + Copy> std::fmt::Debug for DeviceOwnedDebu
} }
impl<T> DeviceOwnedDebugObject<T> { impl<T> DeviceOwnedDebugObject<T> {
pub fn new<S: Into<Cow<'static, str>>>( pub fn new(
device: crate::Device, device: crate::Device,
object: T, object: T,
name: Option<S>, name: Option<Cow<'static, str>>,
) -> ash::prelude::VkResult<Self> ) -> ash::prelude::VkResult<Self>
where where
T: vk::Handle + Copy, T: vk::Handle + Copy,
{ {
let name = name.map(Into::<Cow<_>>::into);
if let Some(name) = name.as_ref() { if let Some(name) = name.as_ref() {
let name = device.debug_name_object(object, name);
std::ffi::CString::new(name.as_bytes()).unwrap_or(c"invalid name".to_owned());
unsafe {
device.debug_utils().set_debug_utils_object_name(
&vk::DebugUtilsObjectNameInfoEXT::default()
.object_handle(object)
.object_name(&name),
)?;
}
} }
Ok(Self { Ok(Self {
@ -328,3 +344,61 @@ impl<T> DeviceOwnedDebugObject<T> {
self.object 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
}
}
)?
};
}

View file

@ -1,11 +1,96 @@
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 super::{Device, Queue};
use ash::{prelude::*, vk}; use ash::{prelude::*, vk};
use vk_mem::Alloc; 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)] #[derive(Debug)]
pub struct Image2D { pub struct Image2D {
device: Device, device: Device,
@ -94,32 +179,29 @@ impl Image2D {
self.format self.format
} }
pub fn view( pub fn device(&self) -> Device {
self: &Arc<Self>, self.device.clone()
device: &Device, }
aspect: vk::ImageAspectFlags,
) -> VkResult<Arc<ImageView2D>> { pub fn view(&self, desc: ImageViewDesc) -> VkResult<ImageView> {
let create_info = vk::ImageViewCreateInfo::default() let create_info = vk::ImageViewCreateInfo::default()
.flags(desc.flags)
.image(self.image()) .image(self.image())
.view_type(vk::ImageViewType::TYPE_2D) .view_type(vk::ImageViewType::TYPE_2D)
.format(self.format) .format(desc.format)
.components(vk::ComponentMapping::default()) .components(desc.components)
.subresource_range( .subresource_range(
vk::ImageSubresourceRange::default() vk::ImageSubresourceRange::default()
.aspect_mask(aspect) .aspect_mask(desc.aspect)
.base_mip_level(0) .base_mip_level(desc.mip_range.0)
.level_count(self.mip_levels) .level_count(desc.mip_range.count())
.base_array_layer(0) .base_array_layer(desc.layer_range.0)
.layer_count(1), .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 { ImageView::construct(self.device.clone(), view, desc.name)
view,
image: self.clone(),
aspect,
}))
} }
pub fn image(&self) -> vk::Image { pub fn image(&self) -> vk::Image {
@ -136,22 +218,13 @@ impl Image2D {
} }
} }
pub struct ImageView2D { define_device_owned_handle! {
view: vk::ImageView, #[derive(Debug)]
aspect: vk::ImageAspectFlags, pub ImageView(vk::ImageView) {} => |this| unsafe {
image: Arc<Image2D>, this.device().dev().destroy_image_view(this.handle(), None);
}
impl Drop for ImageView2D {
fn drop(&mut self) {
unsafe {
self.image.device.dev().destroy_image_view(self.view, None);
}
} }
} }
impl ImageView2D {}
pub struct QueueOwnership { pub struct QueueOwnership {
pub src: u32, pub src: u32,
pub dst: u32, pub dst: u32,

View file

@ -12,6 +12,8 @@ use std::{
collections::{BTreeMap, BTreeSet, HashMap}, collections::{BTreeMap, BTreeSet, HashMap},
ffi::{CStr, CString}, ffi::{CStr, CString},
fmt::Debug, fmt::Debug,
hash::Hash,
hint::black_box,
marker::PhantomData, marker::PhantomData,
ops::Deref, ops::Deref,
sync::{ sync::{
@ -20,7 +22,9 @@ use std::{
}, },
}; };
use bytemuck::Contiguous;
use egui::Color32; use egui::Color32;
use indexmap::IndexMap;
use parking_lot::{Mutex, MutexGuard, RwLock}; use parking_lot::{Mutex, MutexGuard, RwLock};
use ash::{ use ash::{
@ -44,17 +48,51 @@ mod render_graph;
mod sync; mod sync;
mod util; mod util;
use device::{Device, DeviceAndQueues, DeviceQueueFamilies, WeakDevice}; use device::{Device, DeviceAndQueues, DeviceOwned, DeviceQueueFamilies, WeakDevice};
mod texture { mod texture {
use std::{collections::BTreeMap, sync::Arc}; use std::{
collections::{hash_map::Entry, BTreeMap, HashMap},
sync::Arc,
};
use crate::{def_monotonic_id, images::Image2D, Device}; use ash::prelude::VkResult;
use parking_lot::Mutex;
use crate::{
def_monotonic_id,
images::{Image2D, ImageView},
Device,
};
def_monotonic_id!(TextureId); def_monotonic_id!(TextureId);
pub struct Texture {
id: TextureId,
image: Arc<Image2D>,
views: Mutex<HashMap<crate::images::ImageViewDesc, Arc<ImageView>>>,
}
impl Texture {
pub fn image(&self) -> Arc<Image2D> {
self.image.clone()
}
pub fn view(&self, desc: crate::images::ImageViewDesc) -> VkResult<Arc<ImageView>> {
let mut views = self.views.lock();
let view = match views.entry(desc) {
Entry::Occupied(entry) => entry.get().clone(),
Entry::Vacant(entry) => {
let view = Arc::new(self.image.view(entry.key().clone())?);
entry.insert(view).clone()
}
};
Ok(view)
}
}
pub struct TextureManager { pub struct TextureManager {
pub textures: BTreeMap<TextureId, Arc<Image2D>>, pub textures: BTreeMap<TextureId, Arc<Texture>>,
dev: Device, dev: Device,
} }
@ -65,6 +103,25 @@ mod texture {
textures: BTreeMap::new(), textures: BTreeMap::new(),
} }
} }
pub fn insert_image_with_id(&mut self, id: TextureId, image: Arc<Image2D>) {
self.textures.insert(
id,
Arc::new(Texture {
id,
image,
views: Mutex::new(HashMap::new()),
}),
);
}
pub fn remove_texture(&mut self, id: TextureId) -> Option<Arc<Texture>> {
self.textures.remove(&id)
}
pub fn get_texture(&self, id: TextureId) -> Option<Arc<Texture>> {
self.textures.get(&id).cloned()
}
} }
} }
@ -84,6 +141,8 @@ pub enum Error {
NulError(#[from] std::ffi::NulError), NulError(#[from] std::ffi::NulError),
#[error("No Physical Device found.")] #[error("No Physical Device found.")]
NoPhysicalDevice, NoPhysicalDevice,
#[error(transparent)]
Io(#[from] std::io::Error),
} }
type Result<T> = core::result::Result<T, Error>; type Result<T> = core::result::Result<T, Error>;
@ -164,13 +223,16 @@ impl Queue {
} }
} }
trait ExtendsDeviceFeatures2Debug: vk::ExtendsPhysicalDeviceFeatures2 + Debug {} trait ExtendsDeviceFeatures2Debug: vk::ExtendsPhysicalDeviceFeatures2 + Debug + Send + Sync {}
trait ExtendsDeviceProperties2Debug: trait ExtendsDeviceProperties2Debug:
vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone + Send + Sync vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone + Send + Sync
{ {
} }
impl<T: vk::ExtendsPhysicalDeviceFeatures2 + Debug> ExtendsDeviceFeatures2Debug for T {} impl<T: vk::ExtendsPhysicalDeviceFeatures2 + Debug + Send + Sync> ExtendsDeviceFeatures2Debug
for T
{
}
impl<T: vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone + Send + Sync> impl<T: vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone + Send + Sync>
ExtendsDeviceProperties2Debug for T ExtendsDeviceProperties2Debug for T
{ {
@ -178,13 +240,13 @@ impl<T: vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone + Send + Sync>
#[derive(Default, Debug)] #[derive(Default, Debug)]
struct PhysicalDeviceFeatures { struct PhysicalDeviceFeatures {
version: u32, pub version: u32,
physical_features_10: vk::PhysicalDeviceFeatures, pub physical_features_10: vk::PhysicalDeviceFeatures,
physical_features_11: Option<vk::PhysicalDeviceVulkan11Features<'static>>, pub physical_features_11: Option<vk::PhysicalDeviceVulkan11Features<'static>>,
physical_features_12: Option<vk::PhysicalDeviceVulkan12Features<'static>>, pub physical_features_12: Option<vk::PhysicalDeviceVulkan12Features<'static>>,
physical_features_13: Option<vk::PhysicalDeviceVulkan13Features<'static>>, pub physical_features_13: Option<vk::PhysicalDeviceVulkan13Features<'static>>,
extra_features: Vec<Box<dyn ExtendsDeviceFeatures2Debug>>, pub extra_features: Vec<Box<dyn ExtendsDeviceFeatures2Debug>>,
device_extensions: Vec<vk::ExtensionProperties>, pub device_extensions: Vec<vk::ExtensionProperties>,
} }
impl PhysicalDeviceFeatures { impl PhysicalDeviceFeatures {
@ -291,6 +353,16 @@ impl PhysicalDeviceFeatures {
features2 features2
} }
fn supports_extension(&self, e: &vk::ExtensionProperties) -> bool {
self.device_extensions
.iter()
.find(|ext| {
ext.extension_name_as_c_str() == e.extension_name_as_c_str()
&& ext.spec_version >= e.spec_version
})
.is_some()
}
fn compatible_with(&self, device: &Self) -> bool { fn compatible_with(&self, device: &Self) -> bool {
let sort_exts = |a: &vk::ExtensionProperties, b: &vk::ExtensionProperties| { let sort_exts = |a: &vk::ExtensionProperties, b: &vk::ExtensionProperties| {
(a.extension_name_as_c_str().unwrap(), a.spec_version) (a.extension_name_as_c_str().unwrap(), a.spec_version)
@ -1001,9 +1073,37 @@ impl Drop for Surface {
} }
} }
struct SamplerCache {
device: Device,
samplers: HashMap<pipeline::SamplerDesc, pipeline::Sampler>,
}
impl SamplerCache {
pub fn new(device: Device) -> SamplerCache {
Self {
device,
samplers: HashMap::new(),
}
}
pub fn get_sampler(&mut self, desc: pipeline::SamplerDesc) -> VkResult<vk::Sampler> {
use std::collections::hash_map::Entry;
let entry = match self.samplers.entry(desc) {
Entry::Occupied(entry) => entry.get().handle(),
Entry::Vacant(entry) => {
let sampler = pipeline::Sampler::new(self.device.clone(), entry.key())?;
entry.insert(sampler).handle()
}
};
Ok(entry)
}
}
pub struct Vulkan { pub struct Vulkan {
instance: Arc<Instance>, instance: Arc<Instance>,
device: Device, device: Device,
samplers: SamplerCache,
} }
impl Drop for Vulkan { impl Drop for Vulkan {
@ -1069,7 +1169,8 @@ impl Vulkan {
let extensions = Self::get_extensions( let extensions = Self::get_extensions(
&entry, &entry,
&layers, &layers,
&[ash::ext::debug_utils::NAME, ash::ext::layer_settings::NAME], // &[ash::ext::debug_utils::NAME, ash::ext::layer_settings::NAME],
&[ash::ext::debug_utils::NAME],
display_handle, display_handle,
) )
.unwrap(); .unwrap();
@ -1121,6 +1222,10 @@ impl Vulkan {
.features12( .features12(
vk::PhysicalDeviceVulkan12Features::default() vk::PhysicalDeviceVulkan12Features::default()
.shader_int8(true) .shader_int8(true)
.runtime_descriptor_array(true)
.descriptor_binding_partially_bound(true)
.shader_sampled_image_array_non_uniform_indexing(true)
.descriptor_binding_sampled_image_update_after_bind(true)
.storage_buffer8_bit_access(true), .storage_buffer8_bit_access(true),
) )
.features13( .features13(
@ -1180,7 +1285,11 @@ impl Vulkan {
tracing::debug!("pdev: {pdev:?}"); tracing::debug!("pdev: {pdev:?}");
let device = Device::new(instance.clone(), pdev, features)?; let device = Device::new(instance.clone(), pdev, features)?;
Ok(Self { instance, device }) Ok(Self {
instance,
samplers: SamplerCache::new(device.clone()),
device,
})
} }
fn queue_family_supports_presentation( fn queue_family_supports_presentation(
@ -1191,17 +1300,28 @@ impl Vulkan {
) -> bool { ) -> bool {
unsafe { unsafe {
match display_handle { match display_handle {
RawDisplayHandle::Xlib(_xlib_display_handle) => { RawDisplayHandle::Xlib(display) => {
todo!() let surface =
ash::khr::xlib_surface::Instance::new(&instance.entry, &instance.instance);
surface.get_physical_device_xlib_presentation_support(
pdev,
queue_family,
display.display.unwrap().as_ptr() as _,
display.screen as _,
)
//todo!("xlib")
} }
RawDisplayHandle::Xcb(_xcb_display_handle) => todo!(), RawDisplayHandle::Xcb(_xcb_display_handle) => todo!("xcb"),
RawDisplayHandle::Wayland(wayland_display_handle) => { RawDisplayHandle::Wayland(wayland_display_handle) => {
ash::khr::wayland_surface::Instance::new(&instance.entry, &instance.instance) let surface = ash::khr::wayland_surface::Instance::new(
.get_physical_device_wayland_presentation_support( &instance.entry,
pdev, &instance.instance,
queue_family, );
wayland_display_handle.display.cast().as_mut(), surface.get_physical_device_wayland_presentation_support(
) pdev,
queue_family,
wayland_display_handle.display.cast().as_mut(),
)
} }
RawDisplayHandle::Drm(_) => { RawDisplayHandle::Drm(_) => {
todo!() todo!()
@ -1656,9 +1776,258 @@ impl WindowContext {
} }
} }
#[derive(Debug, Default)] #[derive(Debug)]
pub struct EguiState { pub struct EguiState {
pub textures: HashMap<egui::TextureId, texture::TextureId>, textures: HashMap<egui::TextureId, EguiTextureInfo>,
descriptor_pool: pipeline::DescriptorPool,
descriptor_set: vk::DescriptorSet,
descriptor_layout: pipeline::DescriptorSetLayout,
pipeline_layout: pipeline::PipelineLayout,
pipeline: pipeline::Pipeline,
render_state: Option<EguiRenderState>,
}
#[derive(Debug)]
struct EguiRenderState {
vertices: Arc<buffers::Buffer>,
indices: Arc<buffers::Buffer>,
draw_calls: Arc<buffers::Buffer>,
texture_ids: Arc<buffers::Buffer>,
textures_to_free: Vec<texture::TextureId>,
num_draw_calls: usize,
}
#[derive(Debug, Clone, Copy)]
struct EguiTextureInfo {
id: texture::TextureId,
options: egui::epaint::textures::TextureOptions,
}
impl EguiTextureInfo {
fn into_sampler_desc(&self) -> pipeline::SamplerDesc {
let address_mode = match self.options.wrap_mode {
egui::TextureWrapMode::ClampToEdge => vk::SamplerAddressMode::CLAMP_TO_EDGE,
egui::TextureWrapMode::Repeat => vk::SamplerAddressMode::REPEAT,
egui::TextureWrapMode::MirroredRepeat => vk::SamplerAddressMode::MIRRORED_REPEAT,
};
pipeline::SamplerDesc {
min_filter: match self.options.minification {
egui::TextureFilter::Nearest => vk::Filter::NEAREST,
egui::TextureFilter::Linear => vk::Filter::LINEAR,
},
mag_filter: match self.options.magnification {
egui::TextureFilter::Nearest => vk::Filter::NEAREST,
egui::TextureFilter::Linear => vk::Filter::LINEAR,
},
mipmap_mode: match self.options.mipmap_mode {
Some(egui::TextureFilter::Linear) => vk::SamplerMipmapMode::LINEAR,
Some(egui::TextureFilter::Nearest) => vk::SamplerMipmapMode::NEAREST,
None => Default::default(),
},
address_u: address_mode,
address_v: address_mode,
address_w: address_mode,
max_lod: vk::LOD_CLAMP_NONE,
..Default::default()
}
}
}
impl EguiState {
const TEXTURE_BINDING: u32 = 0;
const UNIFORM_BINDING: u32 = 1;
fn new(device: Device) -> Result<Self> {
let descriptor_pool = pipeline::DescriptorPool::new(
device.clone(),
pipeline::DescriptorPoolDesc {
flags: vk::DescriptorPoolCreateFlags::UPDATE_AFTER_BIND,
name: Some("egui-descriptorpool".into()),
sizes: &[
vk::DescriptorPoolSize {
ty: vk::DescriptorType::COMBINED_IMAGE_SAMPLER,
descriptor_count: device
.physical_device()
.properties
.base
.limits
.max_descriptor_set_sampled_images,
},
vk::DescriptorPoolSize {
ty: vk::DescriptorType::STORAGE_BUFFER,
descriptor_count: 1,
},
],
max_sets: 1,
..Default::default()
},
)?;
let descriptor_layout = pipeline::DescriptorSetLayout::new(
device.clone(),
pipeline::DescriptorSetLayoutDesc {
flags: vk::DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL,
name: Some("egui-descriptor-layout".into()),
bindings: &[
pipeline::DescriptorSetLayoutBindingDesc {
binding: Self::TEXTURE_BINDING,
count: device
.physical_device()
.properties
.base
.limits
.max_descriptor_set_sampled_images,
kind: vk::DescriptorType::COMBINED_IMAGE_SAMPLER,
stage: vk::ShaderStageFlags::FRAGMENT,
flags: Some(
vk::DescriptorBindingFlags::PARTIALLY_BOUND
| vk::DescriptorBindingFlags::UPDATE_AFTER_BIND,
),
},
pipeline::DescriptorSetLayoutBindingDesc {
binding: Self::UNIFORM_BINDING,
count: 1,
kind: vk::DescriptorType::STORAGE_BUFFER,
stage: vk::ShaderStageFlags::FRAGMENT,
flags: None,
},
],
},
)?;
let sets = descriptor_pool.allocate(&[pipeline::DescriptorSetAllocDesc {
name: None,
layout: &descriptor_layout,
}])?;
let pipeline_layout = pipeline::PipelineLayout::new(
device.clone(),
pipeline::PipelineLayoutDesc {
descriptor_set_layouts: &[&descriptor_layout],
push_constant_ranges: &[vk::PushConstantRange {
offset: 0,
size: 128,
stage_flags: vk::ShaderStageFlags::VERTEX,
}],
name: Some("egui-pipeline-layout".into()),
},
)?;
let frag_shader = pipeline::ShaderModule::new_from_path(
device.clone(),
"crates/renderer/shaders/egui_frag.spv",
)?;
let vert_shader = pipeline::ShaderModule::new_from_path(
device.clone(),
"crates/renderer/shaders/egui_vert.spv",
)?;
let pipeline = pipeline::Pipeline::new(
device.clone(),
pipeline::PipelineDesc::Graphics(pipeline::GraphicsPipelineDesc {
flags: Default::default(),
name: Some("egui-pipeline".into()),
shader_stages: &[
pipeline::ShaderStageDesc {
flags: vk::PipelineShaderStageCreateFlags::empty(),
module: &frag_shader,
stage: vk::ShaderStageFlags::FRAGMENT,
entry: c"main".into(),
},
pipeline::ShaderStageDesc {
flags: vk::PipelineShaderStageCreateFlags::empty(),
module: &vert_shader,
stage: vk::ShaderStageFlags::VERTEX,
entry: c"main".into(),
},
],
render_pass: None,
layout: &pipeline_layout,
subpass: None,
base_pipeline: None,
vertex_input: Some(pipeline::VertexInputState {
bindings: &[vk::VertexInputBindingDescription {
binding: 0,
stride: 20,
input_rate: vk::VertexInputRate::VERTEX,
}],
attributes: &[
vk::VertexInputAttributeDescription {
location: 0,
binding: 0,
format: vk::Format::R32G32_SFLOAT,
offset: 0,
},
vk::VertexInputAttributeDescription {
location: 1,
binding: 0,
format: vk::Format::R32G32_SFLOAT,
offset: 8,
},
vk::VertexInputAttributeDescription {
location: 2,
binding: 0,
format: vk::Format::R8G8B8A8_UNORM,
offset: 16,
},
],
}),
input_assembly: Some(pipeline::InputAssemblyState {
topology: vk::PrimitiveTopology::TRIANGLE_LIST,
primitive_restart: false,
}),
tessellation: None,
viewport: Some(pipeline::ViewportState {
num_viewports: 1,
num_scissors: 1,
..Default::default()
}),
rasterization: Some(pipeline::RasterizationState {
cull_mode: vk::CullModeFlags::NONE,
..Default::default()
}),
multisample: Some(pipeline::MultisampleState {
..Default::default()
}),
depth_stencil: Some(pipeline::DepthStencilState {
depth: Some(pipeline::DepthState {
write_enable: false,
compare_op: Some(vk::CompareOp::LESS),
bounds: Some(pipeline::DepthBounds { min: 0.0, max: 1.0 }),
}),
..Default::default()
}),
color_blend: Some(pipeline::ColorBlendState {
attachments: &[vk::PipelineColorBlendAttachmentState {
color_write_mask: vk::ColorComponentFlags::RGBA,
blend_enable: 0,
..Default::default()
}],
..Default::default()
}),
rendering: Some(pipeline::RenderingState {
color_formats: &[vk::Format::R8G8B8A8_UNORM],
..Default::default()
}),
dynamic: Some(pipeline::DynamicState {
dynamic_states: &[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR],
..Default::default()
}),
}),
)?;
Ok(Self {
textures: HashMap::new(),
descriptor_pool,
descriptor_layout,
descriptor_set: sets[0],
pipeline,
pipeline_layout,
render_state: None,
})
}
fn lookup_texture(&self, id: egui::epaint::TextureId) -> Option<texture::TextureId> {
self.textures.get(&id).map(|entry| entry.id)
}
} }
pub struct Renderer<W> { pub struct Renderer<W> {
@ -1677,14 +2046,14 @@ impl<W> Renderer<W> {
let vulkan = Vulkan::new("Vidya", &[], &[], Some(display))?; let vulkan = Vulkan::new("Vidya", &[], &[], Some(display))?;
Ok(Self { Ok(Self {
texture_handler: texture::TextureManager::new(vulkan.device.clone()), texture_handler: texture::TextureManager::new(vulkan.device.clone()),
egui_state: EguiState::new(vulkan.device.clone())?,
vulkan, vulkan,
egui_state: Default::default(),
display, display,
window_contexts: HashMap::new(), window_contexts: HashMap::new(),
}) })
} }
pub fn draw_egui(&mut self, ctx: &egui::Context, output: egui::FullOutput) { pub fn draw_egui(&mut self, ctx: &egui::Context, output: egui::FullOutput) -> Result<()> {
let pool = commands::SingleUseCommandPool::new( let pool = commands::SingleUseCommandPool::new(
self.vulkan.device.clone(), self.vulkan.device.clone(),
self.vulkan.device.graphics_queue().clone(), self.vulkan.device.graphics_queue().clone(),
@ -1708,6 +2077,7 @@ impl<W> Renderer<W> {
vk_mem::AllocationCreateFlags::MAPPED vk_mem::AllocationCreateFlags::MAPPED
| vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE | vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE
| vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT, | vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT,
Some(format!("egui-{egui_id:?}-staging-buf").into()),
) )
.expect("staging buffer"); .expect("staging buffer");
{ {
@ -1790,21 +2160,29 @@ impl<W> Renderer<W> {
}], }],
); );
let id = self let id = self.egui_state.lookup_texture(*egui_id).unwrap_or_else(|| {
.egui_state let id = texture::TextureId::new();
.textures
.get(egui_id)
.cloned()
.unwrap_or_else(|| {
let id = texture::TextureId::new();
self.egui_state.textures.insert(*egui_id, id);
id self.egui_state.textures.insert(
}); *egui_id,
EguiTextureInfo {
id,
options: delta.options,
},
);
id
});
if let Some(pos) = delta.pos { if let Some(pos) = delta.pos {
// SAFETY: must exist because image is not whole. // SAFETY: must exist because image is not whole.
let existing_texture = self.texture_handler.textures.get(&id).cloned().unwrap(); let existing_texture = self
.texture_handler
.textures
.get(&id)
.cloned()
.unwrap()
.image();
cmd.image_barrier( cmd.image_barrier(
texture.image(), texture.image(),
@ -1860,7 +2238,8 @@ impl<W> Renderer<W> {
vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
None, None,
); );
self.texture_handler.textures.insert(id, texture.clone()); self.texture_handler
.insert_image_with_id(id, texture.clone());
info!("new texture for egui: {egui_id:?} -> {id:?}"); info!("new texture for egui: {egui_id:?} -> {id:?}");
} }
@ -1868,25 +2247,252 @@ impl<W> Renderer<W> {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let fence = Arc::new(sync::Fence::create(self.vulkan.device.clone()).unwrap()); let draw_data = ctx.tessellate(output.shapes, output.pixels_per_point);
#[repr(C)]
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
struct Vertex {
pos: glam::Vec2,
uv: glam::Vec2,
color: egui::epaint::Color32,
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
struct DrawCall(vk::DrawIndexedIndirectCommand);
unsafe impl bytemuck::Zeroable for DrawCall {}
unsafe impl bytemuck::Pod for DrawCall {}
let mut vertices = Vec::new();
let mut indices = Vec::new();
let mut draw_calls = Vec::new();
let mut textures = IndexMap::new();
let mut textures_indices = Vec::new();
for draw in draw_data {
let egui::epaint::Primitive::Mesh(mesh) = draw.primitive else {
continue;
};
draw_calls.push(DrawCall(vk::DrawIndexedIndirectCommand {
index_count: mesh.indices.len() as u32,
instance_count: 1,
first_index: indices.len() as u32,
vertex_offset: vertices.len() as i32,
first_instance: 0,
}));
vertices.extend(mesh.vertices.iter().map(|v| Vertex {
pos: glam::vec2(v.pos.x, v.pos.y),
uv: glam::vec2(v.uv.x, v.uv.y),
color: v.color,
}));
indices.extend(mesh.indices);
let texture = self
.egui_state
.textures
.get(&mesh.texture_id)
.cloned()
.unwrap();
if !textures.contains_key(&texture.id) {
textures.insert(texture.id, texture);
}
let idx = textures.get_index_of(&texture.id).unwrap();
textures_indices.push(idx as u32);
}
eprintln!("{:?}", self.egui_state.textures);
eprintln!("{draw_calls:?}");
let num_draw_calls = draw_calls.len();
let device = self.vulkan.device.clone();
let (draw_staging, vertices, indices, draw_calls, texture_ids) = {
let vertices_size = vertices.len() * size_of::<Vertex>();
let indices_size = indices.len() * size_of::<u32>();
let draw_calls_size = draw_calls.len() * size_of::<vk::DrawIndexedIndirectCommand>();
let staging_size = vertices_size + indices_size + draw_calls_size;
let mut staging = buffers::Buffer::new(
device.clone(),
staging_size,
vk::BufferUsageFlags::TRANSFER_SRC,
&[device.queue_families().graphics_familty()],
vk_mem::MemoryUsage::AutoPreferHost,
vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE
| vk_mem::AllocationCreateFlags::MAPPED,
Some("egui-draw-staging".into()),
)?;
{
let mut map = staging.map_arc()?;
let (st_vertices, rest) = map.split_at_mut(vertices_size);
let (st_indices, st_drawcalls) = rest.split_at_mut(indices_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));
}
let vertices = buffers::Buffer::new(
device.clone(),
vertices_size,
vk::BufferUsageFlags::VERTEX_BUFFER | vk::BufferUsageFlags::TRANSFER_DST,
&[device.queue_families().graphics_familty()],
vk_mem::MemoryUsage::AutoPreferDevice,
vk_mem::AllocationCreateFlags::empty(),
Some("egui-draw-vertices".into()),
)?;
let indices = buffers::Buffer::new(
device.clone(),
indices_size,
vk::BufferUsageFlags::INDEX_BUFFER | vk::BufferUsageFlags::TRANSFER_DST,
&[device.queue_families().graphics_familty()],
vk_mem::MemoryUsage::AutoPreferDevice,
vk_mem::AllocationCreateFlags::empty(),
Some("egui-draw-indices".into()),
)?;
let draw_calls = buffers::Buffer::new(
device.clone(),
draw_calls_size,
vk::BufferUsageFlags::INDIRECT_BUFFER | vk::BufferUsageFlags::TRANSFER_DST,
&[device.queue_families().graphics_familty()],
vk_mem::MemoryUsage::AutoPreferDevice,
vk_mem::AllocationCreateFlags::empty(),
Some("egui-draw-draw_calls".into()),
)?;
cmd.copy_buffers(
staging.buffer(),
vertices.buffer(),
&[vk::BufferCopy {
src_offset: 0,
dst_offset: 0,
size: vertices_size as u64,
}],
);
cmd.copy_buffers(
staging.buffer(),
indices.buffer(),
&[vk::BufferCopy {
src_offset: vertices_size as u64,
dst_offset: 0,
size: indices_size as u64,
}],
);
cmd.copy_buffers(
staging.buffer(),
draw_calls.buffer(),
&[vk::BufferCopy {
src_offset: (vertices_size + indices_size) as u64,
dst_offset: 0,
size: draw_calls_size as u64,
}],
);
let mut texture_ids = buffers::Buffer::new(
device.clone(),
textures_indices.len() * size_of::<u32>(),
vk::BufferUsageFlags::STORAGE_BUFFER,
&[device.queue_families().graphics_familty()],
vk_mem::MemoryUsage::AutoPreferDevice,
vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE,
Some("egui-draw-texture_ids".into()),
)?;
{
let mut map = texture_ids.map_arc()?;
map.copy_from_slice(bytemuck::cast_slice(&textures_indices));
}
(staging, vertices, indices, draw_calls, texture_ids)
};
let descriptor_infos = textures
.values()
.map(|entry| {
let texture = self.texture_handler.get_texture(entry.id).unwrap();
let info = vk::DescriptorImageInfo {
sampler: self
.vulkan
.samplers
.get_sampler(entry.into_sampler_desc())
.unwrap(),
image_view: texture
.view(images::ImageViewDesc {
kind: vk::ImageViewType::TYPE_2D,
format: texture.image().format(),
aspect: vk::ImageAspectFlags::COLOR,
mip_range: (0..1).into(),
layer_range: (0..1).into(),
..Default::default()
})
.unwrap()
.handle(),
image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
};
info
})
.collect::<Vec<_>>();
let uniform_info = vk::DescriptorBufferInfo {
buffer: texture_ids.buffer(),
offset: 0,
range: texture_ids.len(),
};
let descriptor_writes = descriptor_infos
.iter()
.enumerate()
.map(|(i, info)| {
vk::WriteDescriptorSet::default()
.image_info(core::slice::from_ref(info))
.descriptor_count(1)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.dst_set(self.egui_state.descriptor_set)
.dst_binding(EguiState::TEXTURE_BINDING)
.dst_array_element(i as u32)
})
.chain(core::iter::once({
vk::WriteDescriptorSet::default()
.buffer_info(core::slice::from_ref(&uniform_info))
.descriptor_count(1)
.dst_binding(EguiState::UNIFORM_BINDING)
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
.dst_array_element(0)
.dst_set(self.egui_state.descriptor_set)
}))
.collect::<Vec<_>>();
unsafe {
device.dev().update_descriptor_sets(&descriptor_writes, &[]);
}
let to_remove_tex_ids = output
.textures_delta
.free
.iter()
.filter_map(|id| self.egui_state.textures.get(id).cloned())
.map(|entry| entry.id)
.collect::<Vec<_>>();
self.egui_state.render_state = Some(EguiRenderState {
vertices,
indices,
draw_calls,
num_draw_calls,
texture_ids,
textures_to_free: to_remove_tex_ids,
});
let fence = Arc::new(sync::Fence::create(device.clone()).unwrap());
let future = cmd.submit_async(None, None, fence).unwrap(); let future = cmd.submit_async(None, None, fence).unwrap();
future.block(); future.block();
let draw_data = ctx.tessellate(output.shapes, output.pixels_per_point); black_box((cmd_objects, draw_staging));
// do drawing stuff
let cmd = pool.alloc().unwrap();
// free after drawing // free after drawing
let texture_deltas = &output.textures_delta; Ok(())
texture_deltas
.free
.iter()
.filter_map(|id| self.egui_state.textures.get(id).cloned())
.for_each(|id| {
self.texture_handler.textures.remove(&id);
});
} }
pub fn debug_draw<K, F: FnOnce()>(&mut self, window: &K, pre_present_cb: F) -> Result<()> pub fn debug_draw<K, F: FnOnce()>(&mut self, window: &K, pre_present_cb: F) -> Result<()>
@ -1974,9 +2580,78 @@ impl<W> Renderer<W> {
vk::ImageAspectFlags::COLOR, vk::ImageAspectFlags::COLOR,
vk::PipelineStageFlags2::TRANSFER, vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::TRANSFER_WRITE, vk::AccessFlags2::TRANSFER_WRITE,
vk::PipelineStageFlags2::TRANSFER, vk::PipelineStageFlags2::FRAGMENT_SHADER,
vk::AccessFlags2::empty(), vk::AccessFlags2::SHADER_WRITE,
vk::ImageLayout::TRANSFER_DST_OPTIMAL, vk::ImageLayout::TRANSFER_DST_OPTIMAL,
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
None,
);
let egui_ctx = self.egui_state.render_state.take();
if let Some(ctx) = egui_ctx.as_ref() {
let color_attachment = &vk::RenderingAttachmentInfo::default()
.image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
.image_view(frame.view)
.load_op(vk::AttachmentLoadOp::LOAD)
.store_op(vk::AttachmentStoreOp::STORE);
cmd.begin_rendering(
vk::RenderingInfo::default()
.color_attachments(core::slice::from_ref(color_attachment))
.layer_count(1)
.render_area(vk::Rect2D::default().extent(frame.swapchain.extent)),
);
cmd.set_scissors(&[vk::Rect2D::default()
.offset(vk::Offset2D::default())
.extent(frame.swapchain.extent)]);
cmd.set_viewport(&[vk::Viewport::default()
.x(0.0)
.y(0.0)
.min_depth(0.0)
.max_depth(1.0)
.width(frame.swapchain.extent.width as f32)
.height(frame.swapchain.extent.height as f32)]);
cmd.bind_pipeline(&self.egui_state.pipeline);
cmd.bind_indices(ctx.indices.buffer(), 0, vk::IndexType::UINT32);
cmd.bind_vertices(ctx.vertices.buffer(), 0);
cmd.push_constants(
&self.egui_state.pipeline_layout,
vk::ShaderStageFlags::VERTEX,
0,
bytemuck::cast_slice(
&[
frame.swapchain.extent.width as f32,
frame.swapchain.extent.height as f32,
]
.map(|f| f.to_bits()),
),
);
cmd.bind_descriptor_sets(
&self.egui_state.pipeline_layout,
vk::PipelineBindPoint::GRAPHICS,
&[self.egui_state.descriptor_set],
);
cmd.draw_indexed_indirect(
ctx.draw_calls.buffer(),
0,
ctx.num_draw_calls as u32,
size_of::<vk::DrawIndexedIndirectCommand>() as u32,
);
cmd.end_rendering();
}
cmd.image_barrier(
frame.image,
vk::ImageAspectFlags::COLOR,
vk::PipelineStageFlags2::FRAGMENT_SHADER,
vk::AccessFlags2::SHADER_WRITE,
vk::PipelineStageFlags2::BOTTOM_OF_PIPE,
vk::AccessFlags2::empty(),
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
vk::ImageLayout::PRESENT_SRC_KHR, vk::ImageLayout::PRESENT_SRC_KHR,
None, None,
); );

View file

@ -2,12 +2,15 @@ use std::{borrow::Cow, path::Path, sync::Arc};
use ash::{prelude::*, vk}; use ash::{prelude::*, vk};
use crate::device::{Device, DeviceOwnedDebugObject}; use crate::{
define_device_owned_handle,
device::{Device, DeviceOwnedDebugObject},
};
#[derive(Debug, Default)] #[derive(Debug)]
pub struct ShaderStageDesc<'a> { pub struct ShaderStageDesc<'a> {
pub flags: vk::PipelineShaderStageCreateFlags, pub flags: vk::PipelineShaderStageCreateFlags,
pub module: vk::ShaderModule, pub module: &'a ShaderModule,
pub stage: vk::ShaderStageFlags, pub stage: vk::ShaderStageFlags,
pub entry: Cow<'a, std::ffi::CStr>, pub entry: Cow<'a, std::ffi::CStr>,
// specialization: Option<vk::SpecializationInfo> // specialization: Option<vk::SpecializationInfo>
@ -19,6 +22,7 @@ pub struct DescriptorSetLayoutBindingDesc {
pub count: u32, pub count: u32,
pub kind: vk::DescriptorType, pub kind: vk::DescriptorType,
pub stage: vk::ShaderStageFlags, pub stage: vk::ShaderStageFlags,
pub flags: Option<vk::DescriptorBindingFlags>,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -30,7 +34,7 @@ pub struct DescriptorSetLayoutDesc<'a> {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct PipelineLayoutDesc<'a> { pub struct PipelineLayoutDesc<'a> {
pub descriptor_set_layouts: &'a [Arc<DescriptorSetLayout>], pub descriptor_set_layouts: &'a [&'a DescriptorSetLayout],
pub push_constant_ranges: &'a [vk::PushConstantRange], pub push_constant_ranges: &'a [vk::PushConstantRange],
pub name: Option<Cow<'static, str>>, pub name: Option<Cow<'static, str>>,
} }
@ -117,7 +121,7 @@ impl Default for RasterizationState {
} }
} }
#[derive(Debug, Default)] #[derive(Debug)]
pub struct MultisampleState<'a> { pub struct MultisampleState<'a> {
pub flags: vk::PipelineMultisampleStateCreateFlags, pub flags: vk::PipelineMultisampleStateCreateFlags,
pub sample_shading_enable: bool, pub sample_shading_enable: bool,
@ -128,6 +132,20 @@ pub struct MultisampleState<'a> {
pub alpha_to_one_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)] #[derive(Debug)]
pub struct DepthBounds { pub struct DepthBounds {
pub min: f32, pub min: f32,
@ -164,6 +182,13 @@ pub struct ColorBlendState<'a> {
pub blend_constants: [f32; 4], 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)] #[derive(Debug, Default)]
pub struct DynamicState<'a> { pub struct DynamicState<'a> {
pub flags: vk::PipelineDynamicStateCreateFlags, pub flags: vk::PipelineDynamicStateCreateFlags,
@ -176,7 +201,7 @@ pub struct GraphicsPipelineDesc<'a> {
pub name: Option<Cow<'static, str>>, pub name: Option<Cow<'static, str>>,
pub shader_stages: &'a [ShaderStageDesc<'a>], pub shader_stages: &'a [ShaderStageDesc<'a>],
pub render_pass: Option<vk::RenderPass>, pub render_pass: Option<vk::RenderPass>,
pub layout: Arc<PipelineLayout>, pub layout: &'a PipelineLayout,
pub subpass: Option<u32>, pub subpass: Option<u32>,
pub base_pipeline: Option<Arc<Pipeline>>, pub base_pipeline: Option<Arc<Pipeline>>,
@ -189,63 +214,120 @@ pub struct GraphicsPipelineDesc<'a> {
pub depth_stencil: Option<DepthStencilState>, pub depth_stencil: Option<DepthStencilState>,
pub color_blend: Option<ColorBlendState<'a>>, pub color_blend: Option<ColorBlendState<'a>>,
pub dynamic: Option<DynamicState<'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)] #[derive(Debug)]
pub struct DescriptorSetLayout { pub struct DescriptorSetAllocDesc<'a> {
set_layout: DeviceOwnedDebugObject<vk::DescriptorSetLayout>, pub name: Option<Cow<'static, str>>,
pub layout: &'a DescriptorSetLayout,
} }
impl Drop for DescriptorSetLayout { define_device_owned_handle! {
fn drop(&mut self) { #[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 { unsafe {
self.set_layout self.device()
.dev() .dev()
.dev() .reset_descriptor_pool(self.handle(), vk::DescriptorPoolResetFlags::empty())
.destroy_descriptor_set_layout(self.set_layout.handle(), None);
} }
} }
} }
define_device_owned_handle! {
#[derive(Debug)]
pub DescriptorSetLayout(vk::DescriptorSetLayout) {} => |this| unsafe {
this.device().dev().destroy_descriptor_set_layout(this.handle(), None);
}
}
impl DescriptorSetLayout { impl DescriptorSetLayout {
pub fn new(device: Device, desc: DescriptorSetLayoutDesc) -> VkResult<Self> { pub fn new(device: Device, desc: DescriptorSetLayoutDesc) -> VkResult<Self> {
let bindings = desc let (flags, bindings): (Vec<_>, Vec<_>) = desc
.bindings .bindings
.iter() .iter()
.map(|binding| { .map(|binding| {
vk::DescriptorSetLayoutBinding::default() let flag = binding.flags.unwrap_or_default();
let binding = vk::DescriptorSetLayoutBinding::default()
.binding(binding.binding) .binding(binding.binding)
.descriptor_count(binding.count) .descriptor_count(binding.count)
.descriptor_type(binding.kind) .descriptor_type(binding.kind)
.stage_flags(binding.stage) .stage_flags(binding.stage);
})
.collect::<Vec<_>>();
let info = &vk::DescriptorSetLayoutCreateInfo::default() (flag, binding)
})
.unzip();
let flags =
&mut vk::DescriptorSetLayoutBindingFlagsCreateInfo::default().binding_flags(&flags);
let mut info = vk::DescriptorSetLayoutCreateInfo::default()
.bindings(&bindings) .bindings(&bindings)
.flags(desc.flags); .flags(desc.flags);
let layout = unsafe { device.dev().create_descriptor_set_layout(info, None)? }; 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);
}
Ok(Self { let layout = unsafe { device.dev().create_descriptor_set_layout(&info, None)? };
set_layout: DeviceOwnedDebugObject::new(device, layout, desc.name)?,
}) Self::construct(device, layout, desc.name)
} }
} }
#[derive(Debug)] use crate::device::DeviceOwned;
pub struct PipelineLayout {
pipeline_layout: DeviceOwnedDebugObject<vk::PipelineLayout>,
}
impl Drop for PipelineLayout { define_device_owned_handle! {
fn drop(&mut self) { #[derive(Debug)]
unsafe { pub PipelineLayout(vk::PipelineLayout) {} => |this| unsafe {
self.pipeline_layout this.device().dev().destroy_pipeline_layout(this.handle(), None);
.dev()
.dev()
.destroy_pipeline_layout(self.pipeline_layout.handle(), None);
}
} }
} }
@ -254,22 +336,149 @@ impl PipelineLayout {
let set_layouts = desc let set_layouts = desc
.descriptor_set_layouts .descriptor_set_layouts
.iter() .iter()
.map(|desc| desc.set_layout.handle()) .map(|desc| desc.handle())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let info = &vk::PipelineLayoutCreateInfo::default() let info = &vk::PipelineLayoutCreateInfo::default()
.set_layouts(&set_layouts) .set_layouts(&set_layouts)
.push_constant_ranges(desc.push_constant_ranges); .push_constant_ranges(desc.push_constant_ranges);
let layout = unsafe { device.dev().create_pipeline_layout(info, None)? }; let layout = unsafe { device.dev().create_pipeline_layout(info, None)? };
Ok(Self { Self::construct(device, layout, desc.name)
pipeline_layout: DeviceOwnedDebugObject::new(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)] #[derive(Debug)]
pub struct Pipeline { pub struct Pipeline {
pipeline: DeviceOwnedDebugObject<vk::Pipeline>, pipeline: DeviceOwnedDebugObject<vk::Pipeline>,
bind_point: vk::PipelineBindPoint,
} }
impl Drop for Pipeline { impl Drop for Pipeline {
@ -286,7 +495,7 @@ impl Drop for Pipeline {
impl ShaderStageDesc<'_> { impl ShaderStageDesc<'_> {
fn into_create_info(&self) -> vk::PipelineShaderStageCreateInfo { fn into_create_info(&self) -> vk::PipelineShaderStageCreateInfo {
vk::PipelineShaderStageCreateInfo::default() vk::PipelineShaderStageCreateInfo::default()
.module(self.module) .module(self.module.handle())
.flags(self.flags) .flags(self.flags)
.stage(self.stage) .stage(self.stage)
.name(&self.entry) .name(&self.entry)
@ -296,11 +505,13 @@ impl ShaderStageDesc<'_> {
impl Pipeline { impl Pipeline {
pub fn new(device: Device, desc: PipelineDesc) -> VkResult<Self> { pub fn new(device: Device, desc: PipelineDesc) -> VkResult<Self> {
let name: Option<Cow<'static, str>>; let name: Option<Cow<'static, str>>;
let bind_point: vk::PipelineBindPoint;
let result = match desc { let result = match desc {
PipelineDesc::Compute(desc) => { PipelineDesc::Compute(desc) => {
name = desc.name; name = desc.name;
bind_point = vk::PipelineBindPoint::COMPUTE;
let info = &vk::ComputePipelineCreateInfo::default() let info = &vk::ComputePipelineCreateInfo::default()
.layout(desc.layout.pipeline_layout.handle()) .layout(desc.layout.handle())
.stage(desc.shader_stage.into_create_info()); .stage(desc.shader_stage.into_create_info());
unsafe { unsafe {
@ -313,6 +524,7 @@ impl Pipeline {
} }
PipelineDesc::Graphics(desc) => { PipelineDesc::Graphics(desc) => {
name = desc.name; name = desc.name;
bind_point = vk::PipelineBindPoint::GRAPHICS;
let stages = desc let stages = desc
.shader_stages .shader_stages
@ -426,6 +638,15 @@ impl Pipeline {
info 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 { fn option_to_ptr<T>(option: &Option<T>) -> *const T {
option option
.as_ref() .as_ref()
@ -433,7 +654,7 @@ impl Pipeline {
.unwrap_or(core::ptr::null()) .unwrap_or(core::ptr::null())
} }
let info = &vk::GraphicsPipelineCreateInfo { let mut info = vk::GraphicsPipelineCreateInfo {
flags: desc.flags, flags: desc.flags,
stage_count: stages.len() as u32, stage_count: stages.len() as u32,
p_stages: stages.as_ptr(), p_stages: stages.as_ptr(),
@ -446,7 +667,7 @@ impl Pipeline {
p_depth_stencil_state: option_to_ptr(&depth_stencil), p_depth_stencil_state: option_to_ptr(&depth_stencil),
p_color_blend_state: option_to_ptr(&color_blend), p_color_blend_state: option_to_ptr(&color_blend),
p_dynamic_state: option_to_ptr(&dynamic), p_dynamic_state: option_to_ptr(&dynamic),
layout: desc.layout.pipeline_layout.handle(), layout: desc.layout.handle(),
render_pass: desc.render_pass.unwrap_or(vk::RenderPass::null()), render_pass: desc.render_pass.unwrap_or(vk::RenderPass::null()),
subpass: desc.subpass.unwrap_or(0), subpass: desc.subpass.unwrap_or(0),
base_pipeline_handle: desc base_pipeline_handle: desc
@ -456,10 +677,14 @@ impl Pipeline {
..Default::default() ..Default::default()
}; };
if let Some(rendering) = rendering.as_mut() {
info = info.push_next(rendering)
}
unsafe { unsafe {
device.dev().create_graphics_pipelines( device.dev().create_graphics_pipelines(
vk::PipelineCache::null(), vk::PipelineCache::null(),
core::slice::from_ref(info), core::slice::from_ref(&info),
None, None,
) )
} }
@ -481,6 +706,14 @@ impl Pipeline {
Ok(Self { Ok(Self {
pipeline: DeviceOwnedDebugObject::new(device, pipeline, name)?, 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
}
} }

View file

@ -1,5 +1,6 @@
use std::hash::Hash; use std::hash::Hash;
use crate::util::hash_f32;
use ash::vk; use ash::vk;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -7,18 +8,6 @@ pub struct Rgba(pub [f32; 4]);
impl std::hash::Hash for Rgba { impl std::hash::Hash for Rgba {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { 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)); self.0.map(|f| hash_f32(state, f));
} }
} }

View file

@ -19,6 +19,10 @@ macro_rules! def_monotonic_id {
.expect(&format!("integer overwarp for {}", stringify!($ty))), .expect(&format!("integer overwarp for {}", stringify!($ty))),
) )
} }
pub fn as_u32(&self) -> u32 {
self.0.get()
}
} }
}; };
} }
@ -298,3 +302,24 @@ impl Rect2D {
] ]
} }
} }
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),
}
}