Skip to content

Commit 610d9da

Browse files
authored
Merge pull request #16 from DarthAffe/DataHandlingRework
Data handling rework
2 parents 9066366 + 57462e9 commit 610d9da

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+4560
-773
lines changed

ScreenCapture.NET/DirectX/DX11CompatibilityExtensions.cs renamed to ScreenCapture.NET.DX11/DX11CompatibilityExtensions.cs

File renamed without changes.

ScreenCapture.NET.DX11/DX11ScreenCapture.cs

Lines changed: 509 additions & 0 deletions
Large diffs are not rendered by default.

ScreenCapture.NET/DirectX/DX11ScreenCaptureService.cs renamed to ScreenCapture.NET.DX11/DX11ScreenCaptureService.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ public class DX11ScreenCaptureService : IScreenCaptureService
1212
#region Properties & Fields
1313

1414
private readonly IDXGIFactory1 _factory;
15-
1615
private readonly Dictionary<Display, DX11ScreenCapture> _screenCaptures = new();
1716

17+
private bool _isDisposed;
18+
1819
#endregion
1920

2021
#region Constructors
@@ -27,13 +28,17 @@ public DX11ScreenCaptureService()
2728
DXGI.CreateDXGIFactory1(out _factory!).CheckError();
2829
}
2930

31+
~DX11ScreenCaptureService() => Dispose();
32+
3033
#endregion
3134

3235
#region Methods
3336

