render egui to the screen (finally)
This commit is contained in:
parent
81f1ee1f96
commit
e8bcaae2a7
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue