Skip to content

Commit 6ee7836

Browse files
committed
Add DirectX implementation for graphics
1 parent 1f9f7f4 commit 6ee7836

9 files changed

Lines changed: 251 additions & 11 deletions

File tree

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using OVRSharp.Exceptions;
2+
using SharpDX;
3+
using SharpDX.Direct3D11;
4+
using System;
5+
using System.Drawing;
6+
using System.Drawing.Imaging;
7+
using Valve.VR;
8+
9+
namespace OVRSharp.Graphics.DirectX
10+
{
11+
public class DirectXCompositor : ICompositorAPI
12+
{
13+
private readonly Device device;
14+
15+
public DirectXCompositor()
16+
{
17+
device = new Device(SharpDX.Direct3D.DriverType.Hardware, DeviceCreationFlags.Debug);
18+
}
19+
20+
public Bitmap GetMirrorImage(EVREye eye = EVREye.Eye_Left)
21+
{
22+
var srvPtr = IntPtr.Zero;
23+
24+
var result = OpenVR.Compositor.GetMirrorTextureD3D11(eye, device.NativePointer, ref srvPtr);
25+
if (result != EVRCompositorError.None)
26+
throw new OpenVRSystemException<EVRCompositorError>("Failed to get mirror texture from OpenVR", result);
27+
28+
var srv = new ShaderResourceView(srvPtr);
29+
var tex = srv.Resource.QueryInterface<Texture2D>();
30+
var texDesc = tex.Description;
31+
32+
var bitmap = new Bitmap(texDesc.Width, texDesc.Height);
33+
var boundsRect = new Rectangle(0, 0, texDesc.Width, texDesc.Height);
34+
35+
// Copy texture to CPU so we can read from it
36+
var cpuTex = new Texture2D(device, new Texture2DDescription
37+
{
38+
CpuAccessFlags = CpuAccessFlags.Read,
39+
BindFlags = BindFlags.None,
40+
Format = texDesc.Format,
41+
Width = texDesc.Width,
42+
Height = texDesc.Height,
43+
OptionFlags = ResourceOptionFlags.None,
44+
MipLevels = 1,
45+
ArraySize = 1,
46+
SampleDescription = { Count = 1, Quality = 0 },
47+
Usage = ResourceUsage.Staging
48+
});
49+
50+
device.ImmediateContext.CopyResource(tex, cpuTex);
51+
OpenVR.Compositor.ReleaseMirrorTextureD3D11(srvPtr);
52+
53+
var mapSource = device.ImmediateContext.MapSubresource(cpuTex, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None);
54+
var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
55+
var sourcePtr = mapSource.DataPointer;
56+
var destPtr = mapDest.Scan0;
57+
58+
for (int y = 0; y < texDesc.Height; y++)
59+
{
60+
Utilities.CopyMemory(destPtr, sourcePtr, texDesc.Width * 4);
61+
sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch);
62+
destPtr = IntPtr.Add(destPtr, mapDest.Stride);
63+
}
64+
65+
bitmap.UnlockBits(mapDest);
66+
device.ImmediateContext.UnmapSubresource(cpuTex, 0);
67+
cpuTex.Dispose();
68+
69+
FlipChannels(ref bitmap);
70+
return bitmap;
71+
}
72+
73+
private static void FlipChannels(ref Bitmap bitmap)
74+
{
75+
var bytesPerPixel = Image.GetPixelFormatSize(bitmap.PixelFormat) / 8;
76+
var data = bitmap.LockBits(
77+
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
78+
ImageLockMode.ReadWrite,
79+
bitmap.PixelFormat
80+
);
81+
82+
var length = System.Math.Abs(data.Stride) * bitmap.Height;
83+
84+
unsafe
85+
{
86+
byte* rgbValues = (byte*)data.Scan0.ToPointer();
87+
for (int i = 0; i < length; i += bytesPerPixel)
88+
{
89+
byte dummy = rgbValues[i];
90+
rgbValues[i] = rgbValues[i + 2];
91+
rgbValues[i + 2] = dummy;
92+
}
93+
}
94+
95+
bitmap.UnlockBits(data);
96+
}
97+
}
98+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net5.0</TargetFramework>
5+
<Authors>TJ Horner</Authors>
6+
<Description>DirectX implementation of OVRSharp graphics APIs</Description>
7+
<RepositoryUrl>https://github.com/OVRTools/OVRSharp</RepositoryUrl>
8+
<PackageProjectUrl>https://github.com/OVRTools/OVRSharp</PackageProjectUrl>
9+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
10+
<RepositoryType>git</RepositoryType>
11+
<PackageTags>openvr</PackageTags>
12+
<Copyright>2020-2021 TJ Horner</Copyright>
13+
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
14+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
15+
<Product>OVRSharp</Product>
16+
<VersionPrefix>1.1.0</VersionPrefix>
17+
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
18+
</PropertyGroup>
19+
20+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
21+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
22+
</PropertyGroup>
23+
24+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
25+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
26+
</PropertyGroup>
27+
28+
<ItemGroup>
29+
<PackageReference Include="SharpDX" Version="4.2.0" />
30+
<PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" />
31+
<PackageReference Include="System.Drawing.Common" Version="5.0.0" />
32+
</ItemGroup>
33+
34+
<ItemGroup>
35+
<ProjectReference Include="..\OVRSharp\OVRSharp.csproj" />
36+
</ItemGroup>
37+
38+
</Project>

OVRSharp.Graphics.OpenGL/OVRSharp.Graphics.OpenGL.csproj

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net5.0</TargetFramework>
4+
<TargetFramework>net5.0</TargetFramework>
5+
<Authors>TJ Horner</Authors>
6+
<Description>OpenGL implementation of OVRSharp graphics APIs</Description>
7+
<RepositoryUrl>https://github.com/OVRTools/OVRSharp</RepositoryUrl>
8+
<PackageProjectUrl>https://github.com/OVRTools/OVRSharp</PackageProjectUrl>
9+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
10+
<RepositoryType>git</RepositoryType>
11+
<PackageTags>openvr</PackageTags>
12+
<Copyright>2020-2021 TJ Horner</Copyright>
13+
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
14+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
15+
<Product>OVRSharp</Product>
16+
<VersionPrefix>1.1.0</VersionPrefix>
17+
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
518
</PropertyGroup>
619

720
<ItemGroup>

OVRSharp.Graphics.OpenGL/OpenGLCompositor.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class OpenGLCompositor : NativeWindow, ICompositorAPI
1515
public OpenGLCompositor() : base(
1616
new NativeWindowSettings
1717
{
18-
StartVisible = true,
18+
StartVisible = false,
1919
Title = "OVRSharp Window",
2020
WindowState = OpenTK.Windowing.Common.WindowState.Minimized
2121
}
@@ -24,12 +24,14 @@ public OpenGLCompositor() : base(
2424
public Bitmap GetMirrorImage(EVREye eye = EVREye.Eye_Left)
2525
{
2626
uint textureId = 0;
27-
var handle = IntPtr.Zero;
27+
var handle = new IntPtr();
2828

2929
var result = OpenVR.Compositor.GetMirrorTextureGL(eye, ref textureId, handle);
3030
if (result != EVRCompositorError.None)
3131
throw new OpenVRSystemException<EVRCompositorError>("Failed to get mirror texture from OpenVR", result);
3232

33+
OpenVR.Compositor.LockGLSharedTextureForAccess(handle);
34+
3335
GL.BindTexture(TextureTarget.Texture2d, new TextureHandle((int)textureId));
3436

3537
var height = 0;
@@ -46,9 +48,11 @@ public Bitmap GetMirrorImage(EVREye eye = EVREye.Eye_Left)
4648
GL.ReadPixels(0, 0, width, height, OpenTK.Graphics.OpenGLES3.PixelFormat.Rgb, PixelType.UnsignedByte, data.Scan0);
4749

4850
bitmap.UnlockBits(data);
49-
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
50-
51+
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
52+
53+
OpenVR.Compositor.UnlockGLSharedTextureForAccess(handle);
5154
OpenVR.Compositor.ReleaseSharedGLTexture(textureId, handle);
55+
5256
return bitmap;
5357
}
5458
}

OVRSharp.sln

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,19 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 16
44
VisualStudioVersion = 16.0.808.1
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OVRSharp", "OVRSharp\OVRSharp.csproj", "{DA0D98B4-9F23-414E-9B9F-CD8F113992BE}"
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OVRSharp", "OVRSharp\OVRSharp.csproj", "{DA0D98B4-9F23-414E-9B9F-CD8F113992BE}"
77
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OVRSharp.Graphics.OpenGL", "OVRSharp.Graphics.OpenGL\OVRSharp.Graphics.OpenGL.csproj", "{ABEE1C23-AC02-4E6E-AC02-EAA0205BE84B}"
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OVRSharp.Graphics.OpenGL", "OVRSharp.Graphics.OpenGL\OVRSharp.Graphics.OpenGL.csproj", "{ABEE1C23-AC02-4E6E-AC02-EAA0205BE84B}"
99
EndProject
10-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OVRSharp.Graphics.OpenGL.Tests", "tests\OVRSharp.Graphics.OpenGL.Tests\OVRSharp.Graphics.OpenGL.Tests.csproj", "{C78CB0B3-9DCF-4083-A234-C5FB2661A9B3}"
10+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OVRSharp.Graphics.OpenGL.Tests", "tests\OVRSharp.Graphics.OpenGL.Tests\OVRSharp.Graphics.OpenGL.Tests.csproj", "{C78CB0B3-9DCF-4083-A234-C5FB2661A9B3}"
11+
EndProject
12+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EDA24C40-CE58-429D-A903-19BDE6D024E6}"
13+
EndProject
14+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{AC203731-548C-4C7D-95BD-AAA6D7D288FC}"
15+
EndProject
16+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OVRSharp.Graphics.DirectX", "OVRSharp.Graphics.DirectX\OVRSharp.Graphics.DirectX.csproj", "{AF840E83-0D65-4558-8086-F19491B4532B}"
17+
EndProject
18+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OVRSharp.Graphics.DirectX.Tests", "tests\OVRSharp.Graphics.DirectX.Tests\OVRSharp.Graphics.DirectX.Tests.csproj", "{80860435-8D24-48BB-BA3A-25ECB6AA6E0A}"
1119
EndProject
1220
Global
1321
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -27,10 +35,25 @@ Global
2735
{C78CB0B3-9DCF-4083-A234-C5FB2661A9B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
2836
{C78CB0B3-9DCF-4083-A234-C5FB2661A9B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
2937
{C78CB0B3-9DCF-4083-A234-C5FB2661A9B3}.Release|Any CPU.Build.0 = Release|Any CPU
38+
{AF840E83-0D65-4558-8086-F19491B4532B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39+
{AF840E83-0D65-4558-8086-F19491B4532B}.Debug|Any CPU.Build.0 = Debug|Any CPU
40+
{AF840E83-0D65-4558-8086-F19491B4532B}.Release|Any CPU.ActiveCfg = Release|Any CPU
41+
{AF840E83-0D65-4558-8086-F19491B4532B}.Release|Any CPU.Build.0 = Release|Any CPU
42+
{80860435-8D24-48BB-BA3A-25ECB6AA6E0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43+
{80860435-8D24-48BB-BA3A-25ECB6AA6E0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
44+
{80860435-8D24-48BB-BA3A-25ECB6AA6E0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
45+
{80860435-8D24-48BB-BA3A-25ECB6AA6E0A}.Release|Any CPU.Build.0 = Release|Any CPU
3046
EndGlobalSection
3147
GlobalSection(SolutionProperties) = preSolution
3248
HideSolutionNode = FALSE
3349
EndGlobalSection
50+
GlobalSection(NestedProjects) = preSolution
51+
{DA0D98B4-9F23-414E-9B9F-CD8F113992BE} = {EDA24C40-CE58-429D-A903-19BDE6D024E6}
52+
{ABEE1C23-AC02-4E6E-AC02-EAA0205BE84B} = {EDA24C40-CE58-429D-A903-19BDE6D024E6}
53+
{C78CB0B3-9DCF-4083-A234-C5FB2661A9B3} = {AC203731-548C-4C7D-95BD-AAA6D7D288FC}
54+
{AF840E83-0D65-4558-8086-F19491B4532B} = {EDA24C40-CE58-429D-A903-19BDE6D024E6}
55+
{80860435-8D24-48BB-BA3A-25ECB6AA6E0A} = {AC203731-548C-4C7D-95BD-AAA6D7D288FC}
56+
EndGlobalSection
3457
GlobalSection(ExtensibilityGlobals) = postSolution
3558
SolutionGuid = {93984CAB-8CA9-430A-8166-5E2B339CA8A8}
3659
EndGlobalSection

OVRSharp/Exceptions/OpenVRSystemException.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
namespace OVRSharp.Exceptions
44
{
5-
public class OpenVRSystemException<ErrorType> : Exception
5+
public class OpenVRSystemException<TError> : Exception
66
{
7-
public readonly ErrorType Error;
7+
public readonly TError Error;
88

99
public OpenVRSystemException() : base() { }
1010
public OpenVRSystemException(string message) : base(message) { }
1111
public OpenVRSystemException(string message, Exception inner) : base(message, inner) { }
1212

13-
public OpenVRSystemException(string message, ErrorType error) : this($"{message} ({error})")
13+
public OpenVRSystemException(string message, TError error) : this($"{message} ({error})")
1414
{
1515
Error = error;
1616
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using FluentAssertions;
2+
using NUnit.Framework;
3+
using Valve.VR;
4+
5+
namespace OVRSharp.Graphics.DirectX.Tests
6+
{
7+
public class CompositorTests
8+
{
9+
private Application app;
10+
private ICompositorAPI compositor;
11+
12+
[OneTimeSetUp]
13+
public void Setup()
14+
{
15+
app = new Application(Application.ApplicationType.Background);
16+
compositor = new DirectXCompositor();
17+
}
18+
19+
[Test]
20+
[TestCase(EVREye.Eye_Left)]
21+
[TestCase(EVREye.Eye_Right)]
22+
public void ShouldGetMirrorTextureSuccessfully(EVREye eye)
23+
{
24+
var bitmap = compositor.GetMirrorImage(eye);
25+
bitmap.Height.Should().BeGreaterThan(0);
26+
bitmap.Width.Should().BeGreaterThan(0);
27+
}
28+
29+
// This test is mostly here to make sure we are deallocating resources properly.
30+
[Test]
31+
public void ShouldWithstandRapidCalls()
32+
{
33+
for (var i = 0; i < 1000; i++)
34+
{
35+
var bitmap = compositor.GetMirrorImage(EVREye.Eye_Left);
36+
bitmap.Height.Should().BeGreaterThan(0);
37+
bitmap.Width.Should().BeGreaterThan(0);
38+
39+
bitmap.Dispose();
40+
}
41+
}
42+
}
43+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net5.0</TargetFramework>
5+
6+
<IsPackable>false</IsPackable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="FluentAssertions" Version="5.10.3" />
11+
<PackageReference Include="NUnit" Version="3.12.0" />
12+
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<ProjectReference Include="..\..\OVRSharp.Graphics.DirectX\OVRSharp.Graphics.DirectX.csproj" />
18+
<ProjectReference Include="..\..\OVRSharp\OVRSharp.csproj" />
19+
</ItemGroup>
20+
21+
</Project>

0 commit comments

Comments
 (0)