diff --git a/wgui/src/gfx/mod.rs b/wgui/src/gfx/mod.rs index a6376bb..5f7a06d 100644 --- a/wgui/src/gfx/mod.rs +++ b/wgui/src/gfx/mod.rs @@ -202,24 +202,6 @@ impl WGfx { )?)) } - pub fn create_pipeline_procedural( - self: &Arc, - vert: Arc, - frag: Arc, - format: Format, - blend: Option, - topology: PrimitiveTopology, - ) -> anyhow::Result>> { - Ok(Arc::new(WGfxPipeline::new_procedural( - self.clone(), - vert, - frag, - format, - blend, - topology, - )?)) - } - pub fn create_gfx_command_buffer( self: &Arc, usage: CommandBufferUsage, diff --git a/wgui/src/gfx/pass.rs b/wgui/src/gfx/pass.rs index f1e83e9..fd1f8e2 100644 --- a/wgui/src/gfx/pass.rs +++ b/wgui/src/gfx/pass.rs @@ -2,135 +2,36 @@ use std::{marker::PhantomData, ops::Range, sync::Arc}; use smallvec::smallvec; use vulkano::{ - buffer::{BufferContents, IndexBuffer, Subbuffer}, + buffer::{BufferContents, Subbuffer}, command_buffer::{ AutoCommandBufferBuilder, CommandBufferInheritanceInfo, CommandBufferInheritanceRenderPassType, CommandBufferInheritanceRenderingInfo, CommandBufferUsage, SecondaryAutoCommandBuffer, }, - descriptor_set::DescriptorSet, + descriptor_set::{DescriptorSet, WriteDescriptorSet}, + image::{ + sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + view::ImageView, + }, pipeline::{ Pipeline, PipelineBindPoint, graphics::{vertex_input::Vertex, viewport::Viewport}, }, }; -use super::pipeline::WGfxPipeline; +use super::{WGfx, pipeline::WGfxPipeline}; pub struct WGfxPass { pub command_buffer: Arc, + graphics: Arc, + descriptor_sets: Vec>, _dummy: PhantomData, } -impl WGfxPass<()> { - pub(super) fn new_procedural( - pipeline: Arc>, - dimensions: [f32; 2], - vertices: Range, - instances: Range, - descriptor_sets: Vec>, - ) -> anyhow::Result { - let viewport = Viewport { - offset: [0.0, 0.0], - extent: dimensions, - depth_range: 0.0..=1.0, - }; - let pipeline_inner = pipeline.inner(); - let mut command_buffer = AutoCommandBufferBuilder::secondary( - pipeline.graphics.command_buffer_allocator.clone(), - pipeline.graphics.queue_gfx.queue_family_index(), - CommandBufferUsage::MultipleSubmit, - CommandBufferInheritanceInfo { - render_pass: Some(CommandBufferInheritanceRenderPassType::BeginRendering( - CommandBufferInheritanceRenderingInfo { - color_attachment_formats: vec![Some(pipeline.format)], - - ..Default::default() - }, - )), - ..Default::default() - }, - )?; - - unsafe { - command_buffer - .set_viewport(0, smallvec![viewport])? - .bind_pipeline_graphics(pipeline_inner)? - .bind_descriptor_sets( - PipelineBindPoint::Graphics, - pipeline.inner().layout().clone(), - 0, - descriptor_sets, - )? - .draw( - vertices.end - vertices.start, - instances.end - instances.start, - vertices.start, - instances.start, - )? - }; - - Ok(Self { - command_buffer: command_buffer.build()?, - _dummy: PhantomData, - }) - } -} - impl WGfxPass where V: BufferContents + Vertex, { - pub(super) fn new_indexed( - pipeline: Arc>, - dimensions: [f32; 2], - vertex_buffer: Subbuffer<[V]>, - index_buffer: IndexBuffer, - descriptor_sets: Vec>, - ) -> anyhow::Result { - let viewport = Viewport { - offset: [0.0, 0.0], - extent: dimensions, - depth_range: 0.0..=1.0, - }; - let pipeline_inner = pipeline.inner(); - let mut command_buffer = AutoCommandBufferBuilder::secondary( - pipeline.graphics.command_buffer_allocator.clone(), - pipeline.graphics.queue_gfx.queue_family_index(), - CommandBufferUsage::MultipleSubmit, - CommandBufferInheritanceInfo { - render_pass: Some(CommandBufferInheritanceRenderPassType::BeginRendering( - CommandBufferInheritanceRenderingInfo { - color_attachment_formats: vec![Some(pipeline.format)], - - ..Default::default() - }, - )), - ..Default::default() - }, - )?; - - unsafe { - command_buffer - .set_viewport(0, smallvec![viewport])? - .bind_pipeline_graphics(pipeline_inner)? - .bind_descriptor_sets( - PipelineBindPoint::Graphics, - pipeline.inner().layout().clone(), - 0, - descriptor_sets, - )? - .bind_vertex_buffers(0, vertex_buffer)? - .bind_index_buffer(index_buffer.clone())? - .draw_indexed(index_buffer.len() as u32, 1, 0, 0, 0)? - }; - - Ok(Self { - command_buffer: command_buffer.build()?, - _dummy: PhantomData, - }) - } - - pub(super) fn new_instanced( + pub(super) fn new( pipeline: Arc>, dimensions: [f32; 2], vertex_buffer: Subbuffer<[V]>, @@ -168,7 +69,7 @@ where PipelineBindPoint::Graphics, pipeline.inner().layout().clone(), 0, - descriptor_sets, + descriptor_sets.clone(), )? .bind_vertex_buffers(0, vertex_buffer)? .draw( @@ -181,7 +82,35 @@ where Ok(Self { command_buffer: command_buffer.build()?, + graphics: pipeline.graphics.clone(), + descriptor_sets, _dummy: PhantomData, }) } + + pub fn update_sampler( + &self, + set: usize, + texture: Arc, + filter: Filter, + ) -> anyhow::Result<()> { + let sampler = Sampler::new( + self.graphics.device.clone(), + SamplerCreateInfo { + mag_filter: filter, + min_filter: filter, + address_mode: [SamplerAddressMode::Repeat; 3], + ..Default::default() + }, + )?; + + unsafe { + self.descriptor_sets[set].update_by_ref( + [WriteDescriptorSet::image_view_sampler(0, texture, sampler)], + [], + )?; + } + + Ok(()) + } } diff --git a/wgui/src/gfx/pipeline.rs b/wgui/src/gfx/pipeline.rs index 0fcaa0a..425e97e 100644 --- a/wgui/src/gfx/pipeline.rs +++ b/wgui/src/gfx/pipeline.rs @@ -3,7 +3,7 @@ use std::{marker::PhantomData, ops::Range, sync::Arc}; use smallvec::smallvec; use vulkano::{ buffer::{ - BufferContents, BufferUsage, IndexBuffer, Subbuffer, + BufferContents, BufferUsage, Subbuffer, allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}, }, descriptor_set::{DescriptorSet, WriteDescriptorSet}, @@ -177,46 +177,6 @@ where } } -impl WGfxPipeline<()> { - pub(super) fn new_procedural( - graphics: Arc, - vert: Arc, - frag: Arc, - format: Format, - blend: Option, - topology: PrimitiveTopology, - ) -> anyhow::Result { - let vert_entry_point = vert.entry_point("main").unwrap(); // want panic - let frag_entry_point = frag.entry_point("main").unwrap(); // want panic - - WGfxPipeline::new_from_stages( - graphics, - format, - blend, - topology, - vert_entry_point, - frag_entry_point, - None, - ) - } - - pub fn create_pass_procedural( - self: &Arc, - dimensions: [f32; 2], - vertices: Range, - instances: Range, - descriptor_sets: Vec>, - ) -> anyhow::Result> { - WGfxPass::new_procedural( - self.clone(), - dimensions, - vertices, - instances, - descriptor_sets, - ) - } -} - impl WGfxPipeline where V: BufferContents + Vertex, @@ -258,7 +218,7 @@ where instances: Range, descriptor_sets: Vec>, ) -> anyhow::Result> { - WGfxPass::new_instanced( + WGfxPass::new( self.clone(), dimensions, vertex_buffer, @@ -267,20 +227,4 @@ where descriptor_sets, ) } - - pub fn create_pass_indexed( - self: &Arc, - dimensions: [f32; 2], - vertex_buffer: Subbuffer<[V]>, - index_buffer: IndexBuffer, - descriptor_sets: Vec>, - ) -> anyhow::Result> { - WGfxPass::new_indexed( - self.clone(), - dimensions, - vertex_buffer, - index_buffer, - descriptor_sets, - ) - } } diff --git a/wgui/src/renderer_vk/rect.rs b/wgui/src/renderer_vk/rect.rs index 617e44c..263548f 100644 --- a/wgui/src/renderer_vk/rect.rs +++ b/wgui/src/renderer_vk/rect.rs @@ -141,7 +141,6 @@ impl RectRenderer { let set0 = viewport.get_rect_descriptor(&self.pipeline); let set1 = self.model_buffer.get_rect_descriptor(&self.pipeline); - let pass = self.pipeline.color_rect.create_pass( [vp[0] as _, vp[1] as _], self.vert_buffer.clone(), diff --git a/wlx-overlay-s/src/backend/openxr/lines.rs b/wlx-overlay-s/src/backend/openxr/lines.rs index 0202578..90fbf70 100644 --- a/wlx-overlay-s/src/backend/openxr/lines.rs +++ b/wlx-overlay-s/src/backend/openxr/lines.rs @@ -13,7 +13,7 @@ use wgui::gfx::{WGfx, pass::WGfxPass, pipeline::WGfxPipeline}; use crate::{ backend::openxr::helpers, - graphics::{CommandBuffers, ExtentExt, Vert2Uv}, + graphics::{CommandBuffers, Vert2Uv}, state::AppState, }; use vulkano::{ diff --git a/wlx-overlay-s/src/graphics/mod.rs b/wlx-overlay-s/src/graphics/mod.rs index 4232e1f..6c9d0e7 100644 --- a/wlx-overlay-s/src/graphics/mod.rs +++ b/wlx-overlay-s/src/graphics/mod.rs @@ -9,7 +9,7 @@ use std::{ use glam::{Vec2, vec2}; use vulkano::{ buffer::{BufferCreateInfo, BufferUsage}, - command_buffer::{PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract}, + command_buffer::{CommandBufferUsage, PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract}, image::view::ImageView, memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}, sync::GpuFuture, @@ -72,10 +72,11 @@ pub struct WGfxExtras { pub drm_formats: Vec, pub queue_capture: Option>, pub quad_verts: Vert2Buf, + pub fallback_image: Arc, } impl WGfxExtras { - pub fn new(gfx: &WGfx, queue_capture: Option>) -> anyhow::Result { + pub fn new(gfx: Arc, queue_capture: Option>) -> anyhow::Result { let mut shaders = HashMap::new(); let shader = vert_quad::load(gfx.device.clone())?; @@ -127,11 +128,19 @@ impl WGfxExtras { vertices.into_iter(), )?; + let mut cmd_xfer = gfx.create_xfer_command_buffer(CommandBufferUsage::OneTimeSubmit)?; + let fallback_image = + cmd_xfer.upload_image(1, 1, Format::R8G8B8A8_SRGB, &[255, 0, 255, 255])?; + cmd_xfer.build_and_execute_now()?; + + let fallback_image = ImageView::new_default(fallback_image)?; + Ok(Self { shaders, drm_formats, queue_capture, quad_verts, + fallback_image, }) } } @@ -354,7 +363,7 @@ pub fn init_openxr_graphics( queue_xfer, Format::R8G8B8A8_SRGB, ); - let extras = WGfxExtras::new(&gfx, queue_capture)?; + let extras = WGfxExtras::new(gfx.clone(), queue_capture)?; Ok((gfx, extras)) } @@ -471,7 +480,7 @@ pub fn init_openvr_graphics( queue_xfer, Format::R8G8B8A8_SRGB, ); - let extras = WGfxExtras::new(&gfx, queue_capture)?; + let extras = WGfxExtras::new(gfx.clone(), queue_capture)?; Ok((gfx, extras)) } diff --git a/wlx-overlay-s/src/overlays/screen.rs b/wlx-overlay-s/src/overlays/screen.rs index ae0966a..51b6401 100644 --- a/wlx-overlay-s/src/overlays/screen.rs +++ b/wlx-overlay-s/src/overlays/screen.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use std::{ f32::consts::PI, ptr, - sync::{atomic::AtomicU64, Arc, LazyLock}, + sync::{Arc, LazyLock, atomic::AtomicU64}, time::Instant, }; use vulkano::{ @@ -11,15 +11,15 @@ use vulkano::{ command_buffer::CommandBufferUsage, device::Queue, format::Format, - image::{sampler::Filter, view::ImageView, Image}, + image::{Image, sampler::Filter, view::ImageView}, pipeline::graphics::{color_blend::AttachmentBlend, input_assembly::PrimitiveTopology}, }; -use wgui::gfx::{cmd::XferCommandBuffer, pass::WGfxPass, pipeline::WGfxPipeline, WGfx}; +use wgui::gfx::{WGfx, cmd::XferCommandBuffer, pass::WGfxPass, pipeline::WGfxPipeline}; use wlx_capture::frame as wlx_frame; use wlx_capture::{ - frame::{FrameFormat, MouseMeta, WlxFrame}, WlxCapture, + frame::{FrameFormat, MouseMeta, WlxFrame}, }; #[cfg(feature = "pipewire")] @@ -38,7 +38,7 @@ use wlx_capture::pipewire::PipewireStream; use { crate::config::AStrMapExt, wlx_capture::{ - wayland::{wayland_client::protocol::wl_output, WlxClient, WlxOutput}, + wayland::{WlxClient, WlxOutput, wayland_client::protocol::wl_output}, wlr_dmabuf::WlrDmabufCapture, wlr_screencopy::WlrScreencopyCapture, }, @@ -47,7 +47,7 @@ use { #[cfg(feature = "x11")] use wlx_capture::xshm::{XshmCapture, XshmScreen}; -use glam::{vec2, vec3a, Affine2, Affine3A, Quat, Vec2, Vec3}; +use glam::{Affine2, Affine3A, Quat, Vec2, Vec3, vec2, vec3a}; use crate::{ backend::{ @@ -57,10 +57,11 @@ use crate::{ SplitOverlayBackend, }, }, - config::{def_pw_tokens, GeneralConfig, PwTokenMap}, + config::{GeneralConfig, PwTokenMap, def_pw_tokens}, graphics::{ - dmabuf::{fourcc_to_vk, WGfxDmabuf}, - upload_quad_vertices, CommandBuffers, ExtentExt, Vert2Uv, + CommandBuffers, Vert2Uv, + dmabuf::{WGfxDmabuf, fourcc_to_vk}, + upload_quad_vertices, }, hid::{MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT}, state::{AppSession, AppState, KeyboardFocus, ScreenMeta}, @@ -166,12 +167,15 @@ struct MousePass { struct ScreenPipeline { mouse: Option, pipeline: Arc>, + pass: WGfxPass, buf_alpha: Subbuffer<[f32]>, extentf: [f32; 2], } impl ScreenPipeline { fn new(extent: &[u32; 3], app: &mut AppState) -> anyhow::Result { + let extentf = [extent[0] as f32, extent[1] as f32]; + let pipeline = app.gfx.create_pipeline( app.gfx_extras.shaders.get("vert_quad").unwrap().clone(), // want panic app.gfx_extras.shaders.get("frag_screen").unwrap().clone(), // want panic @@ -185,11 +189,25 @@ impl ScreenPipeline { .gfx .empty_buffer(BufferUsage::TRANSFER_DST | BufferUsage::UNIFORM_BUFFER, 1)?; - let extentf = [extent[0] as f32, extent[1] as f32]; + let set0 = pipeline.uniform_sampler( + 0, + app.gfx_extras.fallback_image.clone(), + app.gfx.texture_filter, + )?; + let set1 = pipeline.buffer(1, buf_alpha.clone())?; + + let pass = pipeline.create_pass( + extentf, + app.gfx_extras.quad_verts.clone(), + 0..4, + 0..1, + vec![set0.clone(), set1], + )?; Ok(Self { mouse: None, pipeline, + pass, buf_alpha, extentf, }) @@ -243,26 +261,15 @@ impl ScreenPipeline { alpha: f32, ) -> anyhow::Result<()> { let view = ImageView::new_default(image)?; - let set0 = self - .pipeline - .uniform_sampler(0, view, app.gfx.texture_filter)?; + self.pass.update_sampler(0, view, app.gfx.texture_filter)?; self.buf_alpha.write()?[0] = alpha; - let set1 = self.pipeline.buffer(1, self.buf_alpha.clone())?; - let pass = self.pipeline.create_pass( - tgt.extent_f32(), - app.gfx_extras.quad_verts.clone(), - 0..4, - 0..1, - vec![set0, set1], - )?; - let mut cmd = app .gfx .create_gfx_command_buffer(CommandBufferUsage::OneTimeSubmit)?; cmd.begin_rendering(tgt)?; - cmd.run_ref(&pass)?; + cmd.run_ref(&self.pass)?; if let (Some(mouse), Some(pass)) = (mouse, self.mouse.as_mut()) { let size = CURSOR_SIZE * self.extentf[1]; @@ -542,20 +549,26 @@ impl OverlayRenderer for ScreenRenderer { let dmabuf_formats = if !supports_dmabuf { log::info!("Capture method does not support DMA-buf"); if app.gfx_extras.queue_capture.is_none() { - log::warn!("Current GPU does not support multiple queues. Software capture will take place on the main thread. Expect degraded performance."); + log::warn!( + "Current GPU does not support multiple queues. Software capture will take place on the main thread. Expect degraded performance." + ); } &Vec::new() } else if !allow_dmabuf { log::info!("Not using DMA-buf capture due to {capture_method}"); if app.gfx_extras.queue_capture.is_none() { - log::warn!("Current GPU does not support multiple queues. Software capture will take place on the main thread. Expect degraded performance."); + log::warn!( + "Current GPU does not support multiple queues. Software capture will take place on the main thread. Expect degraded performance." + ); } &Vec::new() } else { log::warn!( "Using DMA-buf capture. If screens are blank for you, switch to SHM using:" ); - log::warn!("echo 'capture_method: pw_fallback' > ~/.config/wlxoverlay/conf.d/pw_fallback.yaml"); + log::warn!( + "echo 'capture_method: pw_fallback' > ~/.config/wlxoverlay/conf.d/pw_fallback.yaml" + ); &app.gfx_extras.drm_formats }; diff --git a/wlx-overlay-s/src/overlays/wayvr.rs b/wlx-overlay-s/src/overlays/wayvr.rs index 1e85422..031750d 100644 --- a/wlx-overlay-s/src/overlays/wayvr.rs +++ b/wlx-overlay-s/src/overlays/wayvr.rs @@ -1,13 +1,14 @@ -use glam::{vec3a, Affine2, Vec3, Vec3A}; +use glam::{Affine2, Vec3, Vec3A, vec3a}; use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc}; use vulkano::{ + buffer::{BufferUsage, Subbuffer}, command_buffer::CommandBufferUsage, format::Format, - image::{view::ImageView, Image, ImageTiling, SubresourceLayout}, + image::{Image, ImageTiling, SubresourceLayout, view::ImageView}, pipeline::graphics::input_assembly::PrimitiveTopology, }; use wayvr_ipc::packet_server::{self, PacketServer, WvrStateChanged}; -use wgui::gfx::{pipeline::WGfxPipeline, WGfx}; +use wgui::gfx::{WGfx, pass::WGfxPass, pipeline::WGfxPipeline}; use wlx_capture::frame::{DmabufFrame, FourCC, FrameFormat, FramePlane}; use crate::{ @@ -15,18 +16,17 @@ use crate::{ common::{OverlayContainer, OverlaySelector}, input::{self, InteractionHandler}, overlay::{ - ui_transform, FrameMeta, OverlayData, OverlayID, OverlayRenderer, OverlayState, - ShouldRender, SplitOverlayBackend, Z_ORDER_DASHBOARD, + FrameMeta, OverlayData, OverlayID, OverlayRenderer, OverlayState, ShouldRender, + SplitOverlayBackend, Z_ORDER_DASHBOARD, ui_transform, }, task::TaskType, wayvr::{ - self, display, + self, WayVR, WayVRAction, WayVRDisplayClickAction, display, server_ipc::{gen_args_vec, gen_env_vec}, - WayVR, WayVRAction, WayVRDisplayClickAction, }, }, config_wayvr, - graphics::{dmabuf::WGfxDmabuf, CommandBuffers, ExtentExt, Vert2Uv}, + graphics::{CommandBuffers, Vert2Uv, dmabuf::WGfxDmabuf}, state::{self, AppState, KeyboardFocus}, }; @@ -183,6 +183,8 @@ struct ImageData { pub struct WayVRRenderer { pipeline: Arc>, + pass: WGfxPass, + buf_alpha: Subbuffer<[f32]>, image: Option, context: Rc>, graphics: Arc, @@ -205,8 +207,28 @@ impl WayVRRenderer { false, )?; + let buf_alpha = app + .gfx + .empty_buffer(BufferUsage::TRANSFER_DST | BufferUsage::UNIFORM_BUFFER, 1)?; + + let set0 = pipeline.uniform_sampler( + 0, + app.gfx_extras.fallback_image.clone(), + app.gfx.texture_filter, + )?; + let set1 = pipeline.buffer(1, buf_alpha.clone())?; + let pass = pipeline.create_pass( + [resolution[0] as _, resolution[1] as _], + app.gfx_extras.quad_verts.clone(), + 0..4, + 0..1, + vec![set0, set1], + )?; + Ok(Self { pipeline, + pass, + buf_alpha, context: Rc::new(RefCell::new(WayVRContext::new(wvr, display))), graphics: app.gfx.clone(), image: None, @@ -728,27 +750,15 @@ impl OverlayRenderer for WayVRRenderer { return Ok(false); }; - let set0 = self.pipeline.uniform_sampler( - 0, - image.vk_image_view.clone(), - app.gfx.texture_filter, - )?; - - let set1 = self.pipeline.uniform_buffer_upload(1, vec![alpha])?; - - let pass = self.pipeline.create_pass( - tgt.extent_f32(), - app.gfx_extras.quad_verts.clone(), - 0..4, - 0..1, - vec![set0, set1], - )?; + self.pass + .update_sampler(0, image.vk_image_view.clone(), self.graphics.texture_filter)?; + self.buf_alpha.write()?[0] = alpha; let mut cmd_buffer = app .gfx .create_gfx_command_buffer(CommandBufferUsage::OneTimeSubmit)?; cmd_buffer.begin_rendering(tgt)?; - cmd_buffer.run_ref(&pass)?; + cmd_buffer.run_ref(&self.pass)?; cmd_buffer.end_rendering()?; buf.push(cmd_buffer.build()?);