Implement GPU Accelerated VR Overlay (#944)

This commit is contained in:
Ethan Cordray
2024-10-21 18:02:11 -04:00
committed by GitHub
parent 411f50969b
commit f9686ad803
3 changed files with 113 additions and 131 deletions
-1
View File
@@ -51,7 +51,6 @@ namespace VRCX
cefSettings.CefCommandLineArgs.Add("disable-pdf-extension"); cefSettings.CefCommandLineArgs.Add("disable-pdf-extension");
cefSettings.CefCommandLineArgs["autoplay-policy"] = "no-user-gesture-required"; cefSettings.CefCommandLineArgs["autoplay-policy"] = "no-user-gesture-required";
cefSettings.CefCommandLineArgs.Add("disable-web-security"); cefSettings.CefCommandLineArgs.Add("disable-web-security");
cefSettings.SetOffScreenRenderingBestPerformanceArgs(); // causes white screen sometimes?
if (WebApi.ProxySet) if (WebApi.ProxySet)
{ {
+64 -118
View File
@@ -10,116 +10,76 @@ using CefSharp.OffScreen;
using CefSharp.Structs; using CefSharp.Structs;
using SharpDX.Direct3D11; using SharpDX.Direct3D11;
using System; using System;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using SharpDX.Direct3D;
using SharpDX.Mathematics.Interop;
using Range = CefSharp.Structs.Range; using Range = CefSharp.Structs.Range;
namespace VRCX namespace VRCX
{ {
public class OffScreenBrowser : ChromiumWebBrowser, IRenderHandler public class OffScreenBrowser : ChromiumWebBrowser, IRenderHandler
{ {
private readonly ReaderWriterLockSlim _paintBufferLock; private Device _device;
private GCHandle _paintBuffer; private Device1 _device1;
private int _width; private DeviceMultithread _deviceMultithread;
private int _height; private Query _query;
private Texture2D _renderTarget;
public OffScreenBrowser(string address, int width, int height) public OffScreenBrowser(string address, int width, int height)
: base( : base(address, automaticallyCreateBrowser: false)
address,
new BrowserSettings()
{
DefaultEncoding = "UTF-8"
}
)
{ {
_paintBufferLock = new ReaderWriterLockSlim(); var windowInfo = new WindowInfo();
windowInfo.SetAsWindowless(IntPtr.Zero);
windowInfo.WindowlessRenderingEnabled = true;
windowInfo.SharedTextureEnabled = true;
windowInfo.Width = width;
windowInfo.Height = height;
var browserSettings = new BrowserSettings()
{
DefaultEncoding = "UTF-8",
WindowlessFrameRate = 60
};
CreateBrowser(windowInfo, browserSettings);
Size = new System.Drawing.Size(width, height); Size = new System.Drawing.Size(width, height);
RenderHandler = this; RenderHandler = this;
JavascriptBindings.ApplyVrJavascriptBindings(JavascriptObjectRepository); JavascriptBindings.ApplyVrJavascriptBindings(JavascriptObjectRepository);
} }
public void UpdateRender(Device device, Texture2D renderTarget)
{
_device = device;
_device1 = _device.QueryInterface<Device1>();
_deviceMultithread?.Dispose();
_deviceMultithread = _device.QueryInterfaceOrNull<DeviceMultithread>();
_deviceMultithread?.SetMultithreadProtected(true);
_renderTarget = renderTarget;
_query?.Dispose();
_query = new Query(_device, new QueryDescription
{
Type = QueryType.Event,
Flags = QueryFlags.None
});
}
public new void Dispose() public new void Dispose()
{ {
RenderHandler = null; RenderHandler = null;
base.Dispose(); base.Dispose();
_paintBufferLock.EnterWriteLock();
try
{
if (_paintBuffer.IsAllocated == true)
{
_paintBuffer.Free();
}
}
finally
{
_paintBufferLock.ExitWriteLock();
}
_paintBufferLock.Dispose();
}
public void RenderToTexture(Texture2D texture)
{
// Safeguard against uninitialized texture
if (texture == null)
return;
_paintBufferLock.EnterReadLock();
try
{
if (_width > 0 &&
_height > 0)
{
var context = texture.Device.ImmediateContext;
var dataBox = context.MapSubresource(
texture,
0,
MapMode.WriteDiscard,
MapFlags.None
);
if (dataBox.IsEmpty == false)
{
var sourcePtr = _paintBuffer.AddrOfPinnedObject();
var destinationPtr = dataBox.DataPointer;
var pitch = _width * 4;
var rowPitch = dataBox.RowPitch;
if (pitch == rowPitch)
{
WinApi.RtlCopyMemory(
destinationPtr,
sourcePtr,
(uint)(_width * _height * 4)
);
}
else
{
for (var y = _height; y > 0; --y)
{
WinApi.RtlCopyMemory(
destinationPtr,
sourcePtr,
(uint)pitch
);
sourcePtr += pitch;
destinationPtr += rowPitch;
}
}
}
context.UnmapSubresource(texture, 0);
}
}
finally
{
_paintBufferLock.ExitReadLock();
}
} }
ScreenInfo? IRenderHandler.GetScreenInfo() ScreenInfo? IRenderHandler.GetScreenInfo()
{ {
return null; return new ScreenInfo
{
DeviceScaleFactor = 1.0F
};
} }
bool IRenderHandler.GetScreenPoint(int viewX, int viewY, out int screenX, out int screenY) bool IRenderHandler.GetScreenPoint(int viewX, int viewY, out int screenX, out int screenY)
@@ -136,7 +96,24 @@ namespace VRCX
void IRenderHandler.OnAcceleratedPaint(PaintElementType type, Rect dirtyRect, AcceleratedPaintInfo paintInfo) void IRenderHandler.OnAcceleratedPaint(PaintElementType type, Rect dirtyRect, AcceleratedPaintInfo paintInfo)
{ {
// NOT USED if (type != PaintElementType.View)
return;
if (_device == null)
return;
using Texture2D cefTexture = _device1.OpenSharedResource1<Texture2D>(paintInfo.SharedTextureHandle);
_device.ImmediateContext.CopyResource(cefTexture, _renderTarget);
_device.ImmediateContext.End(_query);
_device.ImmediateContext.Flush();
RawBool q = _device.ImmediateContext.GetData<RawBool>(_query, AsynchronousFlags.DoNotFlush);
while (!q)
{
Thread.Yield();
q = _device.ImmediateContext.GetData<RawBool>(_query, AsynchronousFlags.DoNotFlush);
}
} }
void IRenderHandler.OnCursorChange(IntPtr cursor, CursorType type, CursorInfo customCursorInfo) void IRenderHandler.OnCursorChange(IntPtr cursor, CursorType type, CursorInfo customCursorInfo)
@@ -149,37 +126,6 @@ namespace VRCX
void IRenderHandler.OnPaint(PaintElementType type, Rect dirtyRect, IntPtr buffer, int width, int height) void IRenderHandler.OnPaint(PaintElementType type, Rect dirtyRect, IntPtr buffer, int width, int height)
{ {
if (type == PaintElementType.View)
{
_paintBufferLock.EnterWriteLock();
try
{
if (_width != width ||
_height != height)
{
_width = width;
_height = height;
if (_paintBuffer.IsAllocated == true)
{
_paintBuffer.Free();
}
_paintBuffer = GCHandle.Alloc(
new byte[_width * _height * 4],
GCHandleType.Pinned
);
}
WinApi.RtlCopyMemory(
_paintBuffer.AddrOfPinnedObject(),
buffer,
(uint)(width * height * 4)
);
}
finally
{
_paintBufferLock.ExitWriteLock();
}
}
} }
void IRenderHandler.OnPopupShow(bool show) void IRenderHandler.OnPopupShow(bool show)
+49 -12
View File
@@ -16,6 +16,10 @@ using SharpDX.Direct3D11;
using SharpDX.DXGI; using SharpDX.DXGI;
using Valve.VR; using Valve.VR;
using Device = SharpDX.Direct3D11.Device; using Device = SharpDX.Direct3D11.Device;
using Device1 = SharpDX.Direct3D11.Device1;
using Device2 = SharpDX.Direct3D11.Device2;
using Device3 = SharpDX.Direct3D11.Device3;
using Device4 = SharpDX.Direct3D11.Device4;
namespace VRCX namespace VRCX
{ {
@@ -87,7 +91,8 @@ namespace VRCX
{ {
Factory f = new Factory1(); Factory f = new Factory1();
_device = new Device(f.GetAdapter(OpenVR.System.GetD3D9AdapterIndex()), _device = new Device(f.GetAdapter(OpenVR.System.GetD3D9AdapterIndex()),
DeviceCreationFlags.SingleThreaded | DeviceCreationFlags.BgraSupport); DeviceCreationFlags.BgraSupport);
UpgradeDevice();
_texture1?.Dispose(); _texture1?.Dispose();
_texture1 = new Texture2D( _texture1 = new Texture2D(
@@ -100,12 +105,11 @@ namespace VRCX
ArraySize = 1, ArraySize = 1,
Format = Format.B8G8R8A8_UNorm, Format = Format.B8G8R8A8_UNorm,
SampleDescription = new SampleDescription(1, 0), SampleDescription = new SampleDescription(1, 0),
Usage = ResourceUsage.Dynamic, BindFlags = BindFlags.ShaderResource
BindFlags = BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.Write
} }
); );
_browser1?.UpdateRender(_device, _texture1);
_texture2?.Dispose(); _texture2?.Dispose();
_texture2 = new Texture2D( _texture2 = new Texture2D(
_device, _device,
@@ -117,11 +121,48 @@ namespace VRCX
ArraySize = 1, ArraySize = 1,
Format = Format.B8G8R8A8_UNorm, Format = Format.B8G8R8A8_UNorm,
SampleDescription = new SampleDescription(1, 0), SampleDescription = new SampleDescription(1, 0),
Usage = ResourceUsage.Dynamic, BindFlags = BindFlags.ShaderResource
BindFlags = BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.Write
} }
); );
_browser2?.UpdateRender(_device, _texture2);
}
private void UpgradeDevice()
{
Device5 device5 = _device.QueryInterfaceOrNull<Device5>();
if (device5 != null)
{
_device.Dispose();
_device = device5;
return;
}
Device4 device4 = _device.QueryInterfaceOrNull<Device4>();
if (device4 != null)
{
_device.Dispose();
_device = device4;
return;
}
Device3 device3 = _device.QueryInterfaceOrNull<Device3>();
if (device3 != null)
{
_device.Dispose();
_device = device3;
return;
}
Device2 device2 = _device.QueryInterfaceOrNull<Device2>();
if (device2 != null)
{
_device.Dispose();
_device = device2;
return;
}
Device1 device1 = _device.QueryInterfaceOrNull<Device1>();
if (device1 != null)
{
_device.Dispose();
_device = device1;
}
} }
private void ThreadLoop() private void ThreadLoop()
@@ -152,10 +193,6 @@ namespace VRCX
while (_thread != null) while (_thread != null)
{ {
if (_wristOverlayActive)
_browser1.RenderToTexture(_texture1);
if (_hmdOverlayActive)
_browser2.RenderToTexture(_texture2);
try try
{ {
Thread.Sleep(32); Thread.Sleep(32);