Skip to content

Commit

Permalink
Optimization 💥
Browse files Browse the repository at this point in the history
	modified:   Cargo.lock
	modified:   Cargo.toml
	modified:   src/buffer.rs
	modified:   src/d3d11.rs
	modified:   src/frame.rs
	modified:   src/graphics_capture_api.rs
	modified:   src/lib.rs
	modified:   src/monitor.rs
	modified:   src/window.rs
  • Loading branch information
NiiightmareXD committed Oct 31, 2023
1 parent 8bb1665 commit 82c1aa2
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 60 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
22 changes: 18 additions & 4 deletions src/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
use std::alloc::Layout;

/// To Send Raw Pointers Between Threads
pub struct SendPtr<T>(pub *mut T);
pub struct SendSyncPtr<T>(pub *mut T);

impl<T> SendPtr<T> {
pub fn new(ptr: *mut T) -> Self {
impl<T> SendSyncPtr<T> {
pub const fn new(ptr: *mut T) -> Self {
Self(ptr)
}
}

unsafe impl<T> Send for SendPtr<T> {}
unsafe impl<T> Send for SendSyncPtr<T> {}
unsafe impl<T> Sync for SendSyncPtr<T> {}

/// To Share Buffer Struct Between Threads
pub struct SendBuffer<T>(pub T);

impl<T> SendBuffer<T> {
pub const fn new(device: T) -> Self {
Self(device)
}
}

#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl<T> Send for SendBuffer<T> {}

/// To Save Pointer And It's Layout Together
#[derive(Clone, Copy)]
pub struct Buffer {
pub ptr: *mut u8,
pub layout: Layout,
Expand Down
5 changes: 3 additions & 2 deletions src/d3d11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ use windows::{
pub struct SendDirectX<T>(pub T);

impl<T> SendDirectX<T> {
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<T> Send for SendDirectX<T> {}

/// Used To Handle Internal DirectX Errors
Expand Down Expand Up @@ -66,7 +67,7 @@ pub fn create_d3d_device() -> Result<(ID3D11Device, ID3D11DeviceContext), Box<dy
Some(&mut d3d_device),
Some(&mut feature_level),
Some(&mut d3d_device_context),
)?
)?;
};

if feature_level != D3D_FEATURE_LEVEL_11_1 {
Expand Down
96 changes: 65 additions & 31 deletions src/frame.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
use std::{mem, ptr};
use std::{
alloc::{self, Layout},
mem, ptr,
};

use image::ColorType;
use log::info;
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
use thiserror::Error;
use windows::Win32::Graphics::Direct3D11::{
ID3D11DeviceContext, ID3D11Texture2D, D3D11_MAPPED_SUBRESOURCE, D3D11_MAP_READ,
};

use crate::buffer::{Buffer, SendSyncPtr};

/// Used To Handle Internal Frame Errors
#[derive(Error, Eq, PartialEq, Clone, Copy, Debug)]
pub enum FrameError {
Expand All @@ -25,7 +32,7 @@ pub struct Rgba {

/// Frame Struct Used To Crop And Get The Frame Buffer
pub struct Frame {
buffer: *mut u8,
buffer: Buffer,
texture: ID3D11Texture2D,
frame_surface: ID3D11Texture2D,
context: ID3D11DeviceContext,
Expand All @@ -35,40 +42,43 @@ pub struct Frame {

impl Frame {
/// Craete A New Frame
#[must_use]
pub const fn new(
buffer: *mut u8,
buffer: Buffer,
texture: ID3D11Texture2D,
frame_surface: ID3D11Texture2D,
context: ID3D11DeviceContext,
width: u32,
height: u32,
) -> 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<dyn std::error::Error>> {
pub fn buffer(&mut self) -> Result<&[Rgba], Box<dyn std::error::Error>> {
// 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
Expand All @@ -80,7 +90,7 @@ impl Frame {
D3D11_MAP_READ,
0,
Some(&mut mapped_resource),
)?
)?;
};

// Create A Slice From The Bits
Expand All @@ -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::<u8>(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::<Rgba>();
let send_sync_ptr = SendSyncPtr::new(self.buffer.ptr);
let send_sync_pdata = SendSyncPtr::new(mapped_resource.pData.cast::<u8>());

(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::<Rgba>(),
(self.width * self.height) as usize,
)
}
Expand All @@ -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<FrameBuffer, Box<dyn std::error::Error>> {
// }

/// Save The Frame As An Image To Specified Path
pub fn save_as_image(&self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
pub fn save_as_image(&mut self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
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::<u8>(), mem::size_of_val(buffer))
};

image::save_buffer(path, buf, self.width, self.height, ColorType::Rgba8)?;
Expand Down
19 changes: 9 additions & 10 deletions src/graphics_capture_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -137,16 +137,15 @@ impl GraphicsCaptureApi {
&TypedEventHandler::<Direct3D11CaptureFramePool, IInspectable>::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();

let mut last_size = item.Size()?;
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) {
Expand Down Expand Up @@ -188,7 +187,7 @@ impl GraphicsCaptureApi {
.Recreate(
&direct3d_device_recreate.0,
DirectXPixelFormat::R8G8B8A8UIntNormalized,
2,
1,
frame_content_size,
)
.unwrap();
Expand Down Expand Up @@ -226,7 +225,7 @@ impl GraphicsCaptureApi {
&texture_desc,
None,
Some(&mut texture),
)?
)?;
};
let texture = texture.unwrap();

Expand Down
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 6 additions & 3 deletions src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) };
Expand Down Expand Up @@ -52,20 +53,22 @@ impl Monitor {
None,
None,
Some(Self::enum_monitors_callback),
LPARAM(&mut monitors as *mut Vec<HMONITOR> 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
}
Expand Down Expand Up @@ -93,7 +96,7 @@ impl TryFrom<Monitor> for GraphicsCaptureItem {
// Get Capture Item From HMONITOR
let monitor = value.as_raw_hmonitor();

let interop = windows::core::factory::<GraphicsCaptureItem, IGraphicsCaptureItemInterop>()?;
let interop = windows::core::factory::<Self, IGraphicsCaptureItemInterop>()?;
Ok(unsafe { interop.CreateForMonitor(monitor)? })
}
}
Loading

0 comments on commit 82c1aa2

Please sign in to comment.