render egui to the screen (finally)
This commit is contained in:
parent
81f1ee1f96
commit
e8bcaae2a7
|
@ -11,7 +11,7 @@ members = [
|
|||
anyhow = "1.0.89"
|
||||
ash = "0.38.0"
|
||||
ash-window = "0.13.0"
|
||||
glam = "0.29.0"
|
||||
glam = {version = "0.29.0", features = ["bytemuck"]}
|
||||
thiserror = "2.0"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
|
|
|
@ -66,8 +66,10 @@ impl WinitState {
|
|||
window.demo_app.ui(&window.egui_platform.context());
|
||||
let output = window.egui_platform.end_pass(Some(&window.window));
|
||||
|
||||
self.renderer
|
||||
.draw_egui(&window.egui_platform.context(), output);
|
||||
let egui_state = self
|
||||
.renderer
|
||||
.draw_egui(&window.egui_platform.context(), output)
|
||||
.unwrap();
|
||||
|
||||
// rendering
|
||||
self.renderer
|
||||
|
|
|
@ -11,7 +11,7 @@ dyn-clone = "1"
|
|||
anyhow = "1.0.89"
|
||||
ash = "0.38.0"
|
||||
ash-window = "0.13.0"
|
||||
glam = "0.29.0"
|
||||
glam = {workspace = true}
|
||||
thiserror = {workspace = true}
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
|
@ -23,5 +23,7 @@ smol.workspace = true
|
|||
tracing-test = "0.2.5"
|
||||
|
||||
raw-window-handle = { workspace = true }
|
||||
egui = { workspace = true }
|
||||
egui = { workspace = true , features = ["bytemuck"]}
|
||||
egui_winit_platform = { workspace = true }
|
||||
bytemuck = { version = "1.21.0", features = ["derive"] }
|
||||
indexmap = "2.7.0"
|
||||
|
|
|
@ -10,6 +10,7 @@ struct VertexIn {
|
|||
struct VertexOut {
|
||||
[[vk::layout(0)]] float4 color;
|
||||
[[vk::layout(1)]] float2 uv;
|
||||
[[vk::layout(2), flat]] uint draw_id;
|
||||
float4 position : SV_Position;
|
||||
}
|
||||
|
||||
|
@ -21,7 +22,7 @@ struct PushConstant {
|
|||
ConstantBuffer<PushConstant> push_constant;
|
||||
|
||||
[shader("vertex")]
|
||||
VertexOut vertex(VertexIn vertex) {
|
||||
VertexOut vertex(VertexIn vertex, uint draw_id : SV_DrawIndex) {
|
||||
VertexOut output;
|
||||
|
||||
output.position = float4(
|
||||
|
@ -32,17 +33,23 @@ VertexOut vertex(VertexIn vertex) {
|
|||
);
|
||||
output.color = vertex.color;
|
||||
output.uv = vertex.uv;
|
||||
output.draw_id = draw_id;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
[[vk::binding(0)]]
|
||||
Sampler2D texture;
|
||||
Sampler2D texture[];
|
||||
|
||||
|
||||
[[vk::binding(1)]]
|
||||
StructuredBuffer<uint> texture_ids;
|
||||
|
||||
[shader("fragment")]
|
||||
Fragment fragment(VertexOut input) {
|
||||
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;
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -6,23 +6,16 @@ use std::{
|
|||
use ash::{prelude::VkResult, vk};
|
||||
use vk_mem::Alloc;
|
||||
|
||||
use crate::Device;
|
||||
use crate::{define_device_owned_handle, device::DeviceOwned, Device};
|
||||
|
||||
pub struct Buffer {
|
||||
device: Device,
|
||||
buffer: vk::Buffer,
|
||||
allocation: vk_mem::Allocation,
|
||||
usage: vk::BufferUsageFlags,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
impl Drop for Buffer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.device
|
||||
.alloc()
|
||||
.destroy_buffer(self.buffer, &mut self.allocation);
|
||||
}
|
||||
define_device_owned_handle! {
|
||||
#[derive(Debug)]
|
||||
pub Buffer(vk::Buffer) {
|
||||
alloc: vk_mem::Allocation,
|
||||
usage: vk::BufferUsageFlags,
|
||||
size: u64,
|
||||
} => |this| unsafe {
|
||||
this.device().clone().alloc().destroy_buffer(this.handle(), &mut this.alloc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,6 +27,7 @@ impl Buffer {
|
|||
queue_families: &[u32],
|
||||
memory_usage: vk_mem::MemoryUsage,
|
||||
alloc_flags: vk_mem::AllocationCreateFlags,
|
||||
name: Option<std::borrow::Cow<'static, str>>,
|
||||
) -> VkResult<Arc<Self>> {
|
||||
let sharing_mode = if queue_families.len() > 1 {
|
||||
vk::SharingMode::CONCURRENT
|
||||
|
@ -56,20 +50,23 @@ impl Buffer {
|
|||
)?
|
||||
};
|
||||
|
||||
let buffer = Self {
|
||||
Ok(Arc::new(Self::construct(
|
||||
device,
|
||||
buffer,
|
||||
name,
|
||||
allocation,
|
||||
usage,
|
||||
size: size as u64,
|
||||
};
|
||||
size as u64,
|
||||
)?))
|
||||
}
|
||||
|
||||
Ok(Arc::new(buffer))
|
||||
pub fn map_arc(self: &mut Arc<Self>) -> VkResult<MappedBuffer<'_>> {
|
||||
Arc::get_mut(self).map(Self::map).unwrap()
|
||||
}
|
||||
|
||||
pub fn map(&mut self) -> VkResult<MappedBuffer<'_>> {
|
||||
let bytes = unsafe {
|
||||
let data = self.device.alloc().map_memory(&mut self.allocation)?;
|
||||
let data = self.inner.dev().alloc().map_memory(&mut self.alloc)?;
|
||||
let slice = core::slice::from_raw_parts_mut(data, self.size as usize);
|
||||
|
||||
slice
|
||||
|
@ -78,7 +75,10 @@ impl Buffer {
|
|||
Ok(MappedBuffer { inner: self, bytes })
|
||||
}
|
||||
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) {
|
||||
unsafe {
|
||||
self.inner
|
||||
.device
|
||||
.inner
|
||||
.dev()
|
||||
.alloc()
|
||||
.unmap_memory(&mut self.inner.allocation);
|
||||
.unmap_memory(&mut self.inner.alloc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ use std::{future::Future, marker::PhantomData, sync::Arc};
|
|||
|
||||
use crate::{
|
||||
buffers::Buffer,
|
||||
device::DeviceOwned,
|
||||
images::{Image2D, QueueOwnership},
|
||||
pipeline::{Pipeline, PipelineLayout},
|
||||
sync::{self, FenceFuture},
|
||||
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(
|
||||
&self,
|
||||
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) {
|
||||
unsafe {
|
||||
|
|
|
@ -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::{
|
||||
khr,
|
||||
|
@ -78,6 +83,7 @@ pub struct DeviceInner {
|
|||
transfer_queue: Queue,
|
||||
present_queue: Queue,
|
||||
sync_threadpool: sync::SyncThreadpool,
|
||||
features: crate::PhysicalDeviceFeatures,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for DeviceInner {
|
||||
|
@ -166,6 +172,7 @@ impl Device {
|
|||
present_queue,
|
||||
compute_queue,
|
||||
transfer_queue,
|
||||
features,
|
||||
sync_threadpool: sync::SyncThreadpool::new(),
|
||||
}
|
||||
};
|
||||
|
@ -196,6 +203,12 @@ impl Device {
|
|||
pub fn phy(&self) -> vk::PhysicalDevice {
|
||||
self.0.physical.pdev
|
||||
}
|
||||
pub fn features(&self) -> &crate::PhysicalDeviceFeatures {
|
||||
&self.0.features
|
||||
}
|
||||
pub fn physical_device(&self) -> &PhysicalDevice {
|
||||
&self.0.physical
|
||||
}
|
||||
pub fn graphics_queue(&self) -> &Queue {
|
||||
&self.0.main_queue
|
||||
}
|
||||
|
@ -237,6 +250,18 @@ impl Device {
|
|||
tracing::warn!("finished waiting: unlocking all queues.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn debug_name_object<T: vk::Handle>(&self, handle: T, name: &str) -> VkResult<()> {
|
||||
let name = std::ffi::CString::new(name.as_bytes()).unwrap_or(c"invalid name".to_owned());
|
||||
unsafe {
|
||||
self.debug_utils().set_debug_utils_object_name(
|
||||
&vk::DebugUtilsObjectNameInfoEXT::default()
|
||||
.object_handle(handle)
|
||||
.object_name(&name),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<khr::swapchain::Device> for Device {
|
||||
|
@ -290,25 +315,16 @@ impl<T: std::fmt::Debug + vk::Handle + Copy> std::fmt::Debug for DeviceOwnedDebu
|
|||
}
|
||||
|
||||
impl<T> DeviceOwnedDebugObject<T> {
|
||||
pub fn new<S: Into<Cow<'static, str>>>(
|
||||
pub fn new(
|
||||
device: crate::Device,
|
||||
object: T,
|
||||
name: Option<S>,
|
||||
name: Option<Cow<'static, str>>,
|
||||
) -> ash::prelude::VkResult<Self>
|
||||
where
|
||||
T: vk::Handle + Copy,
|
||||
{
|
||||
let name = name.map(Into::<Cow<_>>::into);
|
||||
if let Some(name) = name.as_ref() {
|
||||
let 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),
|
||||
)?;
|
||||
}
|
||||
device.debug_name_object(object, name);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
|
@ -328,3 +344,61 @@ impl<T> DeviceOwnedDebugObject<T> {
|
|||
self.object
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DeviceOwned<T> {
|
||||
fn device(&self) -> &Device;
|
||||
fn handle(&self) -> T;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! define_device_owned_handle {
|
||||
($(#[$attr:meta])*
|
||||
$ty_vis:vis $ty:ident($handle:ty) {
|
||||
$($field_vis:vis $field_name:ident : $field_ty:ty),*
|
||||
$(,)?
|
||||
} $(=> |$this:ident| $dtor:stmt)?) => {
|
||||
$(#[$attr])*
|
||||
$ty_vis struct $ty {
|
||||
inner: crate::device::DeviceOwnedDebugObject<$handle>,
|
||||
$(
|
||||
$field_vis $field_name: $field_ty,
|
||||
)*
|
||||
}
|
||||
|
||||
impl crate::device::DeviceOwned<$handle> for $ty {
|
||||
fn device(&self) -> &Device {
|
||||
self.inner.dev()
|
||||
}
|
||||
fn handle(&self) -> $handle {
|
||||
self.inner.handle()
|
||||
}
|
||||
}
|
||||
|
||||
impl $ty {
|
||||
fn construct(
|
||||
device: crate::device::Device,
|
||||
handle: $handle,
|
||||
name: Option<::std::borrow::Cow<'static, str>>,
|
||||
$($field_name: $field_ty,)*
|
||||
) -> ::ash::prelude::VkResult<Self> {
|
||||
Ok(Self {
|
||||
inner: crate::device::DeviceOwnedDebugObject::new(
|
||||
device,
|
||||
handle,
|
||||
name,
|
||||
)?,
|
||||
$($field_name,)*
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl Drop for $ty {
|
||||
fn drop(&mut self) {
|
||||
let mut $this = self;
|
||||
$dtor
|
||||
}
|
||||
}
|
||||
)?
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,11 +1,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 ash::{prelude::*, vk};
|
||||
use vk_mem::Alloc;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ImageViewDesc {
|
||||
pub flags: vk::ImageViewCreateFlags,
|
||||
pub name: Option<Cow<'static, str>>,
|
||||
pub kind: vk::ImageViewType,
|
||||
pub format: vk::Format,
|
||||
pub components: vk::ComponentMapping,
|
||||
pub aspect: vk::ImageAspectFlags,
|
||||
pub mip_range: MipRange,
|
||||
pub layer_range: MipRange,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct MipRange(u32, u32);
|
||||
|
||||
impl Default for MipRange {
|
||||
fn default() -> Self {
|
||||
Self(0, vk::REMAINING_ARRAY_LAYERS)
|
||||
}
|
||||
}
|
||||
|
||||
impl MipRange {
|
||||
fn count(&self) -> u32 {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: core::ops::RangeBounds<u32>> From<R> for MipRange {
|
||||
fn from(value: R) -> Self {
|
||||
let start = match value.start_bound() {
|
||||
std::ops::Bound::Included(v) => *v,
|
||||
std::ops::Bound::Excluded(v) => *v + 1,
|
||||
std::ops::Bound::Unbounded => 0,
|
||||
};
|
||||
let count = match value.end_bound() {
|
||||
std::ops::Bound::Included(v) => *v + 1 - start,
|
||||
std::ops::Bound::Excluded(v) => *v - start,
|
||||
std::ops::Bound::Unbounded => vk::REMAINING_MIP_LEVELS,
|
||||
};
|
||||
|
||||
Self(start, count)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for ImageViewDesc {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.flags.hash(state);
|
||||
self.kind.hash(state);
|
||||
self.format.hash(state);
|
||||
(
|
||||
self.components.r,
|
||||
self.components.g,
|
||||
self.components.b,
|
||||
self.components.a,
|
||||
)
|
||||
.hash(state);
|
||||
self.aspect.hash(state);
|
||||
self.layer_range.hash(state);
|
||||
self.mip_range.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ImageViewDesc {}
|
||||
impl PartialEq for ImageViewDesc {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.flags == other.flags
|
||||
&& self.kind == other.kind
|
||||
&& self.format == other.format
|
||||
&& (
|
||||
self.components.r,
|
||||
self.components.g,
|
||||
self.components.b,
|
||||
self.components.a,
|
||||
) == (
|
||||
other.components.r,
|
||||
other.components.g,
|
||||
other.components.b,
|
||||
other.components.a,
|
||||
)
|
||||
&& self.aspect == other.aspect
|
||||
&& self.mip_range == other.mip_range
|
||||
&& self.layer_range == other.layer_range
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Image2D {
|
||||
device: Device,
|
||||
|
@ -94,32 +179,29 @@ impl Image2D {
|
|||
self.format
|
||||
}
|
||||
|
||||
pub fn view(
|
||||
self: &Arc<Self>,
|
||||
device: &Device,
|
||||
aspect: vk::ImageAspectFlags,
|
||||
) -> VkResult<Arc<ImageView2D>> {
|
||||
pub fn device(&self) -> Device {
|
||||
self.device.clone()
|
||||
}
|
||||
|
||||
pub fn view(&self, desc: ImageViewDesc) -> VkResult<ImageView> {
|
||||
let create_info = vk::ImageViewCreateInfo::default()
|
||||
.flags(desc.flags)
|
||||
.image(self.image())
|
||||
.view_type(vk::ImageViewType::TYPE_2D)
|
||||
.format(self.format)
|
||||
.components(vk::ComponentMapping::default())
|
||||
.format(desc.format)
|
||||
.components(desc.components)
|
||||
.subresource_range(
|
||||
vk::ImageSubresourceRange::default()
|
||||
.aspect_mask(aspect)
|
||||
.base_mip_level(0)
|
||||
.level_count(self.mip_levels)
|
||||
.base_array_layer(0)
|
||||
.layer_count(1),
|
||||
.aspect_mask(desc.aspect)
|
||||
.base_mip_level(desc.mip_range.0)
|
||||
.level_count(desc.mip_range.count())
|
||||
.base_array_layer(desc.layer_range.0)
|
||||
.layer_count(desc.layer_range.count()),
|
||||
);
|
||||
|
||||
let view = unsafe { device.dev().create_image_view(&create_info, None)? };
|
||||
let view = unsafe { self.device.dev().create_image_view(&create_info, None)? };
|
||||
|
||||
Ok(Arc::new(ImageView2D {
|
||||
view,
|
||||
image: self.clone(),
|
||||
aspect,
|
||||
}))
|
||||
ImageView::construct(self.device.clone(), view, desc.name)
|
||||
}
|
||||
|
||||
pub fn image(&self) -> vk::Image {
|
||||
|
@ -136,22 +218,13 @@ impl Image2D {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ImageView2D {
|
||||
view: vk::ImageView,
|
||||
aspect: vk::ImageAspectFlags,
|
||||
image: Arc<Image2D>,
|
||||
}
|
||||
|
||||
impl Drop for ImageView2D {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.image.device.dev().destroy_image_view(self.view, None);
|
||||
}
|
||||
define_device_owned_handle! {
|
||||
#[derive(Debug)]
|
||||
pub ImageView(vk::ImageView) {} => |this| unsafe {
|
||||
this.device().dev().destroy_image_view(this.handle(), None);
|
||||
}
|
||||
}
|
||||
|
||||
impl ImageView2D {}
|
||||
|
||||
pub struct QueueOwnership {
|
||||
pub src: u32,
|
||||
pub dst: u32,
|
||||
|
|
|
@ -12,6 +12,8 @@ use std::{
|
|||
collections::{BTreeMap, BTreeSet, HashMap},
|
||||
ffi::{CStr, CString},
|
||||
fmt::Debug,
|
||||
hash::Hash,
|
||||
hint::black_box,
|
||||
marker::PhantomData,
|
||||
ops::Deref,
|
||||
sync::{
|
||||
|
@ -20,7 +22,9 @@ use std::{
|
|||
},
|
||||
};
|
||||
|
||||
use bytemuck::Contiguous;
|
||||
use egui::Color32;
|
||||
use indexmap::IndexMap;
|
||||
use parking_lot::{Mutex, MutexGuard, RwLock};
|
||||
|
||||
use ash::{
|
||||
|
@ -44,17 +48,51 @@ mod render_graph;
|
|||
mod sync;
|
||||
mod util;
|
||||
|
||||
use device::{Device, DeviceAndQueues, DeviceQueueFamilies, WeakDevice};
|
||||
use device::{Device, DeviceAndQueues, DeviceOwned, DeviceQueueFamilies, WeakDevice};
|
||||
|
||||
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);
|
||||
|
||||
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 textures: BTreeMap<TextureId, Arc<Image2D>>,
|
||||
pub textures: BTreeMap<TextureId, Arc<Texture>>,
|
||||
dev: Device,
|
||||
}
|
||||
|
||||
|
@ -65,6 +103,25 @@ mod texture {
|
|||
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),
|
||||
#[error("No Physical Device found.")]
|
||||
NoPhysicalDevice,
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::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:
|
||||
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>
|
||||
ExtendsDeviceProperties2Debug for T
|
||||
{
|
||||
|
@ -178,13 +240,13 @@ impl<T: vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone + Send + Sync>
|
|||
|
||||
#[derive(Default, Debug)]
|
||||
struct PhysicalDeviceFeatures {
|
||||
version: u32,
|
||||
physical_features_10: vk::PhysicalDeviceFeatures,
|
||||
physical_features_11: Option<vk::PhysicalDeviceVulkan11Features<'static>>,
|
||||
physical_features_12: Option<vk::PhysicalDeviceVulkan12Features<'static>>,
|
||||
physical_features_13: Option<vk::PhysicalDeviceVulkan13Features<'static>>,
|
||||
extra_features: Vec<Box<dyn ExtendsDeviceFeatures2Debug>>,
|
||||
device_extensions: Vec<vk::ExtensionProperties>,
|
||||
pub version: u32,
|
||||
pub physical_features_10: vk::PhysicalDeviceFeatures,
|
||||
pub physical_features_11: Option<vk::PhysicalDeviceVulkan11Features<'static>>,
|
||||
pub physical_features_12: Option<vk::PhysicalDeviceVulkan12Features<'static>>,
|
||||
pub physical_features_13: Option<vk::PhysicalDeviceVulkan13Features<'static>>,
|
||||
pub extra_features: Vec<Box<dyn ExtendsDeviceFeatures2Debug>>,
|
||||
pub device_extensions: Vec<vk::ExtensionProperties>,
|
||||
}
|
||||
|
||||
impl PhysicalDeviceFeatures {
|
||||
|
@ -291,6 +353,16 @@ impl PhysicalDeviceFeatures {
|
|||
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 {
|
||||
let sort_exts = |a: &vk::ExtensionProperties, b: &vk::ExtensionProperties| {
|
||||
(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 {
|
||||
instance: Arc<Instance>,
|
||||
device: Device,
|
||||
samplers: SamplerCache,
|
||||
}
|
||||
|
||||
impl Drop for Vulkan {
|
||||
|
@ -1069,7 +1169,8 @@ impl Vulkan {
|
|||
let extensions = Self::get_extensions(
|
||||
&entry,
|
||||
&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,
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -1121,6 +1222,10 @@ impl Vulkan {
|
|||
.features12(
|
||||
vk::PhysicalDeviceVulkan12Features::default()
|
||||
.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),
|
||||
)
|
||||
.features13(
|
||||
|
@ -1180,7 +1285,11 @@ impl Vulkan {
|
|||
tracing::debug!("pdev: {pdev:?}");
|
||||
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(
|
||||
|
@ -1191,17 +1300,28 @@ impl Vulkan {
|
|||
) -> bool {
|
||||
unsafe {
|
||||
match display_handle {
|
||||
RawDisplayHandle::Xlib(_xlib_display_handle) => {
|
||||
todo!()
|
||||
RawDisplayHandle::Xlib(display) => {
|
||||
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) => {
|
||||
ash::khr::wayland_surface::Instance::new(&instance.entry, &instance.instance)
|
||||
.get_physical_device_wayland_presentation_support(
|
||||
pdev,
|
||||
queue_family,
|
||||
wayland_display_handle.display.cast().as_mut(),
|
||||
)
|
||||
let surface = ash::khr::wayland_surface::Instance::new(
|
||||
&instance.entry,
|
||||
&instance.instance,
|
||||
);
|
||||
surface.get_physical_device_wayland_presentation_support(
|
||||
pdev,
|
||||
queue_family,
|
||||
wayland_display_handle.display.cast().as_mut(),
|
||||
)
|
||||
}
|
||||
RawDisplayHandle::Drm(_) => {
|
||||
todo!()
|
||||
|
@ -1656,9 +1776,258 @@ impl WindowContext {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug)]
|
||||
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> {
|
||||
|
@ -1677,14 +2046,14 @@ impl<W> Renderer<W> {
|
|||
let vulkan = Vulkan::new("Vidya", &[], &[], Some(display))?;
|
||||
Ok(Self {
|
||||
texture_handler: texture::TextureManager::new(vulkan.device.clone()),
|
||||
egui_state: EguiState::new(vulkan.device.clone())?,
|
||||
vulkan,
|
||||
egui_state: Default::default(),
|
||||
display,
|
||||
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(
|
||||
self.vulkan.device.clone(),
|
||||
self.vulkan.device.graphics_queue().clone(),
|
||||
|
@ -1708,6 +2077,7 @@ impl<W> Renderer<W> {
|
|||
vk_mem::AllocationCreateFlags::MAPPED
|
||||
| vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE
|
||||
| vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT,
|
||||
Some(format!("egui-{egui_id:?}-staging-buf").into()),
|
||||
)
|
||||
.expect("staging buffer");
|
||||
{
|
||||
|
@ -1790,21 +2160,29 @@ impl<W> Renderer<W> {
|
|||
}],
|
||||
);
|
||||
|
||||
let id = self
|
||||
.egui_state
|
||||
.textures
|
||||
.get(egui_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| {
|
||||
let id = texture::TextureId::new();
|
||||
self.egui_state.textures.insert(*egui_id, id);
|
||||
let id = self.egui_state.lookup_texture(*egui_id).unwrap_or_else(|| {
|
||||
let id = texture::TextureId::new();
|
||||
|
||||
id
|
||||
});
|
||||
self.egui_state.textures.insert(
|
||||
*egui_id,
|
||||
EguiTextureInfo {
|
||||
id,
|
||||
options: delta.options,
|
||||
},
|
||||
);
|
||||
|
||||
id
|
||||
});
|
||||
|
||||
if let Some(pos) = delta.pos {
|
||||
// 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(
|
||||
texture.image(),
|
||||
|
@ -1860,7 +2238,8 @@ impl<W> Renderer<W> {
|
|||
vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
|
||||
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:?}");
|
||||
}
|
||||
|
||||
|
@ -1868,25 +2247,252 @@ impl<W> Renderer<W> {
|
|||
})
|
||||
.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();
|
||||
|
||||
future.block();
|
||||
|
||||
let draw_data = ctx.tessellate(output.shapes, output.pixels_per_point);
|
||||
|
||||
// do drawing stuff
|
||||
let cmd = pool.alloc().unwrap();
|
||||
|
||||
black_box((cmd_objects, draw_staging));
|
||||
// free after drawing
|
||||
let texture_deltas = &output.textures_delta;
|
||||
texture_deltas
|
||||
.free
|
||||
.iter()
|
||||
.filter_map(|id| self.egui_state.textures.get(id).cloned())
|
||||
.for_each(|id| {
|
||||
self.texture_handler.textures.remove(&id);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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::PipelineStageFlags2::TRANSFER,
|
||||
vk::AccessFlags2::TRANSFER_WRITE,
|
||||
vk::PipelineStageFlags2::TRANSFER,
|
||||
vk::AccessFlags2::empty(),
|
||||
vk::PipelineStageFlags2::FRAGMENT_SHADER,
|
||||
vk::AccessFlags2::SHADER_WRITE,
|
||||
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,
|
||||
None,
|
||||
);
|
||||
|
|
|
@ -2,12 +2,15 @@ use std::{borrow::Cow, path::Path, sync::Arc};
|
|||
|
||||
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 flags: vk::PipelineShaderStageCreateFlags,
|
||||
pub module: vk::ShaderModule,
|
||||
pub module: &'a ShaderModule,
|
||||
pub stage: vk::ShaderStageFlags,
|
||||
pub entry: Cow<'a, std::ffi::CStr>,
|
||||
// specialization: Option<vk::SpecializationInfo>
|
||||
|
@ -19,6 +22,7 @@ pub struct DescriptorSetLayoutBindingDesc {
|
|||
pub count: u32,
|
||||
pub kind: vk::DescriptorType,
|
||||
pub stage: vk::ShaderStageFlags,
|
||||
pub flags: Option<vk::DescriptorBindingFlags>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -30,7 +34,7 @@ pub struct DescriptorSetLayoutDesc<'a> {
|
|||
|
||||
#[derive(Debug, Default)]
|
||||
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 name: Option<Cow<'static, str>>,
|
||||
}
|
||||
|
@ -117,7 +121,7 @@ impl Default for RasterizationState {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug)]
|
||||
pub struct MultisampleState<'a> {
|
||||
pub flags: vk::PipelineMultisampleStateCreateFlags,
|
||||
pub sample_shading_enable: bool,
|
||||
|
@ -128,6 +132,20 @@ pub struct MultisampleState<'a> {
|
|||
pub alpha_to_one_enable: bool,
|
||||
}
|
||||
|
||||
impl<'a> Default for MultisampleState<'a> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
flags: Default::default(),
|
||||
sample_shading_enable: Default::default(),
|
||||
rasterization_samples: vk::SampleCountFlags::TYPE_1,
|
||||
min_sample_shading: 1.0,
|
||||
sample_mask: Default::default(),
|
||||
alpha_to_coverage_enable: Default::default(),
|
||||
alpha_to_one_enable: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DepthBounds {
|
||||
pub min: f32,
|
||||
|
@ -164,6 +182,13 @@ pub struct ColorBlendState<'a> {
|
|||
pub blend_constants: [f32; 4],
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RenderingState<'a> {
|
||||
pub color_formats: &'a [vk::Format],
|
||||
pub depth_format: Option<vk::Format>,
|
||||
pub stencil_format: Option<vk::Format>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DynamicState<'a> {
|
||||
pub flags: vk::PipelineDynamicStateCreateFlags,
|
||||
|
@ -176,7 +201,7 @@ pub struct GraphicsPipelineDesc<'a> {
|
|||
pub name: Option<Cow<'static, str>>,
|
||||
pub shader_stages: &'a [ShaderStageDesc<'a>],
|
||||
pub render_pass: Option<vk::RenderPass>,
|
||||
pub layout: Arc<PipelineLayout>,
|
||||
pub layout: &'a PipelineLayout,
|
||||
pub subpass: Option<u32>,
|
||||
pub base_pipeline: Option<Arc<Pipeline>>,
|
||||
|
||||
|
@ -189,63 +214,120 @@ pub struct GraphicsPipelineDesc<'a> {
|
|||
pub depth_stencil: Option<DepthStencilState>,
|
||||
pub color_blend: Option<ColorBlendState<'a>>,
|
||||
pub dynamic: Option<DynamicState<'a>>,
|
||||
pub rendering: Option<RenderingState<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DescriptorPoolDesc<'a> {
|
||||
pub flags: vk::DescriptorPoolCreateFlags,
|
||||
pub name: Option<Cow<'static, str>>,
|
||||
pub sizes: &'a [vk::DescriptorPoolSize],
|
||||
pub max_sets: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DescriptorSetLayout {
|
||||
set_layout: DeviceOwnedDebugObject<vk::DescriptorSetLayout>,
|
||||
pub struct DescriptorSetAllocDesc<'a> {
|
||||
pub name: Option<Cow<'static, str>>,
|
||||
pub layout: &'a DescriptorSetLayout,
|
||||
}
|
||||
|
||||
impl Drop for DescriptorSetLayout {
|
||||
fn drop(&mut self) {
|
||||
define_device_owned_handle! {
|
||||
#[derive(Debug)]
|
||||
pub DescriptorPool(vk::DescriptorPool) {} => |this| unsafe {
|
||||
this.device().dev().destroy_descriptor_pool(this.handle(), None);
|
||||
}
|
||||
}
|
||||
|
||||
impl DescriptorPool {
|
||||
pub fn new(device: Device, desc: DescriptorPoolDesc) -> VkResult<Self> {
|
||||
let info = &vk::DescriptorPoolCreateInfo::default()
|
||||
.flags(desc.flags)
|
||||
.max_sets(desc.max_sets)
|
||||
.pool_sizes(desc.sizes);
|
||||
|
||||
let handle = unsafe { device.dev().create_descriptor_pool(info, None)? };
|
||||
|
||||
Self::construct(device, handle, desc.name)
|
||||
}
|
||||
|
||||
pub fn allocate(&self, descs: &[DescriptorSetAllocDesc]) -> VkResult<Vec<vk::DescriptorSet>> {
|
||||
let layouts = descs
|
||||
.iter()
|
||||
.map(|desc| desc.layout.handle())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let info = &vk::DescriptorSetAllocateInfo::default()
|
||||
.descriptor_pool(self.handle())
|
||||
.set_layouts(&layouts);
|
||||
let sets = unsafe { self.device().dev().allocate_descriptor_sets(&info)? };
|
||||
|
||||
Ok(sets)
|
||||
}
|
||||
|
||||
// pub fn free(&self) {}
|
||||
pub fn reset(&self) -> VkResult<()> {
|
||||
unsafe {
|
||||
self.set_layout
|
||||
self.device()
|
||||
.dev()
|
||||
.dev()
|
||||
.destroy_descriptor_set_layout(self.set_layout.handle(), None);
|
||||
.reset_descriptor_pool(self.handle(), vk::DescriptorPoolResetFlags::empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_device_owned_handle! {
|
||||
#[derive(Debug)]
|
||||
pub DescriptorSetLayout(vk::DescriptorSetLayout) {} => |this| unsafe {
|
||||
this.device().dev().destroy_descriptor_set_layout(this.handle(), None);
|
||||
}
|
||||
}
|
||||
|
||||
impl DescriptorSetLayout {
|
||||
pub fn new(device: Device, desc: DescriptorSetLayoutDesc) -> VkResult<Self> {
|
||||
let bindings = desc
|
||||
let (flags, bindings): (Vec<_>, Vec<_>) = desc
|
||||
.bindings
|
||||
.iter()
|
||||
.map(|binding| {
|
||||
vk::DescriptorSetLayoutBinding::default()
|
||||
let flag = binding.flags.unwrap_or_default();
|
||||
let binding = vk::DescriptorSetLayoutBinding::default()
|
||||
.binding(binding.binding)
|
||||
.descriptor_count(binding.count)
|
||||
.descriptor_type(binding.kind)
|
||||
.stage_flags(binding.stage)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.stage_flags(binding.stage);
|
||||
|
||||
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)
|
||||
.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 {
|
||||
set_layout: DeviceOwnedDebugObject::new(device, layout, desc.name)?,
|
||||
})
|
||||
let layout = unsafe { device.dev().create_descriptor_set_layout(&info, None)? };
|
||||
|
||||
Self::construct(device, layout, desc.name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PipelineLayout {
|
||||
pipeline_layout: DeviceOwnedDebugObject<vk::PipelineLayout>,
|
||||
}
|
||||
use crate::device::DeviceOwned;
|
||||
|
||||
impl Drop for PipelineLayout {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.pipeline_layout
|
||||
.dev()
|
||||
.dev()
|
||||
.destroy_pipeline_layout(self.pipeline_layout.handle(), None);
|
||||
}
|
||||
define_device_owned_handle! {
|
||||
#[derive(Debug)]
|
||||
pub PipelineLayout(vk::PipelineLayout) {} => |this| unsafe {
|
||||
this.device().dev().destroy_pipeline_layout(this.handle(), None);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,22 +336,149 @@ impl PipelineLayout {
|
|||
let set_layouts = desc
|
||||
.descriptor_set_layouts
|
||||
.iter()
|
||||
.map(|desc| desc.set_layout.handle())
|
||||
.map(|desc| desc.handle())
|
||||
.collect::<Vec<_>>();
|
||||
let info = &vk::PipelineLayoutCreateInfo::default()
|
||||
.set_layouts(&set_layouts)
|
||||
.push_constant_ranges(desc.push_constant_ranges);
|
||||
let layout = unsafe { device.dev().create_pipeline_layout(info, None)? };
|
||||
|
||||
Ok(Self {
|
||||
pipeline_layout: DeviceOwnedDebugObject::new(device, layout, desc.name)?,
|
||||
})
|
||||
Self::construct(device, layout, desc.name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SamplerDesc {
|
||||
pub flags: vk::SamplerCreateFlags,
|
||||
pub min_filter: vk::Filter,
|
||||
pub mag_filter: vk::Filter,
|
||||
pub mipmap_mode: vk::SamplerMipmapMode,
|
||||
pub address_u: vk::SamplerAddressMode,
|
||||
pub address_v: vk::SamplerAddressMode,
|
||||
pub address_w: vk::SamplerAddressMode,
|
||||
pub mip_lod_bias: f32,
|
||||
pub anisotropy_enable: bool,
|
||||
pub max_anisotropy: f32,
|
||||
pub compare_op: Option<vk::CompareOp>,
|
||||
pub min_lod: f32,
|
||||
pub max_lod: f32,
|
||||
pub border_color: vk::BorderColor,
|
||||
pub unnormalized_coordinates: bool,
|
||||
}
|
||||
|
||||
impl Eq for SamplerDesc {}
|
||||
impl PartialEq for SamplerDesc {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
use crate::util::eq_f32;
|
||||
self.flags == other.flags
|
||||
&& self.min_filter == other.min_filter
|
||||
&& self.mag_filter == other.mag_filter
|
||||
&& self.mipmap_mode == other.mipmap_mode
|
||||
&& self.address_u == other.address_u
|
||||
&& self.address_v == other.address_v
|
||||
&& self.address_w == other.address_w
|
||||
&& self.anisotropy_enable == other.anisotropy_enable
|
||||
&& self.compare_op == other.compare_op
|
||||
&& eq_f32(self.mip_lod_bias, other.mip_lod_bias)
|
||||
&& eq_f32(self.max_anisotropy, other.max_anisotropy)
|
||||
&& eq_f32(self.min_lod, other.min_lod)
|
||||
&& eq_f32(self.max_lod, other.max_lod)
|
||||
&& self.border_color == other.border_color
|
||||
&& self.unnormalized_coordinates == other.unnormalized_coordinates
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for SamplerDesc {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
use crate::util::hash_f32;
|
||||
self.flags.hash(state);
|
||||
self.min_filter.hash(state);
|
||||
self.mag_filter.hash(state);
|
||||
self.mipmap_mode.hash(state);
|
||||
self.address_u.hash(state);
|
||||
self.address_v.hash(state);
|
||||
self.address_w.hash(state);
|
||||
hash_f32(state, self.mip_lod_bias);
|
||||
hash_f32(state, self.max_anisotropy);
|
||||
hash_f32(state, self.min_lod);
|
||||
hash_f32(state, self.max_lod);
|
||||
self.anisotropy_enable.hash(state);
|
||||
self.compare_op.hash(state);
|
||||
self.border_color.hash(state);
|
||||
self.unnormalized_coordinates.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
define_device_owned_handle! {
|
||||
#[derive(Debug)]
|
||||
pub Sampler(vk::Sampler) {} => |this| unsafe {
|
||||
this.device().dev().destroy_sampler(this.handle(), None);
|
||||
}
|
||||
}
|
||||
|
||||
impl Sampler {
|
||||
pub fn new(device: Device, desc: &SamplerDesc) -> VkResult<Self> {
|
||||
let info = &vk::SamplerCreateInfo::default()
|
||||
.flags(desc.flags)
|
||||
.min_filter(desc.min_filter)
|
||||
.mag_filter(desc.mag_filter)
|
||||
.mip_lod_bias(desc.mip_lod_bias)
|
||||
.mipmap_mode(desc.mipmap_mode)
|
||||
.address_mode_u(desc.address_u)
|
||||
.address_mode_v(desc.address_v)
|
||||
.address_mode_w(desc.address_w)
|
||||
.anisotropy_enable(desc.anisotropy_enable)
|
||||
.max_anisotropy(desc.max_anisotropy)
|
||||
.compare_enable(desc.compare_op.is_some())
|
||||
.compare_op(desc.compare_op.unwrap_or_default())
|
||||
.min_lod(desc.min_lod)
|
||||
.max_lod(desc.max_lod)
|
||||
.border_color(desc.border_color)
|
||||
.unnormalized_coordinates(desc.unnormalized_coordinates);
|
||||
|
||||
let handle = unsafe { device.dev().create_sampler(info, None)? };
|
||||
|
||||
Self::construct(device, handle, None)
|
||||
}
|
||||
}
|
||||
|
||||
define_device_owned_handle! {
|
||||
#[derive(Debug)]
|
||||
pub ShaderModule(vk::ShaderModule) {} => |this| unsafe {
|
||||
this.device().dev().destroy_shader_module(this.handle(), None);
|
||||
}
|
||||
}
|
||||
|
||||
impl ShaderModule {
|
||||
pub fn new_from_path<P: AsRef<Path>>(device: Device, path: P) -> crate::Result<Self> {
|
||||
use std::io::{BufReader, Read, Seek};
|
||||
|
||||
let mut file = std::fs::File::open(path)?;
|
||||
let size = file.seek(std::io::SeekFrom::End(0))? / 4;
|
||||
file.seek(std::io::SeekFrom::Start(0))?;
|
||||
let mut reader = BufReader::new(file);
|
||||
|
||||
let mut buffer = Vec::<u32>::with_capacity(size as usize);
|
||||
buffer.resize(size as usize, 0);
|
||||
let size = reader.read(bytemuck::cast_slice_mut(buffer.as_mut_slice()))?;
|
||||
buffer.resize(size / 4, 0);
|
||||
|
||||
Ok(Self::new_from_memory(device, &buffer)?)
|
||||
}
|
||||
|
||||
pub fn new_from_memory(device: Device, buffer: &[u32]) -> VkResult<Self> {
|
||||
let info = &vk::ShaderModuleCreateInfo::default().code(buffer);
|
||||
|
||||
let module = unsafe { device.dev().create_shader_module(info, None)? };
|
||||
|
||||
Self::construct(device, module, None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Pipeline {
|
||||
pipeline: DeviceOwnedDebugObject<vk::Pipeline>,
|
||||
bind_point: vk::PipelineBindPoint,
|
||||
}
|
||||
|
||||
impl Drop for Pipeline {
|
||||
|
@ -286,7 +495,7 @@ impl Drop for Pipeline {
|
|||
impl ShaderStageDesc<'_> {
|
||||
fn into_create_info(&self) -> vk::PipelineShaderStageCreateInfo {
|
||||
vk::PipelineShaderStageCreateInfo::default()
|
||||
.module(self.module)
|
||||
.module(self.module.handle())
|
||||
.flags(self.flags)
|
||||
.stage(self.stage)
|
||||
.name(&self.entry)
|
||||
|
@ -296,11 +505,13 @@ impl ShaderStageDesc<'_> {
|
|||
impl Pipeline {
|
||||
pub fn new(device: Device, desc: PipelineDesc) -> VkResult<Self> {
|
||||
let name: Option<Cow<'static, str>>;
|
||||
let bind_point: vk::PipelineBindPoint;
|
||||
let result = match desc {
|
||||
PipelineDesc::Compute(desc) => {
|
||||
name = desc.name;
|
||||
bind_point = vk::PipelineBindPoint::COMPUTE;
|
||||
let info = &vk::ComputePipelineCreateInfo::default()
|
||||
.layout(desc.layout.pipeline_layout.handle())
|
||||
.layout(desc.layout.handle())
|
||||
.stage(desc.shader_stage.into_create_info());
|
||||
|
||||
unsafe {
|
||||
|
@ -313,6 +524,7 @@ impl Pipeline {
|
|||
}
|
||||
PipelineDesc::Graphics(desc) => {
|
||||
name = desc.name;
|
||||
bind_point = vk::PipelineBindPoint::GRAPHICS;
|
||||
|
||||
let stages = desc
|
||||
.shader_stages
|
||||
|
@ -426,6 +638,15 @@ impl Pipeline {
|
|||
info
|
||||
});
|
||||
|
||||
let mut rendering = desc.rendering.map(|state| {
|
||||
let mut info = vk::PipelineRenderingCreateInfo::default()
|
||||
.color_attachment_formats(state.color_formats)
|
||||
.depth_attachment_format(state.depth_format.unwrap_or_default())
|
||||
.stencil_attachment_format(state.stencil_format.unwrap_or_default());
|
||||
|
||||
info
|
||||
});
|
||||
|
||||
fn option_to_ptr<T>(option: &Option<T>) -> *const T {
|
||||
option
|
||||
.as_ref()
|
||||
|
@ -433,7 +654,7 @@ impl Pipeline {
|
|||
.unwrap_or(core::ptr::null())
|
||||
}
|
||||
|
||||
let info = &vk::GraphicsPipelineCreateInfo {
|
||||
let mut info = vk::GraphicsPipelineCreateInfo {
|
||||
flags: desc.flags,
|
||||
stage_count: stages.len() as u32,
|
||||
p_stages: stages.as_ptr(),
|
||||
|
@ -446,7 +667,7 @@ impl Pipeline {
|
|||
p_depth_stencil_state: option_to_ptr(&depth_stencil),
|
||||
p_color_blend_state: option_to_ptr(&color_blend),
|
||||
p_dynamic_state: option_to_ptr(&dynamic),
|
||||
layout: desc.layout.pipeline_layout.handle(),
|
||||
layout: desc.layout.handle(),
|
||||
render_pass: desc.render_pass.unwrap_or(vk::RenderPass::null()),
|
||||
subpass: desc.subpass.unwrap_or(0),
|
||||
base_pipeline_handle: desc
|
||||
|
@ -456,10 +677,14 @@ impl Pipeline {
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(rendering) = rendering.as_mut() {
|
||||
info = info.push_next(rendering)
|
||||
}
|
||||
|
||||
unsafe {
|
||||
device.dev().create_graphics_pipelines(
|
||||
vk::PipelineCache::null(),
|
||||
core::slice::from_ref(info),
|
||||
core::slice::from_ref(&info),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
@ -481,6 +706,14 @@ impl Pipeline {
|
|||
|
||||
Ok(Self {
|
||||
pipeline: DeviceOwnedDebugObject::new(device, pipeline, name)?,
|
||||
bind_point,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn handle(&self) -> vk::Pipeline {
|
||||
self.pipeline.handle()
|
||||
}
|
||||
pub fn bind_point(&self) -> vk::PipelineBindPoint {
|
||||
self.bind_point
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::hash::Hash;
|
||||
|
||||
use crate::util::hash_f32;
|
||||
use ash::vk;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -7,18 +8,6 @@ pub struct Rgba(pub [f32; 4]);
|
|||
|
||||
impl std::hash::Hash for Rgba {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
fn hash_f32<H: std::hash::Hasher>(state: &mut H, f: f32) {
|
||||
let classify = f.classify();
|
||||
match classify {
|
||||
std::num::FpCategory::Nan => (classify as u8).hash(state),
|
||||
std::num::FpCategory::Infinite | std::num::FpCategory::Zero => {
|
||||
(classify as u8, f.signum() as i8).hash(state)
|
||||
}
|
||||
std::num::FpCategory::Subnormal | std::num::FpCategory::Normal => {
|
||||
f.to_bits().hash(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.0.map(|f| hash_f32(state, f));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,10 @@ macro_rules! def_monotonic_id {
|
|||
.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),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue