Skip to content

Commit 70de45d

Browse files
committed
feature: Add Direct3D9 FPS overlay counter example
1 parent 1e59f12 commit 70de45d

File tree

6 files changed

+317
-2
lines changed

6 files changed

+317
-2
lines changed

Directory.Build.props

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project>
2+
3+
<PropertyGroup Condition="'$(Configuration)' != ''">
4+
<ConfigurationGroup Condition="$(Configuration.EndsWith('Debug'))">Debug</ConfigurationGroup>
5+
<ConfigurationGroup Condition="$(Configuration.EndsWith('Release'))">Release</ConfigurationGroup>
6+
<ProjectDir>$(MSBuildThisFileDirectory)</ProjectDir>
7+
<SourceDir>$(ProjectDir)src\</SourceDir>
8+
<CommonPath>$(SourceDir)Common\src</CommonPath>
9+
<BinDir Condition="'$(BinDir)'==''">$(ProjectDir)bin/</BinDir>
10+
<OutputDir>$(BinDir)$(Configuration)/$(Platform)/</OutputDir>
11+
</PropertyGroup>
12+
13+
</Project>

Directory.Build.targets

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project>
2+
<PropertyGroup
3+
Condition="'$(OS)' == 'Windows_NT' and
4+
'$(TargetFrameworkIdentifier)' == '.NETCoreApp' and
5+
'$(SelfContained)' != 'true'"
6+
>
7+
<RunCommand Condition="'$(PlatformTarget)' == 'x86'">$(MSBuildProgramFiles32)\dotnet\dotnet</RunCommand>
8+
<RunCommand Condition="'$(PlatformTarget)' == 'x64'">$(ProgramW6432)\dotnet\dotnet</RunCommand>
9+
</PropertyGroup>
10+
</Project>

GraphicsHook.sln

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 15
44
VisualStudioVersion = 15.0.28307.136
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Direct3DCapture", "src\Direct3DCapture\Direct3DCapture.csproj", "{4A14506E-9C5E-43BC-87E4-34C08DEABDC3}"
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Direct3DCapture", "src\Direct3DCapture\Direct3DCapture.csproj", "{4A14506E-9C5E-43BC-87E4-34C08DEABDC3}"
77
EndProject
88
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E9D61240-9E2C-4BD5-BEF1-607D3422EAAF}"
99
EndProject
1010
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2FDEAA0B-9F04-4CB1-89CF-EF668584453A}"
1111
EndProject
12-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Direct3DCapture.Tests", "test\Direct3DCapture.Tests\Direct3DCapture.Tests.csproj", "{39FCDDD4-78AF-4F6F-BA40-724A5D1AE620}"
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Direct3DCapture.Tests", "test\Direct3DCapture.Tests\Direct3DCapture.Tests.csproj", "{39FCDDD4-78AF-4F6F-BA40-724A5D1AE620}"
13+
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DirectX.Direct3D9.Overlay", "src\DirectX.Direct3D9.Overlay\DirectX.Direct3D9.Overlay.csproj", "{A86B01F8-1FEA-485B-85D7-3941694EC0A4}"
1315
EndProject
1416
Global
1517
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -25,13 +27,18 @@ Global
2527
{39FCDDD4-78AF-4F6F-BA40-724A5D1AE620}.Debug|Any CPU.Build.0 = Debug|Any CPU
2628
{39FCDDD4-78AF-4F6F-BA40-724A5D1AE620}.Release|Any CPU.ActiveCfg = Release|Any CPU
2729
{39FCDDD4-78AF-4F6F-BA40-724A5D1AE620}.Release|Any CPU.Build.0 = Release|Any CPU
30+
{A86B01F8-1FEA-485B-85D7-3941694EC0A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31+
{A86B01F8-1FEA-485B-85D7-3941694EC0A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
32+
{A86B01F8-1FEA-485B-85D7-3941694EC0A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
33+
{A86B01F8-1FEA-485B-85D7-3941694EC0A4}.Release|Any CPU.Build.0 = Release|Any CPU
2834
EndGlobalSection
2935
GlobalSection(SolutionProperties) = preSolution
3036
HideSolutionNode = FALSE
3137
EndGlobalSection
3238
GlobalSection(NestedProjects) = preSolution
3339
{4A14506E-9C5E-43BC-87E4-34C08DEABDC3} = {E9D61240-9E2C-4BD5-BEF1-607D3422EAAF}
3440
{39FCDDD4-78AF-4F6F-BA40-724A5D1AE620} = {2FDEAA0B-9F04-4CB1-89CF-EF668584453A}
41+
{A86B01F8-1FEA-485B-85D7-3941694EC0A4} = {E9D61240-9E2C-4BD5-BEF1-607D3422EAAF}
3542
EndGlobalSection
3643
GlobalSection(ExtensibilityGlobals) = postSolution
3744
SolutionGuid = {15C4F934-433D-4655-9D2C-5E30E7A936C3}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
8+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
9+
<OutputPath>$(OutputDir)</OutputPath>
10+
</PropertyGroup>
11+
12+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
13+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
14+
<OutputPath>$(OutputDir)</OutputPath>
15+
</PropertyGroup>
16+
17+
<ItemGroup>
18+
<PackageReference Include="CoreHook" Version="1.0.3" />
19+
<PackageReference Include="SharpDX.Direct3D9" Version="4.2.0" />
20+
<PackageReference Include="SharpDX.Mathematics" Version="4.2.0" />
21+
</ItemGroup>
22+
23+
</Project>
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Runtime.InteropServices;
4+
using CoreHook;
5+
using SharpDX;
6+
using SharpDX.Direct3D9;
7+
8+
namespace DirectX.Direct3D9.Overlay
9+
{
10+
public class EntryPoint : IEntryPoint
11+
{
12+
private List<IntPtr> _d3DDeviceFunctions = new List<IntPtr>();
13+
private const int D3DDevice9FunctionCount = 119;
14+
15+
private Font _framesPerSecondFont;
16+
17+
private int _frameCount;
18+
private int _lastTickCount;
19+
private float _lastFrameRate;
20+
21+
private string _overlayText;
22+
23+
public EntryPoint(IContext context, string arg1) { }
24+
25+
public void Run(IContext context, string overlayText)
26+
{
27+
_overlayText = overlayText;
28+
29+
InitializeDeviceHook();
30+
while (true)
31+
{
32+
System.Threading.Thread.Sleep(30000);
33+
}
34+
}
35+
36+
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
37+
unsafe delegate int IDirect3DDevice9_PresentDelegate(IntPtr device, Rectangle* sourceRectangle,
38+
Rectangle* destRectangle, IntPtr destWindowOverride, IntPtr dirtyRegion);
39+
40+
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
41+
private delegate int IDirect3DDevice9_EndSceneDelegate(IntPtr device);
42+
43+
private IHook<IDirect3DDevice9_PresentDelegate> _d3DPresentHook;
44+
private IHook<IDirect3DDevice9_EndSceneDelegate> _d3DEndSceneHook;
45+
46+
public unsafe void InitializeDeviceHook()
47+
{
48+
_d3DDeviceFunctions = new List<IntPtr>();
49+
50+
using (var direct3D = new Direct3D())
51+
{
52+
using (var device = new Device(direct3D, 0, DeviceType.NullReference, IntPtr.Zero, CreateFlags.HardwareVertexProcessing, new PresentParameters() { BackBufferWidth = 1, BackBufferHeight = 1, DeviceWindowHandle = IntPtr.Zero }))
53+
{
54+
_d3DDeviceFunctions.AddRange(ReadVTableAddress(device.NativePointer, D3DDevice9FunctionCount));
55+
}
56+
}
57+
58+
_d3DEndSceneHook = HookFactory.CreateHook<IDirect3DDevice9_EndSceneDelegate>(_d3DDeviceFunctions[(int)FunctionOrdinals.EndScene],
59+
Detour_EndScene,
60+
this);
61+
62+
_d3DPresentHook = HookFactory.CreateHook<IDirect3DDevice9_PresentDelegate>(
63+
_d3DDeviceFunctions[(int)FunctionOrdinals.Present],
64+
Detour_Present,
65+
this);
66+
67+
_d3DEndSceneHook.ThreadACL.SetExclusiveACL(new int[1]);
68+
_d3DPresentHook.ThreadACL.SetExclusiveACL(new int[1]);
69+
}
70+
71+
private static IEnumerable<IntPtr> ReadVTableAddress(IntPtr vTableAddress, int vTableFunctionCount)
72+
{
73+
IntPtr[] addresses = new IntPtr[vTableFunctionCount];
74+
IntPtr vTable = Marshal.ReadIntPtr(vTableAddress);
75+
for (var i = 0; i < vTableFunctionCount; ++i)
76+
{
77+
addresses[i] = Marshal.ReadIntPtr(vTable, i * IntPtr.Size);
78+
}
79+
return addresses;
80+
}
81+
82+
private unsafe int Detour_Present(IntPtr device, Rectangle* sourceRectangle, Rectangle* destRectangle, IntPtr destWindowOverride, IntPtr dirtyRegion)
83+
{
84+
_frameCount++;
85+
86+
return _d3DPresentHook.Original(device, sourceRectangle, destRectangle, destWindowOverride, dirtyRegion);
87+
}
88+
89+
private int Detour_EndScene(IntPtr direct3DDevice)
90+
{
91+
Device dev = (Device)direct3DDevice;
92+
DrawFramesPerSecond(dev);
93+
94+
dev.EndScene();
95+
return Result.Ok.Code;
96+
}
97+
98+
private void DrawFramesPerSecond(Device device)
99+
{
100+
try
101+
{
102+
var tickCount = Environment.TickCount;
103+
if (Math.Abs(tickCount - _lastTickCount) > 1000)
104+
{
105+
_lastFrameRate = (float)_frameCount * 1000 / Math.Abs(tickCount - _lastTickCount);
106+
_frameCount = 0;
107+
_lastTickCount = tickCount;
108+
}
109+
if (_framesPerSecondFont == null)
110+
{
111+
_framesPerSecondFont = new Font(device, new FontDescription()
112+
{
113+
Height = 20,
114+
FaceName = "Arial",
115+
Italic = false,
116+
Width = 0,
117+
MipLevels = 1,
118+
CharacterSet = FontCharacterSet.Default,
119+
OutputPrecision = FontPrecision.Default,
120+
Quality = FontQuality.ClearTypeNatural,
121+
PitchAndFamily = FontPitchAndFamily.Default | FontPitchAndFamily.DontCare,
122+
Weight = FontWeight.Bold
123+
});
124+
}
125+
126+
_framesPerSecondFont.DrawText(null, $"{_lastFrameRate:N0} FPS", 0, 0, new ColorBGRA(244, 66, 86, 255));
127+
}
128+
catch (Exception e)
129+
{
130+
}
131+
}
132+
}
133+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace DirectX.Direct3D9.Overlay
6+
{
7+
public enum FunctionOrdinals : short
8+
{
9+
QueryInterface = 0,
10+
AddRef = 1,
11+
Release = 2,
12+
TestCooperativeLevel = 3,
13+
GetAvailableTextureMem = 4,
14+
EvictManagedResources = 5,
15+
GetDirect3D = 6,
16+
GetDeviceCaps = 7,
17+
GetDisplayMode = 8,
18+
GetCreationParameters = 9,
19+
SetCursorProperties = 10,
20+
SetCursorPosition = 11,
21+
ShowCursor = 12,
22+
CreateAdditionalSwapChain = 13,
23+
GetSwapChain = 14,
24+
GetNumberOfSwapChains = 15,
25+
Reset = 16,
26+
Present = 17,
27+
GetBackBuffer = 18,
28+
GetRasterStatus = 19,
29+
SetDialogBoxMode = 20,
30+
SetGammaRamp = 21,
31+
GetGammaRamp = 22,
32+
CreateTexture = 23,
33+
CreateVolumeTexture = 24,
34+
CreateCubeTexture = 25,
35+
CreateVertexBuffer = 26,
36+
CreateIndexBuffer = 27,
37+
CreateRenderTarget = 28,
38+
CreateDepthStencilSurface = 29,
39+
UpdateSurface = 30,
40+
UpdateTexture = 31,
41+
GetRenderTargetData = 32,
42+
GetFrontBufferData = 33,
43+
StretchRect = 34,
44+
ColorFill = 35,
45+
CreateOffscreenPlainSurface = 36,
46+
SetRenderTarget = 37,
47+
GetRenderTarget = 38,
48+
SetDepthStencilSurface = 39,
49+
GetDepthStencilSurface = 40,
50+
BeginScene = 41,
51+
EndScene = 42,
52+
Clear = 43,
53+
SetTransform = 44,
54+
GetTransform = 45,
55+
MultiplyTransform = 46,
56+
SetViewport = 47,
57+
GetViewport = 48,
58+
SetMaterial = 49,
59+
GetMaterial = 50,
60+
SetLight = 51,
61+
GetLight = 52,
62+
LightEnable = 53,
63+
GetLightEnable = 54,
64+
SetClipPlane = 55,
65+
GetClipPlane = 56,
66+
SetRenderState = 57,
67+
GetRenderState = 58,
68+
CreateStateBlock = 59,
69+
BeginStateBlock = 60,
70+
EndStateBlock = 61,
71+
SetClipStatus = 62,
72+
GetClipStatus = 63,
73+
GetTexture = 64,
74+
SetTexture = 65,
75+
GetTextureStageState = 66,
76+
SetTextureStageState = 67,
77+
GetSamplerState = 68,
78+
SetSamplerState = 69,
79+
ValidateDevice = 70,
80+
SetPaletteEntries = 71,
81+
GetPaletteEntries = 72,
82+
SetCurrentTexturePalette = 73,
83+
GetCurrentTexturePalette = 74,
84+
SetScissorRect = 75,
85+
GetScissorRect = 76,
86+
SetSoftwareVertexProcessing = 77,
87+
GetSoftwareVertexProcessing = 78,
88+
SetNPatchMode = 79,
89+
GetNPatchMode = 80,
90+
DrawPrimitive = 81,
91+
DrawIndexedPrimitive = 82,
92+
DrawPrimitiveUP = 83,
93+
DrawIndexedPrimitiveUP = 84,
94+
ProcessVertices = 85,
95+
CreateVertexDeclaration = 86,
96+
SetVertexDeclaration = 87,
97+
GetVertexDeclaration = 88,
98+
SetFVF = 89,
99+
GetFVF = 90,
100+
CreateVertexShader = 91,
101+
SetVertexShader = 92,
102+
GetVertexShader = 93,
103+
SetVertexShaderConstantF = 94,
104+
GetVertexShaderConstantF = 95,
105+
SetVertexShaderConstantI = 96,
106+
GetVertexShaderConstantI = 97,
107+
SetVertexShaderConstantB = 98,
108+
GetVertexShaderConstantB = 99,
109+
SetStreamSource = 100,
110+
GetStreamSource = 101,
111+
SetStreamSourceFreq = 102,
112+
GetStreamSourceFreq = 103,
113+
SetIndices = 104,
114+
GetIndices = 105,
115+
CreatePixelShader = 106,
116+
SetPixelShader = 107,
117+
GetPixelShader = 108,
118+
SetPixelShaderConstantF = 109,
119+
GetPixelShaderConstantF = 110,
120+
SetPixelShaderConstantI = 111,
121+
GetPixelShaderConstantI = 112,
122+
SetPixelShaderConstantB = 113,
123+
GetPixelShaderConstantB = 114,
124+
DrawRectPatch = 115,
125+
DrawTriPatch = 116,
126+
DeletePatch = 117,
127+
CreateQuery = 118
128+
}
129+
}

0 commit comments

Comments
 (0)