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

View File

@@ -51,7 +51,6 @@ namespace VRCX
cefSettings.CefCommandLineArgs.Add("disable-pdf-extension");
cefSettings.CefCommandLineArgs["autoplay-policy"] = "no-user-gesture-required";
cefSettings.CefCommandLineArgs.Add("disable-web-security");
cefSettings.SetOffScreenRenderingBestPerformanceArgs(); // causes white screen sometimes?
if (WebApi.ProxySet)
{

View File

@@ -10,116 +10,76 @@ using CefSharp.OffScreen;
using CefSharp.Structs;
using SharpDX.Direct3D11;
using System;
using System.Runtime.InteropServices;
using System.Threading;
using SharpDX.Direct3D;
using SharpDX.Mathematics.Interop;
using Range = CefSharp.Structs.Range;
namespace VRCX
{
public class OffScreenBrowser : ChromiumWebBrowser, IRenderHandler
{
private readonly ReaderWriterLockSlim _paintBufferLock;
private GCHandle _paintBuffer;
private int _width;
private int _height;
private Device _device;
private Device1 _device1;
private DeviceMultithread _deviceMultithread;
private Query _query;
private Texture2D _renderTarget;
public OffScreenBrowser(string address, int width, int height)
: base(
address,
new BrowserSettings()
{
DefaultEncoding = "UTF-8"
}
)
: base(address, automaticallyCreateBrowser: false)
{
_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);
RenderHandler = this;
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()
{
RenderHandler = null;
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()
{
return null;
return new ScreenInfo
{
DeviceScaleFactor = 1.0F
};
}
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)
{
// 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)
@@ -149,37 +126,6 @@ namespace VRCX
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)

View File

@@ -16,6 +16,10 @@ using SharpDX.Direct3D11;
using SharpDX.DXGI;
using Valve.VR;
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
{
@@ -87,7 +91,8 @@ namespace VRCX
{
Factory f = new Factory1();
_device = new Device(f.GetAdapter(OpenVR.System.GetD3D9AdapterIndex()),
DeviceCreationFlags.SingleThreaded | DeviceCreationFlags.BgraSupport);
DeviceCreationFlags.BgraSupport);
UpgradeDevice();
_texture1?.Dispose();
_texture1 = new Texture2D(
@@ -100,12 +105,11 @@ namespace VRCX
ArraySize = 1,
Format = Format.B8G8R8A8_UNorm,
SampleDescription = new SampleDescription(1, 0),
Usage = ResourceUsage.Dynamic,
BindFlags = BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.Write
BindFlags = BindFlags.ShaderResource
}
);
_browser1?.UpdateRender(_device, _texture1);
_texture2?.Dispose();
_texture2 = new Texture2D(
_device,
@@ -117,11 +121,48 @@ namespace VRCX
ArraySize = 1,
Format = Format.B8G8R8A8_UNorm,
SampleDescription = new SampleDescription(1, 0),
Usage = ResourceUsage.Dynamic,
BindFlags = BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.Write
BindFlags = BindFlags.ShaderResource
}
);
_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()
@@ -152,10 +193,6 @@ namespace VRCX
while (_thread != null)
{
if (_wristOverlayActive)
_browser1.RenderToTexture(_texture1);
if (_hmdOverlayActive)
_browser2.RenderToTexture(_texture2);
try
{
Thread.Sleep(32);