3437
/// <inheritdoc />
3538
public IEnumerable<GraphicsCard> GetGraphicsCards()
3639
{
40+
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
41+
3742
int i = 0;
3843
while (_factory.EnumAdapters1(i, out IDXGIAdapter1 adapter).Success)
3944
{
@@ -46,6 +51,8 @@ public IEnumerable<GraphicsCard> GetGraphicsCards()
4651
/// <inheritdoc />
4752
public IEnumerable<Display> GetDisplays(GraphicsCard graphicsCard)
4853
{
54+
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
55+
4956
using IDXGIAdapter1? adapter = _factory.GetAdapter1(graphicsCard.Index);
5057

5158
int i = 0;
@@ -59,7 +66,7 @@ public IEnumerable<Display> GetDisplays(GraphicsCard graphicsCard)
5966
}
6067
}
6168

62-
private Rotation GetRotation(ModeRotation rotation) => rotation switch
69+
private static Rotation GetRotation(ModeRotation rotation) => rotation switch
6370
{
6471
ModeRotation.Rotate90 => Rotation.Rotation90,
6572
ModeRotation.Rotate180 => Rotation.Rotation180,
@@ -68,8 +75,11 @@ public IEnumerable<Display> GetDisplays(GraphicsCard graphicsCard)
6875
};
6976

7077
/// <inheritdoc />
71-
public IScreenCapture GetScreenCapture(Display display)
78+
IScreenCapture IScreenCaptureService.GetScreenCapture(Display display) => GetScreenCapture(display);
79+
public DX11ScreenCapture GetScreenCapture(Display display)
7280
{
81+
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
82+
7383
if (!_screenCaptures.TryGetValue(display, out DX11ScreenCapture? screenCapture))
7484
_screenCaptures.Add(display, screenCapture = new DX11ScreenCapture(_factory, display));
7585
return screenCapture;
@@ -78,13 +88,17 @@ public IScreenCapture GetScreenCapture(Display display)
7888
/// <inheritdoc />
7989
public void Dispose()
8090
{
91+
if (_isDisposed) return;
92+
8193
foreach (DX11ScreenCapture screenCapture in _screenCaptures.Values)
8294
screenCapture.Dispose();
8395
_screenCaptures.Clear();
8496

85-
_factory.Dispose();
97+
try { _factory.Dispose(); } catch { /**/ }
8698

8799
GC.SuppressFinalize(this);
100+
101+
_isDisposed = true;
88102
}
89103

90104
#endregion
File renamed without changes.
705 Bytes
Loading
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFrameworks>net7.0-windows;net6.0-windows</TargetFrameworks>
4+
<LangVersion>latest</LangVersion>
5+
<Nullable>enable</Nullable>
6+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
7+
8+
<Authors>Darth Affe</Authors>
9+
<Company>Wyrez</Company>
10+
<Language>en-US</Language>
11+
<NeutralLanguage>en-US</NeutralLanguage>
12+
<Title>ScreenCapture.NET.DX11</Title>
13+
<AssemblyName>ScreenCapture.NET.DX11</AssemblyName>
14+
<AssemblyTitle>ScreenCapture.NET.DX11</AssemblyTitle>
15+
<PackageId>ScreenCapture.NET.DX11</PackageId>
16+
<RootNamespace>ScreenCapture.NET</RootNamespace>
17+
<Description>Vortice based Screen-Capturing</Description>
18+
<Summary>Vortice based Screen-Capturing using Desktop Duplication</Summary>
19+
<Copyright>Copyright © Darth Affe 2023</Copyright>
20+
<PackageCopyright>Copyright © Darth Affe 2023</PackageCopyright>
21+
<PackageIcon>icon.png</PackageIcon>
22+
<PackageProjectUrl>https://github.com/DarthAffe/ScreenCapture.NET</PackageProjectUrl>
23+
<PackageLicenseExpression>LGPL-2.1-only</PackageLicenseExpression>
24+
<RepositoryType>Github</RepositoryType>
25+
<RepositoryUrl>https://github.com/DarthAffe/ScreenCapture.NET</RepositoryUrl>
26+
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
27+
28+
<PackageReleaseNotes>
29+
The downscale-level is now automatically limited if it would scale the image below a size of 1x1 px.
30+
This is change that can potentially break existing behavior but should only affect cases that are expected to crash with earlier versions.
31+
</PackageReleaseNotes>
32+
33+
<Version>1.3.2</Version>
34+
<AssemblyVersion>1.3.2</AssemblyVersion>
35+
<FileVersion>1.3.2</FileVersion>
36+
37+
<OutputPath>..\bin\</OutputPath>
38+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
39+
<IncludeSource>True</IncludeSource>
40+
<IncludeSymbols>True</IncludeSymbols>
41+
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
42+
</PropertyGroup>
43+
44+
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
45+
<DefineConstants>$(DefineConstants);TRACE;DEBUG</DefineConstants>
46+
<DebugSymbols>true</DebugSymbols>
47+
<DebugType>full</DebugType>
48+
<Optimize>false</Optimize>
49+
</PropertyGroup>
50+
51+
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
52+
<DebugType>portable</DebugType>
53+
<Optimize>true</Optimize>
54+
<NoWarn>$(NoWarn);CS1591;CS1572;CS1573</NoWarn>
55+
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
56+
</PropertyGroup>
57+
58+
<ItemGroup>
59+
<None Include="Resources\icon.png">
60+
<Pack>True</Pack>
61+
<PackagePath></PackagePath>
62+
</None>
63+
</ItemGroup>
64+
65+
<ItemGroup>
66+
<PackageReference Include="Vortice.Direct3D11" Version="3.2.0" />
67+
</ItemGroup>
68+
69+
<ItemGroup>
70+
<ProjectReference Include="..\ScreenCapture.NET\ScreenCapture.NET.csproj" />
71+
</ItemGroup>
72+
73+
</Project>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helper/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
using System;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
using System.Threading;
5+
using ScreenCapture.NET.Downscale;
6+
using SharpGen.Runtime;
7+
using Vortice.Direct3D9;
8+
9+
namespace ScreenCapture.NET;
10+
11+
/// <summary>
12+
/// Represents a ScreenCapture using DirectX 9.
13+
/// https://learn.microsoft.com/en-us/windows/win32/api/d3d9/nf-d3d9-idirect3ddevice9-getfrontbufferdata
14+
/// </summary>
15+
// ReSharper disable once InconsistentNaming
16+
public sealed class DX9ScreenCapture : AbstractScreenCapture<ColorBGRA>
17+
{
18+
#region Properties & Fields
19+
20+
private readonly object _captureLock = new();
21+
22+
private readonly IDirect3D9 _direct3D9;
23+
private IDirect3DDevice9? _device;
24+
private IDirect3DSurface9? _surface;
25+
private byte[]? _buffer;
26+
private int _stride;
27+
28+
#endregion
29+
30+
#region Constructors
31+
32+
/// <summary>
33+
/// Initializes a new instance of the <see cref="DX9ScreenCapture"/> class.
34+
/// </summary>
35+
/// <param name="direct3D9">The D3D9 instance used.</param>
36+
/// <param name="display">The <see cref="Display"/> to duplicate.</param>
37+
internal DX9ScreenCapture(IDirect3D9 direct3D9, Display display)
38+
: base(display)
39+
{
40+
this._direct3D9 = direct3D9;
41+
42+
Restart();
43+
}
44+
45+
#endregion
46+
47+
#region Methods
48+
49+
/// <inheritdoc />
50+
protected override bool PerformScreenCapture()
51+
{
52+
bool result = false;
53+
lock (_captureLock)
54+
{
55+
if ((_device == null) || (_surface == null) || (_buffer == null))
56+
{
57+
Restart();
58+
return false;
59+
}
60+
61+
try
62+
{
63+
_device.GetFrontBufferData(0, _surface);
64+
65+
LockedRectangle dr = _surface.LockRect(LockFlags.NoSystemLock | LockFlags.ReadOnly);
66+
67+
nint ptr = dr.DataPointer;
68+
for (int y = 0; y < Display.Height; y++)
69+
{
70+
Marshal.Copy(ptr, _buffer, y * _stride, _stride);
71+
ptr += dr.Pitch;
72+
}
73+
74+
_surface.UnlockRect();
75+
76+
result = true;
77+
}
78+
catch (SharpGenException dxException)
79+
{
80+
if (dxException.ResultCode == Result.AccessDenied)
81+
{
82+
try
83+
{
84+
Restart();
85+
}
86+
catch { Thread.Sleep(100); }
87+
}
88+
}
89+
}
90+
91+
return result;
92+
}
93+
94+
/// <inheritdoc />
95+
protected override void PerformCaptureZoneUpdate(CaptureZone<ColorBGRA> captureZone, in Span<byte> buffer)
96+
{
97+
if (_buffer == null) return;
98+
99+
using IDisposable @lock = captureZone.Lock();
100+
{
101+
if (captureZone.DownscaleLevel == 0)
102+
CopyZone(captureZone, buffer);
103+
else
104+
DownscaleZone(captureZone, buffer);
105+
}
106+
}
107+
108+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
109+
private void CopyZone(CaptureZone<ColorBGRA> captureZone, in Span<byte> buffer)
110+
{
111+
ReadOnlySpan<ColorBGRA> source = MemoryMarshal.Cast<byte, ColorBGRA>(_buffer);
112+
Span<ColorBGRA> target = MemoryMarshal.Cast<byte, ColorBGRA>(buffer);
113+
114+
int offsetX = captureZone.X;
115+
int offsetY = captureZone.Y;
116+
int width = captureZone.Width;
117+
int height = captureZone.Height;
118+
119+
for (int y = 0; y < height; y++)
120+
{
121+
int sourceOffset = ((y + offsetY) * Display.Width) + offsetX;
122+
int targetOffset = y * width;
123+
124+
source.Slice(sourceOffset, width).CopyTo(target.Slice(targetOffset, width));
125+
}
126+
}
127+
128+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
129+
private void DownscaleZone(CaptureZone<ColorBGRA> captureZone, in Span<byte> buffer)
130+
{
131+
ReadOnlySpan<byte> source = _buffer;
132+
Span<byte> target = buffer;
133+
134+
int blockSize = captureZone.DownscaleLevel switch
135+
{
136+
1 => 2,
137+
2 => 4,
138+
3 => 8,
139+
4 => 16,
140+
5 => 32,
141+
6 => 64,
142+
7 => 128,
143+
8 => 256,
144+
_ => (int)Math.Pow(2, captureZone.DownscaleLevel),
145+
};
146+
147+
int offsetX = captureZone.X;
148+
int offsetY = captureZone.Y;
149+
int width = captureZone.Width;
150+
int height = captureZone.Height;
151+
int stride = captureZone.Stride;
152+
int bpp = captureZone.ColorFormat.BytesPerPixel;
153+
int unscaledWith = captureZone.UnscaledWidth;
154+
155+
Span<byte> scaleBuffer = stackalloc byte[bpp];
156+
for (int y = 0; y < height; y++)
157+
for (int x = 0; x < width; x++)
158+
{
159+
AverageByteSampler.Sample(new SamplerInfo<byte>((x + offsetX) * blockSize, (y + offsetY) * blockSize, blockSize, blockSize, unscaledWith, bpp, source), scaleBuffer);
160+
161+
int targetOffset = (y * stride) + (x * bpp);
162+
163+
// DarthAffe 07.09.2023: Unroll as optimization since we know it's always 4 bpp - not ideal but it does quite a lot
164+
target[targetOffset] = scaleBuffer[0];
165+
target[targetOffset + 1] = scaleBuffer[1];
166+
target[targetOffset + 2] = scaleBuffer[2];
167+
target[targetOffset + 3] = scaleBuffer[3];
168+
}
169+
}
170+
171+
/// <inheritdoc />
172+
public override void Restart()
173+
{
174+
base.Restart();
175+
176+
lock (_captureLock)
177+
{
178+
DisposeDX();
179+
180+
try
181+
{
182+
PresentParameters presentParameters = new()
183+
{
184+
BackBufferWidth = Display.Width,
185+
BackBufferHeight = Display.Height,
186+
Windowed = true,
187+
SwapEffect = SwapEffect.Discard
188+
};
189+
_device = _direct3D9.CreateDevice(Display.Index, DeviceType.Hardware, IntPtr.Zero, CreateFlags.SoftwareVertexProcessing, presentParameters);
190+
_surface = _device.CreateOffscreenPlainSurface(Display.Width, Display.Height, Format.A8R8G8B8, Pool.Scratch);
191+
_stride = Display.Width * ColorBGRA.ColorFormat.BytesPerPixel;
192+
_buffer = new byte[Display.Height * _stride];
193+
}
194+
catch
195+
{
196+
DisposeDX();
197+
}
198+
}
199+
}
200+
201+
/// <inheritdoc />
202+
protected override void Dispose(bool disposing)
203+
{
204+
base.Dispose(disposing);
205+
206+
lock (_captureLock)
207+
DisposeDX();
208+
}
209+
210+
private void DisposeDX()
211+
{
212+
try
213+
{
214+
try { _surface?.Dispose(); } catch { /**/}
215+
try { _device?.Dispose(); } catch { /**/}
216+
_buffer = null;
217+
_device = null;
218+
_surface = null;
219+
}
220+
catch { /**/ }
221+
}
222+
223+
#endregion
224+
}

0 commit comments

Comments
 (0)