diff --git a/Cargo.lock b/Cargo.lock index 01f8b81..f86cd37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -462,6 +462,7 @@ dependencies = [ "image", "log", "parking_lot", + "rayon", "thiserror", "windows", ] diff --git a/Cargo.toml b/Cargo.toml index cd9a561..b9f62f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ categories = [ image = "0.24.7" log = "0.4.20" parking_lot = "0.12.1" +rayon = "1.8.0" thiserror = "1.0.49" windows = { version = "0.51.1", features = [ "Win32_System_WinRT_Graphics_Capture", diff --git a/src/buffer.rs b/src/buffer.rs index 0a1b168..9d82fcd 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,17 +1,31 @@ use std::alloc::Layout; /// To Send Raw Pointers Between Threads -pub struct SendPtr(pub *mut T); +pub struct SendSyncPtr(pub *mut T); -impl SendPtr { - pub fn new(ptr: *mut T) -> Self { +impl SendSyncPtr { + pub const fn new(ptr: *mut T) -> Self { Self(ptr) } } -unsafe impl Send for SendPtr {} +unsafe impl Send for SendSyncPtr {} +unsafe impl Sync for SendSyncPtr {} + +/// To Share Buffer Struct Between Threads +pub struct SendBuffer(pub T); + +impl SendBuffer { + pub const fn new(device: T) -> Self { + Self(device) + } +} + +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Send for SendBuffer {} /// To Save Pointer And It's Layout Together +#[derive(Clone, Copy)] pub struct Buffer { pub ptr: *mut u8, pub layout: Layout, diff --git a/src/d3d11.rs b/src/d3d11.rs index 76edc3c..1164258 100644 --- a/src/d3d11.rs +++ b/src/d3d11.rs @@ -23,11 +23,12 @@ use windows::{ pub struct SendDirectX(pub T); impl SendDirectX { - pub fn new(device: T) -> Self { + pub const fn new(device: T) -> Self { Self(device) } } +#[allow(clippy::non_send_fields_in_send_ty)] unsafe impl Send for SendDirectX {} /// Used To Handle Internal DirectX Errors @@ -66,7 +67,7 @@ pub fn create_d3d_device() -> Result<(ID3D11Device, ID3D11DeviceContext), Box Self { Self { + buffer, texture, frame_surface, context, width, height, - buffer, } } /// Get The Frame Width - pub fn width(&self) -> u32 { + #[must_use] + pub const fn width(&self) -> u32 { self.width } - // Get The Frame Height - pub fn height(&self) -> u32 { + /// Get The Frame Height + #[must_use] + pub const fn height(&self) -> u32 { self.height } /// Get The Frame Buffer - pub fn buffer(&self) -> Result<&[Rgba], Box> { + pub fn buffer(&mut self) -> Result<&[Rgba], Box> { // Copy The Real Texture To Copy Texture unsafe { self.context - .CopyResource(&self.texture, &self.frame_surface) + .CopyResource(&self.texture, &self.frame_surface); }; // Map The Texture To Enable CPU Access @@ -80,7 +90,7 @@ impl Frame { D3D11_MAP_READ, 0, Some(&mut mapped_resource), - )? + )?; }; // Create A Slice From The Bits @@ -95,24 +105,58 @@ impl Frame { } else { // There Is Padding So We Have To Work According To: // https://learn.microsoft.com/en-us/windows/win32/medfound/image-stride + + // Reallocate If Buffer Is Too Small + if self.buffer.layout.size() < (self.width * self.height * 4) as usize { + info!( + "Reallocating Buffer Size To {:.1}MB", + ((self.width * self.height * 4) as f32 / 1024.0 / 1024.0) + ); + + let new_cap = (self.width * self.height * 4) as usize; + let new_layout = Layout::array::(new_cap)?; + + assert!( + new_layout.size() <= isize::MAX as usize, + "Allocation too large" + ); + + unsafe { + let new_ptr = + alloc::realloc(self.buffer.ptr, self.buffer.layout, new_layout.size()); + + self.buffer.ptr = if new_ptr.is_null() { + alloc::handle_alloc_error(self.buffer.layout) + } else { + new_ptr + }; + + self.buffer.layout = new_layout; + }; + } + let row_size = self.width as usize * std::mem::size_of::(); + let send_sync_ptr = SendSyncPtr::new(self.buffer.ptr); + let send_sync_pdata = SendSyncPtr::new(mapped_resource.pData.cast::()); + + (0..self.height).into_par_iter().for_each(|i| { + let send_sync_ptr = &send_sync_ptr; + let send_sync_pdata = &send_sync_pdata; - for i in 0..self.height { unsafe { ptr::copy_nonoverlapping( - mapped_resource - .pData - .add((i * mapped_resource.RowPitch) as usize) - as *mut u8, - self.buffer.add(i as usize * row_size), + send_sync_pdata + .0 + .add((i * mapped_resource.RowPitch) as usize), + send_sync_ptr.0.add(i as usize * row_size), row_size, - ) + ); }; - } + }); unsafe { std::slice::from_raw_parts( - self.buffer as *mut Rgba, + self.buffer.ptr.cast::(), (self.width * self.height) as usize, ) } @@ -121,22 +165,12 @@ impl Frame { Ok(slice) } - // /// Get Part Of The Frame Buffer - // pub fn sub_buffer( - // &self, - // start_width: u32, - // start_height: u32, - // width: u32, - // height: u32, - // ) -> Result> { - // } - /// Save The Frame As An Image To Specified Path - pub fn save_as_image(&self, path: &str) -> Result<(), Box> { + pub fn save_as_image(&mut self, path: &str) -> Result<(), Box> { let buffer = self.buffer()?; let buf = unsafe { - std::slice::from_raw_parts(buffer.as_ptr() as *mut u8, mem::size_of_val(buffer)) + std::slice::from_raw_parts(buffer.as_ptr().cast::(), mem::size_of_val(buffer)) }; image::save_buffer(path, buf, self.width, self.height, ColorType::Rgba8)?; diff --git a/src/graphics_capture_api.rs b/src/graphics_capture_api.rs index 880a213..64746dd 100644 --- a/src/graphics_capture_api.rs +++ b/src/graphics_capture_api.rs @@ -30,7 +30,7 @@ use windows::{ }; use crate::{ - buffer::{Buffer, SendPtr}, + buffer::{Buffer, SendBuffer}, capture::WindowsCaptureHandler, d3d11::{create_d3d_device, create_direct3d_device, SendDirectX}, frame::Frame, @@ -78,9 +78,9 @@ impl GraphicsCaptureApi { return Err(Box::new(WindowsCaptureError::Unsupported)); } - // Allocate 128MB Of Memory - trace!("Allocating 128MB Of Memory"); - let layout = Layout::new::<[u8; 128 * 1024 * 1024]>(); + // Allocate 8MB Of Memory + trace!("Allocating 8MB Of Memory"); + let layout = Layout::new::<[u8; 8 * 1024 * 1024]>(); let ptr = unsafe { alloc::alloc(layout) }; if ptr.is_null() { alloc::handle_alloc_error(layout); @@ -98,7 +98,7 @@ impl GraphicsCaptureApi { let frame_pool = Direct3D11CaptureFramePool::Create( &direct3d_device, DirectXPixelFormat::R8G8B8A8UIntNormalized, - 2, + 1, item.Size()?, )?; let frame_pool = Arc::new(frame_pool); @@ -137,7 +137,7 @@ impl GraphicsCaptureApi { &TypedEventHandler::::new({ // Init let frame_pool_recreate = frame_pool.clone(); - let closed_frame_pool = closed.clone(); + let closed_frame_pool = closed; let d3d_device_frame_pool = d3d_device.clone(); let context = d3d_device_context.clone(); @@ -145,8 +145,7 @@ impl GraphicsCaptureApi { let callback_frame_arrived = callback; let direct3d_device_recreate = SendDirectX::new(direct3d_device.clone()); - let buffer = SendPtr::new(buffer.ptr); - + let buffer = SendBuffer::new(buffer); move |frame, _| { // Return Early If The Capture Is Closed if closed_frame_pool.load(atomic::Ordering::Relaxed) { @@ -188,7 +187,7 @@ impl GraphicsCaptureApi { .Recreate( &direct3d_device_recreate.0, DirectXPixelFormat::R8G8B8A8UIntNormalized, - 2, + 1, frame_content_size, ) .unwrap(); @@ -226,7 +225,7 @@ impl GraphicsCaptureApi { &texture_desc, None, Some(&mut texture), - )? + )?; }; let texture = texture.unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 3b88e78..5c7067f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,6 +87,14 @@ //! //! Capture::start(settings).unwrap(); //! ``` +#![feature(strict_provenance)] +#![warn(clippy::semicolon_if_nothing_returned)] +#![warn(clippy::inconsistent_struct_constructor)] +#![warn(clippy::must_use_candidate)] +#![warn(clippy::ptr_as_ptr)] +#![warn(clippy::borrow_as_ptr)] +#![warn(clippy::nursery)] +#![warn(clippy::cargo)] mod buffer; pub mod capture; diff --git a/src/monitor.rs b/src/monitor.rs index 6f445c8..33655ff 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -25,6 +25,7 @@ pub struct Monitor { impl Monitor { /// Get The Primary Monitor + #[must_use] pub fn primary() -> Self { let point = POINT { x: 0, y: 0 }; let monitor = unsafe { MonitorFromPoint(point, MONITOR_DEFAULTTOPRIMARY) }; @@ -52,20 +53,22 @@ impl Monitor { None, None, Some(Self::enum_monitors_callback), - LPARAM(&mut monitors as *mut Vec as isize), + LPARAM(std::ptr::addr_of_mut!(monitors) as isize), ) - .ok()? + .ok()?; }; Ok(monitors) } /// Create From A Raw HMONITOR + #[must_use] pub const fn from_raw_hmonitor(monitor: HMONITOR) -> Self { Self { monitor } } /// Get The Raw HMONITOR + #[must_use] pub const fn as_raw_hmonitor(&self) -> HMONITOR { self.monitor } @@ -93,7 +96,7 @@ impl TryFrom for GraphicsCaptureItem { // Get Capture Item From HMONITOR let monitor = value.as_raw_hmonitor(); - let interop = windows::core::factory::()?; + let interop = windows::core::factory::()?; Ok(unsafe { interop.CreateForMonitor(monitor)? }) } } diff --git a/src/window.rs b/src/window.rs index a747f68..989acd3 100644 --- a/src/window.rs +++ b/src/window.rs @@ -67,10 +67,7 @@ impl Window { } } - match target_window { - Some(window) => Ok(window), - None => Err(Box::new(WindowErrors::NotFound)), - } + Ok(target_window.map_or_else(|| Err(Box::new(WindowErrors::NotFound)), Ok)?) } /// Get Window Title @@ -89,6 +86,7 @@ impl Window { } /// Check If The Window Is A Valid Window + #[must_use] pub fn is_window_valid(window: HWND) -> bool { if !unsafe { IsWindowVisible(window).as_bool() } { return false; @@ -121,16 +119,16 @@ impl Window { } /// Get A List Of All Windows - pub fn enumerate() -> Result, Box> { - let mut windows: Vec = Vec::new(); + pub fn enumerate() -> Result, Box> { + let mut windows: Vec = Vec::new(); unsafe { EnumChildWindows( GetDesktopWindow(), Some(Self::enum_windows_callback), - LPARAM(&mut windows as *mut Vec as isize), + LPARAM(std::ptr::addr_of_mut!(windows) as isize), ) - .ok()? + .ok()?; }; Ok(windows) @@ -146,18 +144,20 @@ impl Window { } /// Create From A Raw HWND + #[must_use] pub const fn from_raw_hwnd(window: HWND) -> Self { Self { window } } /// Get The Raw HWND + #[must_use] pub const fn as_raw_hwnd(&self) -> HWND { self.window } // Callback Used For Enumerating All Windows unsafe extern "system" fn enum_windows_callback(window: HWND, vec: LPARAM) -> BOOL { - let windows = &mut *(vec.0 as *mut Vec); + let windows = &mut *(vec.0 as *mut Vec); if Self::is_window_valid(window) { windows.push(Self { window }); @@ -175,7 +175,7 @@ impl TryFrom for GraphicsCaptureItem { // Get Capture Item From HWND let window = value.as_raw_hwnd(); - let interop = windows::core::factory::()?; + let interop = windows::core::factory::()?; Ok(unsafe { interop.CreateForWindow(window)? }) } }