From 51160f97fee18e70061cb368e3fb9cc9a9913847 Mon Sep 17 00:00:00 2001 From: galister <22305755+galister@users.noreply.github.com> Date: Tue, 23 Jan 2024 16:43:17 +0100 Subject: [PATCH] steamvr poc works --- Cargo.lock | 106 ++++--- Cargo.toml | 8 +- src/backend/common.rs | 8 +- src/backend/openvr/lines.rs | 10 +- src/backend/openvr/overlay.rs | 21 +- src/backend/overlay.rs | 10 +- src/graphics.rs | 561 ++++++++++++++++++++++++---------- src/gui/font.rs | 4 +- src/gui/mod.rs | 153 +++++----- src/overlays/screen.rs | 84 ++--- src/shaders/mod.rs | 117 +++++++ src/shaders/src/screen.frag | 111 +++++++ 12 files changed, 817 insertions(+), 376 deletions(-) create mode 100644 src/shaders/src/screen.frag diff --git a/Cargo.lock b/Cargo.lock index 26723cc..ee707db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,7 +293,7 @@ dependencies = [ "futures-lite 2.1.0", "parking", "polling 3.3.1", - "rustix 0.38.26", + "rustix 0.38.28", "slab", "tracing", "windows-sys 0.52.0", @@ -332,7 +332,7 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.26", + "rustix 0.38.28", "windows-sys 0.48.0", ] @@ -359,7 +359,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.26", + "rustix 0.38.28", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -1794,7 +1794,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.3", - "rustix 0.38.26", + "rustix 0.38.28", "windows-sys 0.48.0", ] @@ -1818,9 +1818,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jni" @@ -2101,9 +2101,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "log", @@ -2328,9 +2328,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "orbclient" @@ -2520,7 +2520,7 @@ dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.26", + "rustix 0.38.28", "tracing", "windows-sys 0.52.0", ] @@ -2593,6 +2593,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.33" @@ -2747,9 +2756,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.26" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", @@ -2774,9 +2783,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "same-file" @@ -3115,7 +3124,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall 0.4.1", - "rustix 0.38.26", + "rustix 0.38.28", "windows-sys 0.48.0", ] @@ -3298,9 +3307,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -3373,18 +3382,18 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vk-parse" -version = "0.8.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6a0bda9bbe6b9e50e6456c80aa8fe4cca3b21e4311a1130c41e4915ec2e32a" +checksum = "81086c28be67a8759cd80cbb3c8f7b520e0874605fc5eb74d5a1c9c2d1878e79" dependencies = [ "xml-rs", ] [[package]] name = "vulkano" -version = "0.33.0" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e1f15eeb9d93a05eb3c237332a10806eac1eb82444e54485bfcc1859c483c23" +checksum = "70f4278f76307b3c388679234b397b4f90de29cdba53873c26b624ed82653d75" dependencies = [ "ahash", "ash", @@ -3393,13 +3402,14 @@ dependencies = [ "crossbeam-queue", "half", "heck", - "indexmap 1.9.3", - "libloading 0.7.4", + "indexmap 2.1.0", + "libloading 0.8.1", "objc", "once_cell", "parking_lot", "proc-macro2", "quote", + "raw-window-handle", "regex", "serde", "serde_json", @@ -3411,9 +3421,9 @@ dependencies = [ [[package]] name = "vulkano-macros" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "895b8a2cac1e7650d2d0552f2392da0970a358515ac11a34adaf19bfdc771b98" +checksum = "52be622d364272fd77e298e7f68e8547ae66e7687cb86eb85335412cee7e3965" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -3423,36 +3433,35 @@ dependencies = [ [[package]] name = "vulkano-shaders" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f8cf18e9becbc6d39f1c39e26bcf69546c93989553eb5748cd734a8a697a6e5" +checksum = "d1f63401297565d74afb96e9add12587d8e46235140cee325a8eb6ba4602f4ee" dependencies = [ "ahash", "heck", "proc-macro2", "quote", "shaderc", - "syn 1.0.109", + "syn 2.0.39", "vulkano", ] [[package]] name = "vulkano-util" -version = "0.33.0" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a71b6df05a391161c1baec645a918437c2949d3494bf74c8358fde291d37f5f4" +checksum = "ff1f96a4055a8362c8fa77a17c01da184c6412961cd9572110533ff89e6669a7" dependencies = [ "ahash", "vulkano", - "vulkano-win", "winit", ] [[package]] name = "vulkano-win" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666c77efe5ea82837781961a6bcd957ee2e926777e8de0005f580335d6eaefe7" +checksum = "b26c66e8600327823c15184e096eab37a7491d2f3f52b4956c21d84594fb0183" dependencies = [ "core-graphics-types", "objc", @@ -3682,7 +3691,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9b873b257fbc32ec909c0eb80dea312076a67014e65e245f5eb69a6b8ab330e" dependencies = [ "proc-macro2", - "quick-xml", + "quick-xml 0.28.2", "quote", ] @@ -3727,7 +3736,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.26", + "rustix 0.38.28", ] [[package]] @@ -4014,9 +4023,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.5.22" +version = "0.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40460c13b1e9b107cfc9ede71232f204c36250bbf6f9c78515e2e27ead3122a7" +checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" dependencies = [ "memchr", ] @@ -4090,23 +4099,20 @@ dependencies = [ [[package]] name = "xcb" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3acf6b0945550d37d3a683b8f7de9d9f66b2c14dc390313b34d7ac6f1b4089" +checksum = "5d27b37e69b8c05bfadcd968eb1a4fe27c9c52565b727f88512f43b89567e262" dependencies = [ "bitflags 1.3.2", "libc", - "quick-xml", + "quick-xml 0.30.0", ] [[package]] name = "xcursor" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" -dependencies = [ - "nom", -] +checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" [[package]] name = "xdg-home" @@ -4220,18 +4226,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.28" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e" +checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.28" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b" +checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 2af5d3b..1ad9598 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,10 +35,10 @@ serde_yaml = "0.9.25" smallvec = "1.11.0" strum = { version = "0.25.0", features = ["derive"] } tinyvec = "1.6.0" -vulkano = { version = "0.33.0", features = ["serde"] } -vulkano-shaders = "0.33.0" -vulkano-util = "0.33.0" -vulkano-win = "0.33.0" +vulkano = { version = "0.34.1", features = ["serde"] } +vulkano-shaders = "0.34.0" +vulkano-util = "0.34.1" +vulkano-win = "0.34.0" winit = "0.28.6" wlx-capture = { path = "../wlx-capture" } diff --git a/src/backend/common.rs b/src/backend/common.rs index ca49d07..8daba1c 100644 --- a/src/backend/common.rs +++ b/src/backend/common.rs @@ -39,11 +39,11 @@ where get_screens_x11() }; - let watch = create_watch::(&app, &screens); - overlays.insert(watch.state.id, watch); + //let watch = create_watch::(&app, &screens); + //overlays.insert(watch.state.id, watch); - let keyboard = create_keyboard(&app); - overlays.insert(keyboard.state.id, keyboard); + //let keyboard = create_keyboard(&app); + //overlays.insert(keyboard.state.id, keyboard); let mut first = true; for mut screen in screens { diff --git a/src/backend/openvr/lines.rs b/src/backend/openvr/lines.rs index ee2bdb9..e3378fd 100644 --- a/src/backend/openvr/lines.rs +++ b/src/backend/openvr/lines.rs @@ -8,7 +8,7 @@ use ovr_overlay::overlay::OverlayManager; use vulkano::command_buffer::CommandBufferUsage; use vulkano::format::Format; use vulkano::image::view::ImageView; -use vulkano::image::{ImageAccess, ImageLayout, ImageViewAbstract, ImmutableImage}; +use vulkano::image::ImageLayout; use crate::backend::overlay::{OverlayData, OverlayRenderer, OverlayState, SplitOverlayBackend}; use crate::graphics::WlxGraphics; @@ -20,7 +20,7 @@ static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(1); pub(super) struct LinePool { lines: IdMap>, - view: Arc>, + view: Arc, } impl LinePool { @@ -34,7 +34,7 @@ impl LinePool { graphics .transition_layout( - texture.inner().image.clone(), + texture.clone(), ImageLayout::ShaderReadOnlyOptimal, ImageLayout::TransferSrcOptimal, ) @@ -116,7 +116,7 @@ impl LinePool { } struct StaticRenderer { - view: Arc>, + view: Arc, } impl OverlayRenderer for StaticRenderer { @@ -124,7 +124,7 @@ impl OverlayRenderer for StaticRenderer { fn pause(&mut self, _app: &mut AppState) {} fn resume(&mut self, _app: &mut AppState) {} fn render(&mut self, _app: &mut AppState) {} - fn view(&mut self) -> Option> { + fn view(&mut self) -> Option> { Some(self.view.clone()) } } diff --git a/src/backend/openvr/overlay.rs b/src/backend/openvr/overlay.rs index 2638a51..afadf8c 100644 --- a/src/backend/openvr/overlay.rs +++ b/src/backend/openvr/overlay.rs @@ -4,7 +4,7 @@ use ovr_overlay::{ pose::Matrix3x4, sys::{ETrackingUniverseOrigin, VRVulkanTextureData_t}, }; -use vulkano::{image::ImageAccess, Handle, VulkanObject}; +use vulkano::{Handle, VulkanObject}; use crate::{ backend::overlay::{OverlayData, RelativeTo}, @@ -195,7 +195,7 @@ impl OverlayData { return; }; - let image = view.image().inner().image.clone(); + let image = view.image().clone(); let raw_image = image.handle().as_raw(); @@ -205,17 +205,14 @@ impl OverlayData { } } - let Some(format) = image.format() else { - panic!("{}: Image format is None", self.state.name); - }; - - let dimensions = image.dimensions(); + let dimensions = image.extent(); + let format = image.format(); let mut texture = VRVulkanTextureData_t { m_nImage: raw_image, m_nFormat: format as _, - m_nWidth: dimensions.width(), - m_nHeight: dimensions.height(), + m_nWidth: dimensions[0], + m_nHeight: dimensions[1], m_nSampleCount: image.samples() as u32, m_pDevice: graphics.device.handle().as_raw() as *mut _, m_pPhysicalDevice: graphics.device.physical_device().handle().as_raw() as *mut _, @@ -224,8 +221,9 @@ impl OverlayData { m_nQueueFamilyIndex: graphics.queue.queue_family_index(), }; - log::debug!( - "UploadTex: {:?}, {}x{}, {:?}", + log::info!( + "{}: UploadTex {:?}, {}x{}, {:?}", + self.state.name, format, texture.m_nWidth, texture.m_nHeight, @@ -234,5 +232,6 @@ impl OverlayData { if let Err(e) = overlay.set_image_vulkan(handle, &mut texture) { panic!("Failed to set overlay texture: {}", e); } + log::info!("{}: Uploaded texture", self.state.name); } } diff --git a/src/backend/overlay.rs b/src/backend/overlay.rs index 82ec046..d8d689e 100644 --- a/src/backend/overlay.rs +++ b/src/backend/overlay.rs @@ -7,7 +7,7 @@ use std::{ }; use glam::{Affine2, Affine3A, Mat3A, Quat, Vec3, Vec3A}; -use vulkano::image::ImageViewAbstract; +use vulkano::image::view::ImageView; use crate::state::AppState; @@ -134,7 +134,7 @@ where pub fn render(&mut self, app: &mut AppState) { self.backend.render(app); } - pub fn view(&mut self) -> Option> { + pub fn view(&mut self) -> Option> { self.backend.view() } } @@ -144,7 +144,7 @@ pub trait OverlayRenderer { fn pause(&mut self, app: &mut AppState); fn resume(&mut self, app: &mut AppState); fn render(&mut self, app: &mut AppState); - fn view(&mut self) -> Option>; + fn view(&mut self) -> Option>; } pub struct FallbackRenderer; @@ -154,7 +154,7 @@ impl OverlayRenderer for FallbackRenderer { fn pause(&mut self, _app: &mut AppState) {} fn resume(&mut self, _app: &mut AppState) {} fn render(&mut self, _app: &mut AppState) {} - fn view(&mut self) -> Option> { + fn view(&mut self) -> Option> { None } } @@ -196,7 +196,7 @@ impl OverlayRenderer for SplitOverlayBackend { fn render(&mut self, app: &mut AppState) { self.renderer.render(app); } - fn view(&mut self) -> Option> { + fn view(&mut self) -> Option> { self.renderer.view() } } diff --git a/src/graphics.rs b/src/graphics.rs index b43bf61..5b13973 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -1,22 +1,26 @@ -use std::{error::Error, io::Cursor, slice::Iter, sync::Arc}; +use std::{ + error::Error, + io::Cursor, + os::fd::{FromRawFd, IntoRawFd}, + slice::Iter, + sync::Arc, +}; use ash::vk::SubmitInfo; -use smallvec::smallvec; +use smallvec::{smallvec, SmallVec}; use vulkano::{ buffer::{ allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer, }, command_buffer::{ - allocator::{ - CommandBufferAllocator, CommandBufferBuilderAlloc, StandardCommandBufferAllocator, - }, + allocator::{StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo}, sys::{CommandBufferBeginInfo, UnsafeCommandBufferBuilder}, AutoCommandBufferBuilder, CommandBufferExecFuture, CommandBufferInheritanceInfo, - CommandBufferInheritanceRenderPassType, CommandBufferInheritanceRenderingInfo, - CommandBufferLevel, CommandBufferUsage, PrimaryAutoCommandBuffer, - PrimaryCommandBufferAbstract, RenderingAttachmentInfo, RenderingInfo, - SecondaryAutoCommandBuffer, SubpassContents, + CommandBufferInheritanceRenderPassInfo, CommandBufferInheritanceRenderPassType, + CommandBufferLevel, CommandBufferUsage, CopyBufferToImageInfo, PrimaryAutoCommandBuffer, + PrimaryCommandBufferAbstract, RenderPassBeginInfo, SecondaryAutoCommandBuffer, + SubpassBeginInfo, SubpassContents, SubpassEndInfo, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, @@ -27,33 +31,47 @@ use vulkano::{ }, format::Format, image::{ - sys::Image, AttachmentImage, ImageCreateFlags, ImageDimensions, ImageError, ImageLayout, - ImageUsage, ImageViewAbstract, ImmutableImage, MipmapsCount, StorageImage, SubresourceData, - SwapchainImage, + sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + sys::RawImage, + view::ImageView, + Image, ImageCreateInfo, ImageLayout, ImageTiling, ImageType, ImageUsage, SampleCount, + SubresourceLayout, + }, + instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, + memory::{ + allocator::{ + AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter, StandardMemoryAllocator, + }, + DedicatedAllocation, DeviceMemory, ExternalMemoryHandleType, ExternalMemoryHandleTypes, + MemoryAllocateInfo, MemoryImportInfo, ResourceMemory, }, - instance::{Instance, InstanceCreateInfo, InstanceExtensions}, - memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ graphics::{ - color_blend::{AttachmentBlend, ColorBlendState}, + color_blend::{AttachmentBlend, ColorBlendAttachmentState, ColorBlendState}, input_assembly::InputAssemblyState, - render_pass::PipelineRenderingCreateInfo, - vertex_input::Vertex, + multisample::MultisampleState, + rasterization::RasterizationState, + vertex_input::{Vertex, VertexDefinition}, viewport::{Viewport, ViewportState}, + GraphicsPipelineCreateInfo, }, - GraphicsPipeline, Pipeline, PipelineBindPoint, + layout::PipelineDescriptorSetLayoutCreateInfo, + DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout, + PipelineShaderStageCreateInfo, + }, + render_pass::{ + AttachmentDescription, AttachmentLoadOp, AttachmentReference, AttachmentStoreOp, + Framebuffer, FramebufferCreateInfo, RenderPass, RenderPassCreateInfo, Subpass, + SubpassDescription, }, - render_pass::{LoadOp, StoreOp}, - sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, shader::ShaderModule, swapchain::{CompositeAlpha, Surface, Swapchain, SwapchainCreateInfo}, sync::{ fence::Fence, future::NowFuture, AccessFlags, DependencyInfo, GpuFuture, ImageMemoryBarrier, PipelineStages, }, - Version, VulkanLibrary, VulkanObject, + DeviceSize, VulkanLibrary, VulkanObject, }; -use vulkano_win::VkSurfaceBuild; use winit::{ event_loop::EventLoop, window::{Window, WindowBuilder}, @@ -98,8 +116,11 @@ impl WlxGraphics { #[cfg(not(debug_assertions))] let layers = vec![]; + // TODO headless + let event_loop = EventLoop::new(); + let library_extensions = Surface::required_extensions(&event_loop); + let library = VulkanLibrary::new().unwrap(); - let library_extensions = vulkano_win::required_extensions(&library); let required_extensions = library_extensions.union(&vk_instance_extensions); log::debug!("Instance exts for app: {:?}", &required_extensions); @@ -108,9 +129,9 @@ impl WlxGraphics { let instance = Instance::new( library, InstanceCreateInfo { + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, enabled_extensions: required_extensions, enabled_layers: layers, - enumerate_portability: true, ..Default::default() }, ) @@ -128,17 +149,15 @@ impl WlxGraphics { log::debug!("Device exts for app: {:?}", &device_extensions); // TODO headless - let event_loop = EventLoop::new(); - let surface = WindowBuilder::new() - .build_vk_surface(&event_loop, instance.clone()) - .unwrap(); + let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); + let surface = Surface::from_window(instance.clone(), window.clone()).unwrap(); let (physical_device, my_extensions, queue_family_index) = instance .enumerate_physical_devices() .unwrap() - .filter(|p| { - p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering - }) + //.filter(|p| { + // p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering + //}) .filter_map(|p| { let runtime_extensions = vk_device_extensions_fn(&p); log::debug!( @@ -178,9 +197,9 @@ impl WlxGraphics { physical_device.properties().device_name, ); - if physical_device.api_version() < Version::V1_3 { - device_extensions.khr_dynamic_rendering = true; - } + //if physical_device.api_version() < Version::V1_3 { + // device_extensions.khr_dynamic_rendering = true; + //} let (device, mut queues) = Device::new( physical_device, @@ -203,11 +222,16 @@ impl WlxGraphics { let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + StandardCommandBufferAllocatorCreateInfo { + secondary_buffer_count: 32, + ..Default::default() + }, + )); + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( device.clone(), Default::default(), )); - let descriptor_set_allocator = - Arc::new(StandardDescriptorSetAllocator::new(device.clone())); let vertices = [ Vert2Uv { @@ -228,13 +252,14 @@ impl WlxGraphics { }, ]; let quad_verts = Buffer::from_iter( - &memory_allocator, + memory_allocator.clone(), BufferCreateInfo { usage: BufferUsage::VERTEX_BUFFER, ..Default::default() }, AllocationCreateInfo { - usage: MemoryUsage::Upload, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, vertices.into_iter(), @@ -242,13 +267,14 @@ impl WlxGraphics { .unwrap(); let quad_indices = Buffer::from_iter( - &memory_allocator, + memory_allocator.clone(), BufferCreateInfo { usage: BufferUsage::INDEX_BUFFER, ..Default::default() }, AllocationCreateInfo { - usage: MemoryUsage::Upload, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, INDICES.iter().cloned(), @@ -271,10 +297,7 @@ impl WlxGraphics { } #[allow(dead_code)] - pub fn create_swapchain( - &self, - format: Option, - ) -> (Arc, Vec>) { + pub fn create_swapchain(&self, format: Option) -> (Arc, Vec>) { let (min_image_count, composite_alpha, image_format) = if let Some(format) = format { (1, CompositeAlpha::Opaque, format) } else { @@ -314,7 +337,7 @@ impl WlxGraphics { self.surface.clone(), SwapchainCreateInfo { min_image_count, - image_format: Some(image_format), + image_format, image_extent: window.inner_size().into(), image_usage: ImageUsage::COLOR_ATTACHMENT, composite_alpha, @@ -370,13 +393,14 @@ impl WlxGraphics { T: BufferContents + Clone, { Buffer::from_iter( - &self.memory_allocator, + self.memory_allocator.clone(), BufferCreateInfo { usage, ..Default::default() }, AllocationCreateInfo { - usage: MemoryUsage::Upload, + memory_type_filter: MemoryTypeFilter::PREFER_HOST + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, contents.cloned(), @@ -384,12 +408,8 @@ impl WlxGraphics { .unwrap() } - pub fn dmabuf_texture(&self, frame: DmabufFrame) -> Result, ImageError> { - let dimensions = ImageDimensions::Dim2d { - width: frame.format.width, - height: frame.format.height, - array_layers: 1, - }; + pub fn dmabuf_texture(&self, frame: DmabufFrame) -> Option> { + let extent = [frame.format.width, frame.format.height, 1]; let format = match frame.format.fourcc { DRM_FORMAT_ABGR8888 => Format::R8G8B8A8_UNORM, @@ -399,60 +419,145 @@ impl WlxGraphics { _ => panic!("Unsupported dmabuf format {:x}", frame.format.fourcc), }; - let planes = frame - .planes - .iter() - .take(frame.num_planes) - .filter_map(|plane| { - let Some(fd) = plane.fd else { - return None; - }; - Some(SubresourceData { - fd, + let layouts: Vec = (0..frame.num_planes) + .into_iter() + .map(|i| { + let plane = &frame.planes[i]; + SubresourceLayout { offset: plane.offset as _, + size: 0, row_pitch: plane.stride as _, - }) + array_pitch: None, + depth_pitch: None, + } }) .collect(); - StorageImage::new_from_dma_buf_fd( - &self.memory_allocator, - self.device.clone(), - dimensions, - format, - ImageUsage::SAMPLED | ImageUsage::TRANSFER_SRC, - ImageCreateFlags::empty(), - [self.queue.queue_family_index()], - planes, - frame.format.modifier, - ) - } + let external_memory_handle_types = ExternalMemoryHandleTypes::DMA_BUF; - pub fn render_texture(&self, width: u32, height: u32, format: Format) -> Arc { - let tex = AttachmentImage::with_usage( - &self.memory_allocator, - [width, height], - format, - ImageUsage::SAMPLED | ImageUsage::TRANSFER_SRC | ImageUsage::COLOR_ATTACHMENT, + let image = RawImage::new( + self.device.clone(), + ImageCreateInfo { + image_type: ImageType::Dim2d, + format, + extent, + usage: ImageUsage::SAMPLED | ImageUsage::TRANSFER_SRC, + external_memory_handle_types, + tiling: ImageTiling::DrmFormatModifier, + drm_format_modifiers: vec![frame.format.modifier], + drm_format_modifier_plane_layouts: layouts, + ..Default::default() + }, ) .unwrap(); - tex + let requirements = image.memory_requirements()[0]; + let memory_type_index = self + .memory_allocator + .find_memory_type_index( + requirements.memory_type_bits, + MemoryTypeFilter::PREFER_DEVICE, + ) + .unwrap(); + + debug_assert!(self.device.enabled_extensions().khr_external_memory_fd); + debug_assert!(self.device.enabled_extensions().khr_external_memory); + debug_assert!(self.device.enabled_extensions().ext_external_memory_dma_buf); + + let memory = unsafe { + if frame.num_planes != 1 { + log::error!("Unsupported number of DMA-buf planes: {}", frame.num_planes); + return None; + } + let Some(fd) = frame.planes[0].fd else { + log::error!("DMA-buf plane has no FD"); + return None; + }; + + let file = std::fs::File::from_raw_fd(fd); + let new_file = file.try_clone().unwrap(); + file.into_raw_fd(); + + DeviceMemory::import( + self.device.clone(), + MemoryAllocateInfo { + allocation_size: requirements.layout.size(), + memory_type_index, + dedicated_allocation: Some(DedicatedAllocation::Image(&image)), + ..Default::default() + }, + MemoryImportInfo::Fd { + file: new_file, + handle_type: ExternalMemoryHandleType::DmaBuf, + }, + ) + .unwrap() + }; + + let allocations: SmallVec<[ResourceMemory; 1]> = + smallvec![ResourceMemory::new_dedicated(memory)]; + + if let Some(image) = image.bind_memory(allocations).ok() { + Some(Arc::new(image)) + } else { + None + } + } + + pub fn render_texture(&self, width: u32, height: u32, format: Format) -> Arc { + Image::new( + self.memory_allocator.clone(), + ImageCreateInfo { + image_type: ImageType::Dim2d, + format, + extent: [width, height, 1], + usage: ImageUsage::TRANSFER_SRC + | ImageUsage::SAMPLED + | ImageUsage::COLOR_ATTACHMENT, + ..Default::default() + }, + AllocationCreateInfo::default(), + ) + .unwrap() } pub fn create_pipeline( self: &Arc, + render_target: Arc, vert: Arc, frag: Arc, format: Format, ) -> Arc { - Arc::new(WlxPipeline::new(self.clone(), vert, frag, format)) + Arc::new(WlxPipeline::new( + render_target, + self.clone(), + vert, + frag, + format, + )) } - pub fn create_command_buffer( + pub fn create_pipeline_with_layouts( self: &Arc, - usage: CommandBufferUsage, - ) -> WlxCommandBuffer { + render_target: Arc, + vert: Arc, + frag: Arc, + format: Format, + initial_layout: ImageLayout, + final_layout: ImageLayout, + ) -> Arc { + Arc::new(WlxPipeline::new_with_layout( + render_target, + self.clone(), + vert, + frag, + format, + initial_layout, + final_layout, + )) + } + + pub fn create_command_buffer(self: &Arc, usage: CommandBufferUsage) -> WlxCommandBuffer { let command_buffer = AutoCommandBufferBuilder::primary( &self.command_buffer_allocator, self.queue.queue_family_index(), @@ -482,31 +587,25 @@ impl WlxGraphics { ..ImageMemoryBarrier::image(image) }; - let builder_alloc = self - .command_buffer_allocator - .allocate( - self.queue.queue_family_index(), - CommandBufferLevel::Primary, - 1, - ) - .unwrap() - .next() - .unwrap(); - let command_buffer = unsafe { let mut builder = UnsafeCommandBufferBuilder::new( - &builder_alloc.inner(), + &self.command_buffer_allocator, + self.queue.queue_family_index(), + CommandBufferLevel::Primary, CommandBufferBeginInfo { usage: CommandBufferUsage::OneTimeSubmit, + inheritance_info: None, ..Default::default() }, ) .unwrap(); - builder.pipeline_barrier(&DependencyInfo { - image_memory_barriers: smallvec![barrier], - ..Default::default() - }); + builder + .pipeline_barrier(&DependencyInfo { + image_memory_barriers: smallvec![barrier], + ..Default::default() + }) + .unwrap(); builder.build().unwrap() }; @@ -535,24 +634,27 @@ impl WlxGraphics { } } -pub struct WlxCommandBuffer { +pub struct WlxCommandBuffer { graphics: Arc, - command_buffer: AutoCommandBufferBuilder>, + command_buffer: AutoCommandBufferBuilder< + PrimaryAutoCommandBuffer>, + Arc, + >, } -impl WlxCommandBuffer { - pub fn begin(mut self, render_target: Arc) -> Self { +impl WlxCommandBuffer { + pub fn begin_render_pass(mut self, pipeline: &WlxPipeline) -> Self { self.command_buffer - .begin_rendering(RenderingInfo { - contents: SubpassContents::SecondaryCommandBuffers, - color_attachments: vec![Some(RenderingAttachmentInfo { - load_op: LoadOp::Clear, - store_op: StoreOp::Store, - clear_value: Some([0.0, 0.0, 0.0, 0.0].into()), - ..RenderingAttachmentInfo::image_view(render_target.clone()) - })], - ..Default::default() - }) + .begin_render_pass( + RenderPassBeginInfo { + clear_values: vec![Some([0.0, 0.0, 0.0, 1.0].into())], + ..RenderPassBeginInfo::framebuffer(pipeline.framebuffer.clone()) + }, + SubpassBeginInfo { + contents: SubpassContents::SecondaryCommandBuffers, + ..Default::default() + }, + ) .unwrap(); self } @@ -571,26 +673,46 @@ impl WlxCommandBuffer { height: u32, format: Format, data: Vec, - ) -> Arc { - let dimensions = ImageDimensions::Dim2d { - width, - height, - array_layers: 1, - }; - - ImmutableImage::from_iter( - &self.graphics.memory_allocator, - data, - dimensions, - MipmapsCount::Log2, // required for TRANSFER_SRC - format, - &mut self.command_buffer, + ) -> Arc { + let image = Image::new( + self.graphics.memory_allocator.clone(), + ImageCreateInfo { + image_type: ImageType::Dim2d, + format, + extent: [width, height, 1], + usage: ImageUsage::TRANSFER_DST | ImageUsage::TRANSFER_SRC | ImageUsage::SAMPLED, + ..Default::default() + }, + AllocationCreateInfo::default(), ) - .unwrap() + .unwrap(); + + let buffer: Subbuffer<[u8]> = Buffer::new_slice( + self.graphics.memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::TRANSFER_SRC, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_HOST + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + data.len() as DeviceSize, + ) + .unwrap(); + + buffer.write().unwrap().copy_from_slice(data.as_slice()); + + self.command_buffer + .copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(buffer, image.clone())) + .unwrap(); + + image } #[allow(dead_code)] - pub fn texture2d_png(&mut self, bytes: Vec) -> Arc { + pub fn texture2d_png(&mut self, bytes: Vec) -> Arc { let cursor = Cursor::new(bytes); let decoder = png::Decoder::new(cursor); let mut reader = decoder.read_info().unwrap(); @@ -604,13 +726,15 @@ impl WlxCommandBuffer { } } -impl WlxCommandBuffer { - pub fn end_render(mut self) -> Self { - self.command_buffer.end_rendering().unwrap(); +impl WlxCommandBuffer { + pub fn end_render_pass(mut self) -> Self { + self.command_buffer + .end_render_pass(SubpassEndInfo::default()) + .unwrap(); self } - pub fn build(self) -> PrimaryAutoCommandBuffer { + pub fn build(self) -> Arc>> { self.command_buffer.build().unwrap() } @@ -629,11 +753,79 @@ impl WlxCommandBuffer { pub struct WlxPipeline { pub graphics: Arc, pub pipeline: Arc, + pub render_pass: Arc, + pub framebuffer: Arc, + pub view: Arc, pub format: Format, } impl WlxPipeline { fn new( + render_target: Arc, + graphics: Arc, + vert: Arc, + frag: Arc, + format: Format, + ) -> Self { + let render_pass = vulkano::single_pass_renderpass!( + graphics.device.clone(), + attachments: { + color: { + format: format, + samples: 1, + load_op: Clear, + store_op: Store, + }, + }, + pass: { + color: [color], + depth_stencil: {}, + }, + ) + .unwrap(); + + Self::new_from_pass(render_target, render_pass, graphics, vert, frag, format) + } + + fn new_with_layout( + render_target: Arc, + graphics: Arc, + vert: Arc, + frag: Arc, + format: Format, + initial_layout: ImageLayout, + final_layout: ImageLayout, + ) -> Self { + let render_pass_description = RenderPassCreateInfo { + attachments: vec![AttachmentDescription { + format: format, + samples: SampleCount::Sample1, + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::Store, + initial_layout, + final_layout, + ..Default::default() + }], + subpasses: vec![SubpassDescription { + color_attachments: vec![Some(AttachmentReference { + attachment: 0, + layout: ImageLayout::ColorAttachmentOptimal, + ..Default::default() + })], + ..Default::default() + }], + ..Default::default() + }; + + let render_pass = + RenderPass::new(graphics.device.clone(), render_pass_description).unwrap(); + + Self::new_from_pass(render_target, render_pass, graphics, vert, frag, format) + } + + fn new_from_pass( + render_target: Arc, + render_pass: Arc, graphics: Arc, vert: Arc, frag: Arc, @@ -641,24 +833,64 @@ impl WlxPipeline { ) -> Self { let vep = vert.entry_point("main").unwrap(); let fep = frag.entry_point("main").unwrap(); - let pipeline = GraphicsPipeline::start() - .render_pass(PipelineRenderingCreateInfo { - color_attachment_formats: vec![Some(format)], - ..Default::default() - }) - .color_blend_state(ColorBlendState::default().blend(AttachmentBlend::alpha())) - .vertex_input_state(Vert2Uv::per_vertex()) - .input_assembly_state(InputAssemblyState::new()) - .vertex_shader(vep, ()) - .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fep, ()) - .build(graphics.device.clone()) + + let vertex_input_state = Vert2Uv::per_vertex() + .definition(&vep.info().input_interface) .unwrap(); + let stages = smallvec![ + PipelineShaderStageCreateInfo::new(vep), + PipelineShaderStageCreateInfo::new(fep), + ]; + + let layout = PipelineLayout::new( + graphics.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(graphics.device.clone()) + .unwrap(), + ) + .unwrap(); + + let framebuffer = Framebuffer::new( + render_pass.clone(), + FramebufferCreateInfo { + attachments: vec![render_target.clone()], + ..Default::default() + }, + ) + .unwrap(); + + let pipeline = GraphicsPipeline::new( + graphics.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages, + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + color_blend_state: Some(ColorBlendState { + attachments: vec![ColorBlendAttachmentState { + blend: Some(AttachmentBlend::alpha()), + ..Default::default() + }], + ..Default::default() + }), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(Subpass::from(render_pass.clone(), 0).unwrap().into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) + .unwrap(); + Self { graphics, pipeline, format, + render_pass, + framebuffer, + view: render_target, } } @@ -669,7 +901,7 @@ impl WlxPipeline { pub fn uniform_sampler( &self, set: usize, - texture: Arc, + texture: Arc, filter: Filter, ) -> Arc { let sampler = Sampler::new( @@ -689,6 +921,7 @@ impl WlxPipeline { &self.graphics.descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::image_view_sampler(0, texture, sampler)], + [], ) .unwrap() } @@ -701,6 +934,8 @@ impl WlxPipeline { self.graphics.memory_allocator.clone(), SubbufferAllocatorCreateInfo { buffer_usage: BufferUsage::UNIFORM_BUFFER, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, ); @@ -716,6 +951,7 @@ impl WlxPipeline { &self.graphics.descriptor_set_allocator, layout.clone(), [WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)], + [], ) .unwrap() } @@ -743,7 +979,7 @@ pub struct WlxPass { vertex_buffer: Subbuffer<[Vert2Uv]>, index_buffer: Subbuffer<[u16]>, descriptor_sets: Vec>, - pub command_buffer: Arc, + pub command_buffer: Arc>>, } impl WlxPass { @@ -755,9 +991,9 @@ impl WlxPass { descriptor_sets: Vec>, ) -> Self { let viewport = Viewport { - origin: [0.0, 0.0], - dimensions, - depth_range: 0.0..1.0, + offset: [0.0, 0.0], + extent: dimensions, + depth_range: 0.0..=1.0, }; let pipeline_inner = pipeline.inner().clone(); @@ -766,10 +1002,10 @@ impl WlxPass { pipeline.graphics.queue.queue_family_index(), CommandBufferUsage::MultipleSubmit, CommandBufferInheritanceInfo { - render_pass: Some(CommandBufferInheritanceRenderPassType::BeginRendering( - CommandBufferInheritanceRenderingInfo { - color_attachment_formats: vec![Some(pipeline.format)], - ..Default::default() + render_pass: Some(CommandBufferInheritanceRenderPassType::BeginRenderPass( + CommandBufferInheritanceRenderPassInfo { + subpass: Subpass::from(pipeline.render_pass.clone(), 0).unwrap(), + framebuffer: None, }, )), ..Default::default() @@ -778,16 +1014,21 @@ impl WlxPass { .unwrap(); command_buffer - .set_viewport(0, [viewport]) + .set_viewport(0, smallvec![viewport]) + .unwrap() .bind_pipeline_graphics(pipeline_inner) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, pipeline.inner().layout().clone(), 0, descriptor_sets.clone(), ) + .unwrap() .bind_vertex_buffers(0, vertex_buffer.clone()) + .unwrap() .bind_index_buffer(index_buffer.clone()) + .unwrap() .draw_indexed(index_buffer.len() as u32, 1, 0, 0, 0) .or_else(|err| { if let Some(source) = err.source() { @@ -802,7 +1043,7 @@ impl WlxPass { vertex_buffer, index_buffer, descriptor_sets, - command_buffer: Arc::new(command_buffer.build().unwrap()), + command_buffer: command_buffer.build().unwrap(), } } } diff --git a/src/gui/font.rs b/src/gui/font.rs index 8ae11d6..316e3f5 100644 --- a/src/gui/font.rs +++ b/src/gui/font.rs @@ -3,7 +3,7 @@ use std::{rc::Rc, str::FromStr, sync::Arc}; use fontconfig::{FontConfig, OwnedPattern}; use freetype::{bitmap::PixelMode, face::LoadFlag, Face, Library}; use idmap::IdMap; -use vulkano::{command_buffer::CommandBufferUsage, format::Format, image::ImmutableImage}; +use vulkano::{command_buffer::CommandBufferUsage, format::Format, image::Image}; use crate::graphics::WlxGraphics; @@ -26,7 +26,7 @@ struct Font { } pub struct Glyph { - pub tex: Option>, + pub tex: Option>, pub top: f32, pub left: f32, pub width: f32, diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 82d96dd..8d964f3 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -2,10 +2,9 @@ use std::sync::Arc; use glam::{Vec2, Vec3}; use vulkano::{ - command_buffer::{CommandBufferUsage, PrimaryAutoCommandBuffer}, + command_buffer::CommandBufferUsage, format::Format, - image::{view::ImageView, AttachmentImage, ImageAccess, ImageLayout, ImageViewAbstract}, - sampler::Filter, + image::{sampler::Filter, view::ImageView, ImageLayout}, }; use crate::{ @@ -189,8 +188,9 @@ pub struct CanvasData { graphics: Arc, - pipeline_color: Arc, - pipeline_glyph: Arc, + pipeline_bg_color: Arc, + pipeline_fg_glyph: Arc, + pipeline_final: Arc, } pub struct Canvas { @@ -204,14 +204,10 @@ pub struct Canvas { interact_stride: usize, interact_rows: usize, - view_fg: Arc>, - view_bg: Arc>, - view_final: Arc>, + view_final: Arc, pass_fg: WlxPass, pass_bg: WlxPass, - - first_render: bool, } impl Canvas { @@ -222,27 +218,6 @@ impl Canvas { format: Format, data: D, ) -> Self { - let pipeline_color = graphics.create_pipeline( - vert_common::load(graphics.device.clone()).unwrap(), - frag_color::load(graphics.device.clone()).unwrap(), - format, - ); - - let pipeline_glyph = graphics.create_pipeline( - vert_common::load(graphics.device.clone()).unwrap(), - frag_glyph::load(graphics.device.clone()).unwrap(), - format, - ); - - let vertex_buffer = - graphics.upload_verts(width as _, height as _, 0., 0., width as _, height as _); - - let pipeline = graphics.create_pipeline( - vert_common::load(graphics.device.clone()).unwrap(), - frag_sprite::load(graphics.device.clone()).unwrap(), - format, - ); - let tex_fg = graphics.render_texture(width as _, height as _, format); let tex_bg = graphics.render_texture(width as _, height as _, format); let tex_final = graphics.render_texture(width as _, height as _, format); @@ -251,15 +226,41 @@ impl Canvas { let view_bg = ImageView::new_default(tex_bg.clone()).unwrap(); let view_final = ImageView::new_default(tex_final.clone()).unwrap(); - let set_fg = pipeline.uniform_sampler(0, view_fg.clone(), Filter::Nearest); - let set_bg = pipeline.uniform_sampler(0, view_bg.clone(), Filter::Nearest); - let pass_fg = pipeline.create_pass( + let pipeline_bg_color = graphics.create_pipeline( + view_bg.clone(), + vert_common::load(graphics.device.clone()).unwrap(), + frag_color::load(graphics.device.clone()).unwrap(), + format, + ); + + let pipeline_fg_glyph = graphics.create_pipeline( + view_fg.clone(), + vert_common::load(graphics.device.clone()).unwrap(), + frag_glyph::load(graphics.device.clone()).unwrap(), + format, + ); + + let vertex_buffer = + graphics.upload_verts(width as _, height as _, 0., 0., width as _, height as _); + + let pipeline_final = graphics.create_pipeline_with_layouts( + view_final.clone(), + vert_common::load(graphics.device.clone()).unwrap(), + frag_sprite::load(graphics.device.clone()).unwrap(), + format, + ImageLayout::TransferSrcOptimal, + ImageLayout::TransferSrcOptimal, + ); + + let set_fg = pipeline_final.uniform_sampler(0, view_fg.clone(), Filter::Linear); + let set_bg = pipeline_final.uniform_sampler(0, view_bg.clone(), Filter::Linear); + let pass_fg = pipeline_final.create_pass( [width as _, height as _], vertex_buffer.clone(), graphics.quad_indices.clone(), vec![set_fg], ); - let pass_bg = pipeline.create_pass( + let pass_bg = pipeline_final.create_pass( [width as _, height as _], vertex_buffer.clone(), graphics.quad_indices.clone(), @@ -275,8 +276,9 @@ impl Canvas { width, height, graphics, - pipeline_color, - pipeline_glyph, + pipeline_bg_color, + pipeline_fg_glyph, + pipeline_final, }, controls: Vec::new(), hover_controls: [None, None], @@ -284,12 +286,9 @@ impl Canvas { interact_map: vec![None; stride * rows], interact_stride: stride, interact_rows: rows, - view_fg, - view_bg, view_final, pass_fg, pass_bg, - first_render: true, } } @@ -321,13 +320,13 @@ impl Canvas { .canvas .graphics .create_command_buffer(CommandBufferUsage::OneTimeSubmit) - .begin(self.view_bg.clone()); + .begin_render_pass(&self.canvas.pipeline_final); for c in self.controls.iter_mut() { if let Some(fun) = c.on_render_bg { fun(c, &self.canvas, app, &mut cmd_buffer); } } - cmd_buffer.end_render().build_and_execute_now() + cmd_buffer.end_render_pass().build_and_execute_now() } fn render_fg(&mut self, app: &mut AppState) { @@ -335,13 +334,13 @@ impl Canvas { .canvas .graphics .create_command_buffer(CommandBufferUsage::OneTimeSubmit) - .begin(self.view_fg.clone()); + .begin_render_pass(&self.canvas.pipeline_final); for c in self.controls.iter_mut() { if let Some(fun) = c.on_render_fg { fun(c, &self.canvas, app, &mut cmd_buffer); } } - cmd_buffer.end_render().build_and_execute_now() + cmd_buffer.end_render_pass().build_and_execute_now() } } @@ -399,8 +398,12 @@ impl OverlayRenderer for Canvas { } } - let image = self.view_final.image().inner().image.clone(); + if !dirty { + return; + } + /* + let image = self.view_final.image().clone(); if self.first_render { self.first_render = false; } else { @@ -414,16 +417,15 @@ impl OverlayRenderer for Canvas { .wait(None) .unwrap(); } + */ let mut cmd_buffer = self .canvas .graphics .create_command_buffer(CommandBufferUsage::OneTimeSubmit) - .begin(self.view_final.clone()); + .begin_render_pass(&self.canvas.pipeline_final); - if dirty { - self.render_fg(app); - } + self.render_fg(app); // static background cmd_buffer.run_ref(&self.pass_bg); @@ -444,8 +446,10 @@ impl OverlayRenderer for Canvas { // mostly static text cmd_buffer.run_ref(&self.pass_fg); { - let _ = cmd_buffer.end_render().build_and_execute(); + let _ = cmd_buffer.end_render_pass().build_and_execute(); } + + /* self.canvas .graphics .transition_layout( @@ -455,8 +459,9 @@ impl OverlayRenderer for Canvas { ) .wait(None) .unwrap(); + */ } - fn view(&mut self) -> Option> { + fn view(&mut self) -> Option> { Some(self.view_final.clone()) } } @@ -477,21 +482,9 @@ pub struct Control { pub on_release: Option, pub test_highlight: Option bool>, - on_render_bg: Option< - fn(&Self, &CanvasData, &mut AppState, &mut WlxCommandBuffer), - >, - on_render_hl: Option< - fn( - &Self, - &CanvasData, - &mut AppState, - &mut WlxCommandBuffer, - bool, - ), - >, - on_render_fg: Option< - fn(&Self, &CanvasData, &mut AppState, &mut WlxCommandBuffer), - >, + on_render_bg: Option, &mut AppState, &mut WlxCommandBuffer)>, + on_render_hl: Option, &mut AppState, &mut WlxCommandBuffer, bool)>, + on_render_fg: Option, &mut AppState, &mut WlxCommandBuffer)>, } impl Control { @@ -532,7 +525,7 @@ impl Control { &self, canvas: &CanvasData, _: &mut AppState, - cmd_buffer: &mut WlxCommandBuffer, + cmd_buffer: &mut WlxCommandBuffer, ) { let pass = { let vertex_buffer = canvas.graphics.upload_verts( @@ -543,11 +536,11 @@ impl Control { self.rect.w, self.rect.h, ); - let set0 = canvas.pipeline_color.uniform_buffer( + let set0 = canvas.pipeline_bg_color.uniform_buffer( 0, vec![self.bg_color.x, self.bg_color.y, self.bg_color.z, 1.], ); - canvas.pipeline_color.create_pass( + canvas.pipeline_bg_color.create_pass( [canvas.width as _, canvas.height as _], vertex_buffer, canvas.graphics.quad_indices.clone(), @@ -562,7 +555,7 @@ impl Control { &self, canvas: &CanvasData, _: &mut AppState, - cmd_buffer: &mut WlxCommandBuffer, + cmd_buffer: &mut WlxCommandBuffer, strong: bool, ) { let vertex_buffer = canvas.graphics.upload_verts( @@ -573,7 +566,7 @@ impl Control { self.rect.w, self.rect.h, ); - let set0 = canvas.pipeline_color.uniform_buffer( + let set0 = canvas.pipeline_bg_color.uniform_buffer( 0, vec![ self.bg_color.x, @@ -582,7 +575,7 @@ impl Control { if strong { 0.5 } else { 0.3 }, ], ); - let pass = canvas.pipeline_color.create_pass( + let pass = canvas.pipeline_bg_color.create_pass( [canvas.width as _, canvas.height as _], vertex_buffer.clone(), canvas.graphics.quad_indices.clone(), @@ -596,7 +589,7 @@ impl Control { &self, canvas: &CanvasData, app: &mut AppState, - cmd_buffer: &mut WlxCommandBuffer, + cmd_buffer: &mut WlxCommandBuffer, ) { let mut cur_y = self.rect.y; for line in self.text.lines() { @@ -611,16 +604,16 @@ impl Control { glyph.width, glyph.height, ); - let set0 = canvas.pipeline_glyph.uniform_sampler( + let set0 = canvas.pipeline_fg_glyph.uniform_sampler( 0, ImageView::new_default(tex).unwrap(), Filter::Nearest, ); - let set1 = canvas.pipeline_glyph.uniform_buffer( + let set1 = canvas.pipeline_fg_glyph.uniform_buffer( 1, vec![self.fg_color.x, self.fg_color.y, self.fg_color.z, 1.], ); - let pass = canvas.pipeline_glyph.create_pass( + let pass = canvas.pipeline_fg_glyph.create_pass( [canvas.width as _, canvas.height as _], vertex_buffer, canvas.graphics.quad_indices.clone(), @@ -637,7 +630,7 @@ impl Control { &self, canvas: &CanvasData, app: &mut AppState, - cmd_buffer: &mut WlxCommandBuffer, + cmd_buffer: &mut WlxCommandBuffer, ) { let (w, h) = app .fc @@ -656,16 +649,16 @@ impl Control { glyph.width, glyph.height, ); - let set0 = canvas.pipeline_glyph.uniform_sampler( + let set0 = canvas.pipeline_fg_glyph.uniform_sampler( 0, ImageView::new_default(tex).unwrap(), Filter::Nearest, ); - let set1 = canvas.pipeline_glyph.uniform_buffer( + let set1 = canvas.pipeline_fg_glyph.uniform_buffer( 1, vec![self.fg_color.x, self.fg_color.y, self.fg_color.z, 1.], ); - let pass = canvas.pipeline_glyph.create_pass( + let pass = canvas.pipeline_fg_glyph.create_pass( [canvas.width as _, canvas.height as _], vertex_buffer, canvas.graphics.quad_indices.clone(), diff --git a/src/overlays/screen.rs b/src/overlays/screen.rs index 13f8abc..ec1bf60 100644 --- a/src/overlays/screen.rs +++ b/src/overlays/screen.rs @@ -8,10 +8,7 @@ use vulkano::{ buffer::Subbuffer, command_buffer::CommandBufferUsage, format::Format, - image::{ - view::ImageView, AttachmentImage, ImageAccess, ImageLayout, ImageViewAbstract, StorageImage, - }, - sampler::Filter, + image::{sampler::Filter, view::ImageView, Image, ImageLayout}, sync::GpuFuture, Handle, VulkanObject, }; @@ -32,7 +29,7 @@ use crate::{ }, graphics::{Vert2Uv, WlxGraphics, WlxPipeline}, hid::{MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT}, - shaders::{frag_sprite, vert_common}, + shaders::{frag_screen, vert_common}, state::{AppSession, AppState}, }; @@ -110,19 +107,11 @@ struct ScreenPipeline { graphics: Arc, pipeline: Arc, vertex_buffer: Subbuffer<[Vert2Uv]>, - target_layout: ImageLayout, - pub view: Arc>, } impl ScreenPipeline { - fn new(graphics: Arc, image: &StorageImage) -> Self { - let pipeline = graphics.create_pipeline( - vert_common::load(graphics.device.clone()).unwrap(), - frag_sprite::load(graphics.device.clone()).unwrap(), - Format::R8G8B8A8_UNORM, - ); - - let dim = image.dimensions().width_height(); + fn new(graphics: Arc, image: &Image) -> Self { + let dim = image.extent(); let vertex_buffer = graphics.upload_verts(dim[0] as _, dim[1] as _, 0.0, 0.0, dim[0] as _, dim[1] as _); @@ -131,26 +120,31 @@ impl ScreenPipeline { let view = ImageView::new_default(render_texture).unwrap(); + let pipeline = graphics.create_pipeline_with_layouts( + view, + vert_common::load(graphics.device.clone()).unwrap(), + frag_screen::load(graphics.device.clone()).unwrap(), + Format::R8G8B8A8_UNORM, + ImageLayout::ColorAttachmentOptimal, + ImageLayout::ColorAttachmentOptimal, + ); + Self { graphics, pipeline, vertex_buffer, - view, - target_layout: ImageLayout::Undefined, } } - fn render(&mut self, image: Arc) { - if image.inner().image.handle().as_raw() - == self.view.image().inner().image.handle().as_raw() - { + fn render(&mut self, image: Arc) { + if image.handle().as_raw() == self.pipeline.view.image().handle().as_raw() { return; } let mut command_buffer = self .graphics .create_command_buffer(CommandBufferUsage::OneTimeSubmit) - .begin(self.view.clone()); + .begin_render_pass(&self.pipeline); let set0 = self.pipeline.uniform_sampler( 0, @@ -158,7 +152,7 @@ impl ScreenPipeline { Filter::Linear, ); - let dim = self.view.dimensions().width_height(); + let dim = self.pipeline.view.image().extent(); let dim = [dim[0] as f32, dim[1] as f32]; let pass = self.pipeline.create_pass( @@ -169,35 +163,15 @@ impl ScreenPipeline { ); command_buffer.run_ref(&pass); - let image = self.view.image().inner().image.clone(); - - if self.target_layout == ImageLayout::TransferSrcOptimal { - self.graphics - .transition_layout( - image.clone(), - ImageLayout::TransferSrcOptimal, - ImageLayout::ColorAttachmentOptimal, - ) - .wait(None) - .unwrap(); - } - { - let mut exec = command_buffer.end_render().build_and_execute(); + let mut exec = command_buffer.end_render_pass().build_and_execute(); exec.flush().unwrap(); exec.cleanup_finished(); } + } - self.graphics - .transition_layout( - image, - ImageLayout::ColorAttachmentOptimal, - ImageLayout::TransferSrcOptimal, - ) - .wait(None) - .unwrap(); - - self.target_layout = ImageLayout::TransferSrcOptimal; + pub(super) fn view(&self) -> Arc { + self.pipeline.view.clone() } } @@ -205,7 +179,7 @@ pub struct ScreenRenderer { capture: Box, receiver: Option>, pipeline: Option, - last_frame: Option>, + last_image: Option>, } impl ScreenRenderer { @@ -218,7 +192,7 @@ impl ScreenRenderer { capture: Box::new(capture), receiver: None, pipeline: None, - last_frame: None, + last_image: None, }) } @@ -236,7 +210,7 @@ impl ScreenRenderer { capture: Box::new(capture), receiver: None, pipeline: None, - last_frame: None, + last_image: None, }) } } @@ -254,13 +228,13 @@ impl OverlayRenderer for ScreenRenderer { for frame in receiver.try_iter() { match frame { WlxFrame::Dmabuf(frame) => { - if let Ok(new) = app.graphics.dmabuf_texture(frame) { + if let Some(new) = app.graphics.dmabuf_texture(frame) { let pipeline = self .pipeline .get_or_insert_with(|| ScreenPipeline::new(app.graphics.clone(), &new)); - + log::info!("New frame"); pipeline.render(new); - self.last_frame = Some(pipeline.view.clone()); + self.last_image = Some(pipeline.view()); } } WlxFrame::MemFd(_frame) => { @@ -280,8 +254,8 @@ impl OverlayRenderer for ScreenRenderer { fn resume(&mut self, _app: &mut AppState) { self.capture.resume(); } - fn view(&mut self) -> Option> { - self.last_frame.take() + fn view(&mut self) -> Option> { + self.last_image.take() } } diff --git a/src/shaders/mod.rs b/src/shaders/mod.rs index 15e979a..0435779 100644 --- a/src/shaders/mod.rs +++ b/src/shaders/mod.rs @@ -106,3 +106,120 @@ pub mod frag_srgb { ", } } + +pub mod frag_screen { + vulkano_shaders::shader! { + ty: "fragment", + src: r"#version 310 es + precision highp float; + + layout (location = 0) in vec2 in_uv; + layout (location = 0) out vec4 out_color; + + layout (set = 0, binding = 0) uniform sampler2D in_texture; + + layout (set = 0, binding = 1) uniform ColorBlock { + uniform vec4 in_color; + }; + + + vec4 supersample(sampler2D tex, vec2 uv) { + float ddx = dFdx(uv.x); + float ddy = dFdy(uv.y); + float width = sqrt(ddx*ddx + ddy*ddy); + + ivec2 size = textureSize(tex, 0); + + ivec2 pixelWidth = ivec2(width * vec2(size)); + ivec2 xy = ivec2(uv * vec2(size)); + + ivec2 start = xy - pixelWidth/2; + ivec2 end = xy + pixelWidth/2; + + vec4 outColor = vec4(0.0); + int n = 0; + + for (int xSample = start.x; xSample <= end.x; xSample++) { + for (int ySample = start.y; ySample <= end.y; ySample++) { + n++; + outColor += texelFetch(tex, clamp(ivec2(xSample, ySample), ivec2(0), size), 0); + } + } + if (n > 0) { + return outColor / float(n); + } else { + return vec4(0.0); + } + } + + + float gaussian(float x, float t) { + float PI = 3.14159265358; + return exp(-x*x/(2.0 * t*t))/(sqrt(2.0*PI)*t); + } + + + float besselI0(float x) { + return 1.0 + pow(x, 2.0) * (0.25 + pow(x, 2.0) * (0.015625 + pow(x, 2.0) * (0.000434028 + pow(x, 2.0) * (6.78168e-6 + pow(x, 2.0) * (6.78168e-8 + pow(x, 2.0) * (4.7095e-10 + pow(x, 2.0) * (2.40281e-12 + pow(x, 2.0) * (9.38597e-15 + pow(x, 2.0) * (2.8969e-17 + 7.24226e-20 * pow(x, 2.0)))))))))); + } + + float kaiser(float x, float alpha) { + if (x > 1.0) { + return 0.0; + } + return besselI0(alpha * sqrt(1.0-x*x)); + } + + vec4 lowpassFilter(sampler2D tex, vec2 uv, float alpha) { + float PI = 3.14159265358; + + vec4 q = vec4(0.0); + + vec2 dx_uv = dFdx(uv); + vec2 dy_uv = dFdy(uv); + //float width = sqrt(max(dot(dx_uv, dx_uv), dot(dy_uv, dy_uv))); + vec2 width = abs(vec2(dx_uv.x, dy_uv.y)); + + + ivec2 size = textureSize(tex, 0); + + vec2 pixelWidth = floor(width * vec2(size)); + vec2 aspectRatio = normalize(pixelWidth); + + ivec2 xy = ivec2(uv * vec2(size)); + vec2 xyf = uv * vec2(size); + + pixelWidth = clamp(pixelWidth, vec2(1.0), vec2(2.0)); + + + ivec2 start = xy - ivec2(pixelWidth); + ivec2 end = xy + ivec2(pixelWidth); + + vec4 outColor = vec4(0.0); + + float qSum = 0.0; + + for (int v = start.y; v <= end.y; v++) { + for (int u = start.x; u <= end.x; u++) { + float kx = (xyf.x - float(u))/pixelWidth.x; + float ky = (xyf.y - float(v))/pixelWidth.y; + + //float lanczosValue = gaussian(kx, fcx); + float lanczosValue = kaiser(sqrt(kx*kx + ky*ky), alpha); + + q += texelFetch(tex, ivec2(u, v), 0) * lanczosValue; + qSum += lanczosValue; + } + } + + return q/qSum; + } + + void main() + { + out_color = lowpassFilter(in_texture, in_uv, 4.0); + out_color.a = 1.0; + } + ", + } +} diff --git a/src/shaders/src/screen.frag b/src/shaders/src/screen.frag new file mode 100644 index 0000000..50d657f --- /dev/null +++ b/src/shaders/src/screen.frag @@ -0,0 +1,111 @@ +#version 310 es +precision highp float; + +layout (location = 0) in vec2 in_uv; +layout (location = 0) out vec4 out_color; + +layout (set = 0, binding = 0) uniform sampler2D in_texture; + +layout (set = 0, binding = 1) uniform ColorBlock { + uniform vec4 in_color; +}; + + +vec4 supersample(sampler2D tex, vec2 uv) { + float ddx = dFdx(uv.x); + float ddy = dFdy(uv.y); + float width = sqrt(ddx*ddx + ddy*ddy); + + ivec2 size = textureSize(tex, 0); + + ivec2 pixelWidth = ivec2(width * vec2(size)); + ivec2 xy = ivec2(uv * vec2(size)); + + ivec2 start = xy - pixelWidth/2; + ivec2 end = xy + pixelWidth/2; + + vec4 outColor = vec4(0.0); + int n = 0; + + for (int xSample = start.x; xSample <= end.x; xSample++) { + for (int ySample = start.y; ySample <= end.y; ySample++) { + n++; + outColor += texelFetch(tex, clamp(ivec2(xSample, ySample), ivec2(0), size), 0); + } + } + if (n > 0) { + return outColor / float(n); + } else { + return vec4(0.0); + } +} + + +float gaussian(float x, float t) { + float PI = 3.14159265358; + return exp(-x*x/(2.0 * t*t))/(sqrt(2.0*PI)*t); +} + + +float besselI0(float x) { + return 1.0 + pow(x, 2.0) * (0.25 + pow(x, 2.0) * (0.015625 + pow(x, 2.0) * (0.000434028 + pow(x, 2.0) * (6.78168e-6 + pow(x, 2.0) * (6.78168e-8 + pow(x, 2.0) * (4.7095e-10 + pow(x, 2.0) * (2.40281e-12 + pow(x, 2.0) * (9.38597e-15 + pow(x, 2.0) * (2.8969e-17 + 7.24226e-20 * pow(x, 2.0)))))))))); +} + +float kaiser(float x, float alpha) { + if (x > 1.0) { + return 0.0; + } + return besselI0(alpha * sqrt(1.0-x*x)); +} + +vec4 lowpassFilter(sampler2D tex, vec2 uv, float alpha) { + float PI = 3.14159265358; + + vec4 q = vec4(0.0); + + vec2 dx_uv = dFdx(uv); + vec2 dy_uv = dFdy(uv); + //float width = sqrt(max(dot(dx_uv, dx_uv), dot(dy_uv, dy_uv))); + vec2 width = abs(vec2(dx_uv.x, dy_uv.y)); + + + ivec2 size = textureSize(tex, 0); + + vec2 pixelWidth = floor(width * vec2(size)); + vec2 aspectRatio = normalize(pixelWidth); + + ivec2 xy = ivec2(uv * vec2(size)); + vec2 xyf = uv * vec2(size); + + pixelWidth = clamp(pixelWidth, vec2(1.0), vec2(2.0)); + + + ivec2 start = xy - ivec2(pixelWidth); + ivec2 end = xy + ivec2(pixelWidth); + + vec4 outColor = vec4(0.0); + + float qSum = 0.0; + + for (int v = start.y; v <= end.y; v++) { + for (int u = start.x; u <= end.x; u++) { + float kx = fcFactor * (xyf.x - float(u))/pixelWidth.x; + float ky = fcFactor * (xyf.y - float(v))/pixelWidth.y; + + //float lanczosValue = gaussian(kx, fcx); + float lanczosValue = kaiser(sqrt(kx*kx + ky*ky), alpha); + + q += texelFetch(tex, ivec2(u, v), 0) * lanczosValue; + qSum += lanczosValue; + } + } + + return q/qSum; +} + +void main() +{ + out_color = lowpassFilter(in_texture, in_uv, 4.0); + out_color.a = 1.0; +} +