mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-19 14:53:50 +02:00
Merge overlays, move overlay to separate process (#44)
* refactor: merge two overlay offScreenBrowser into one * Electron support for shared overlay * Separate overlay into its own process * fix: invalid overlay texture size * Handle duplicate processes * Remove logging --------- Co-authored-by: pa <maplenagisa@gmail.com> Co-authored-by: rs189 <35667100+rs189@users.noreply.github.com>
This commit is contained in:
87
Dotnet/Overlay/Electron/AppApiVrElectron.cs
Normal file
87
Dotnet/Overlay/Electron/AppApiVrElectron.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace VRCX
|
||||
{
|
||||
public class AppApiVrElectron : AppApiVr
|
||||
{
|
||||
static AppApiVrElectron()
|
||||
{
|
||||
Instance = new AppApiVrElectron();
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
}
|
||||
|
||||
public override void VrInit()
|
||||
{
|
||||
}
|
||||
|
||||
public override List<KeyValuePair<string, string>> GetExecuteVrOverlayFunctionQueue()
|
||||
{
|
||||
var list = new List<KeyValuePair<string, string>>();
|
||||
while (Program.VRCXVRInstance.GetExecuteVrOverlayFunctionQueue().TryDequeue(out var item))
|
||||
{
|
||||
list.Add(item);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public override void ToggleSystemMonitor(bool enabled)
|
||||
{
|
||||
SystemMonitorElectron.Instance.Start(enabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current CPU usage as a percentage.
|
||||
/// </summary>
|
||||
/// <returns>The current CPU usage as a percentage.</returns>
|
||||
public override float CpuUsage()
|
||||
{
|
||||
return SystemMonitorElectron.Instance.CpuUsage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of arrays containing information about the connected VR devices.
|
||||
/// Each sub-array contains the type of device and its current state
|
||||
/// </summary>
|
||||
/// <returns>An array of arrays containing information about the connected VR devices.</returns>
|
||||
public override string[][] GetVRDevices()
|
||||
{
|
||||
return Program.VRCXVRInstance.GetDevices();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of milliseconds that the system has been running.
|
||||
/// </summary>
|
||||
/// <returns>The number of milliseconds that the system has been running.</returns>
|
||||
public override double GetUptime()
|
||||
{
|
||||
return SystemMonitorElectron.Instance.UpTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current language of the operating system.
|
||||
/// </summary>
|
||||
/// <returns>The current language of the operating system.</returns>
|
||||
public override string CurrentCulture()
|
||||
{
|
||||
return CultureInfo.CurrentCulture.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the file path of the custom user js file, if it exists.
|
||||
/// </summary>
|
||||
/// <returns>The file path of the custom user js file, or an empty string if it doesn't exist.</returns>
|
||||
public override string CustomVrScript()
|
||||
{
|
||||
var filePath = Path.Join(Program.AppDataDirectory, "customvr.js");
|
||||
if (File.Exists(filePath))
|
||||
return File.ReadAllText(filePath);
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,7 @@ namespace VRCX
|
||||
private bool _active;
|
||||
private bool _menuButton;
|
||||
private int _overlayHand;
|
||||
private GLTextureWriter _wristOverlayTextureWriter;
|
||||
private GLTextureWriter _hmdOverlayTextureWriter;
|
||||
private GLTextureWriter _overlayTextureWriter;
|
||||
private Thread _thread;
|
||||
private DateTime _nextOverlayUpdate;
|
||||
|
||||
@@ -40,22 +39,22 @@ namespace VRCX
|
||||
private bool _wristOverlayActive;
|
||||
private bool _wristOverlayWasActive;
|
||||
|
||||
private const string WRIST_OVERLAY_SHM_PATH = "/dev/shm/vrcx_wrist_overlay";
|
||||
private const string HMD_OVERLAY_SHM_PATH = "/dev/shm/vrcx_hmd_overlay";
|
||||
private const string OVERLAY_SHM_PATH = "/dev/shm/vrcx_overlay";
|
||||
private const int WRIST_FRAME_WIDTH = 512;
|
||||
private const int WRIST_FRAME_HEIGHT = 512;
|
||||
private const int WRIST_FRAME_SIZE = WRIST_FRAME_WIDTH * WRIST_FRAME_HEIGHT * 4; // RGBA
|
||||
private byte[] wristFrameBuffer = new byte[WRIST_FRAME_SIZE];
|
||||
private const int HMD_FRAME_WIDTH = 1024;
|
||||
private const int HMD_FRAME_HEIGHT = 1024;
|
||||
private const int HMD_FRAME_SIZE = HMD_FRAME_WIDTH * HMD_FRAME_HEIGHT * 4; // RGBA
|
||||
private byte[] hmdFrameBuffer = new byte[HMD_FRAME_SIZE];
|
||||
private MemoryMappedFile _wristOverlayMMF;
|
||||
private MemoryMappedViewAccessor _wristOverlayAccessor;
|
||||
private MemoryMappedFile _hmdOverlayMMF;
|
||||
private MemoryMappedViewAccessor _hmdOverlayAccessor;
|
||||
private readonly ConcurrentQueue<KeyValuePair<string, string>> _wristFeedFunctionQueue = new ConcurrentQueue<KeyValuePair<string, string>>();
|
||||
private readonly ConcurrentQueue<KeyValuePair<string, string>> _hmdFeedFunctionQueue = new ConcurrentQueue<KeyValuePair<string, string>>();
|
||||
|
||||
private const int SHARED_FRAME_SIZE = SHARED_FRAME_WIDTH * SHARED_FRAME_HEIGHT * 4;
|
||||
private const int SHARED_FRAME_WIDTH = 1024;
|
||||
private const int SHARED_FRAME_HEIGHT = WRIST_FRAME_HEIGHT + HMD_FRAME_HEIGHT;
|
||||
private byte[] frameBuffer = new byte[SHARED_FRAME_SIZE];
|
||||
|
||||
private MemoryMappedFile _overlayMMF;
|
||||
private MemoryMappedViewAccessor _overlayAccessor;
|
||||
private readonly ConcurrentQueue<KeyValuePair<string, string>> _overlayFunctionQueue = new ConcurrentQueue<KeyValuePair<string, string>>();
|
||||
|
||||
static VRCXVRElectron()
|
||||
{
|
||||
@@ -86,15 +85,10 @@ namespace VRCX
|
||||
thread?.Interrupt();
|
||||
thread?.Join();
|
||||
|
||||
_wristOverlayAccessor?.Dispose();
|
||||
_wristOverlayAccessor = null;
|
||||
_wristOverlayMMF?.Dispose();
|
||||
_wristOverlayMMF = null;
|
||||
|
||||
_hmdOverlayAccessor?.Dispose();
|
||||
_hmdOverlayAccessor = null;
|
||||
_hmdOverlayMMF?.Dispose();
|
||||
_hmdOverlayMMF = null;
|
||||
_overlayAccessor?.Dispose();
|
||||
_overlayAccessor = null;
|
||||
_overlayMMF?.Dispose();
|
||||
_overlayMMF = null;
|
||||
|
||||
GLContextX11.Cleanup();
|
||||
GLContextWayland.Cleanup();
|
||||
@@ -135,11 +129,8 @@ namespace VRCX
|
||||
throw new Exception("Failed to initialise OpenGL context");
|
||||
}
|
||||
|
||||
_wristOverlayTextureWriter = new GLTextureWriter(512, 512);
|
||||
_wristOverlayTextureWriter.UpdateTexture();
|
||||
|
||||
_hmdOverlayTextureWriter = new GLTextureWriter(1024, 1024);
|
||||
_hmdOverlayTextureWriter.UpdateTexture();
|
||||
_overlayTextureWriter = new GLTextureWriter(SHARED_FRAME_WIDTH, SHARED_FRAME_HEIGHT);
|
||||
_overlayTextureWriter.UpdateTexture();
|
||||
}
|
||||
|
||||
private void UpgradeDevice()
|
||||
@@ -147,33 +138,20 @@ namespace VRCX
|
||||
|
||||
}
|
||||
|
||||
public byte[] GetLatestWristOverlayFrame()
|
||||
public byte[] GetLatestOverlayFrame()
|
||||
{
|
||||
if (_wristOverlayAccessor == null) return null;
|
||||
byte ready = _wristOverlayAccessor.ReadByte(0);
|
||||
if (_overlayAccessor == null) return null;
|
||||
byte ready = _overlayAccessor.ReadByte(0);
|
||||
if (ready == 1)
|
||||
{
|
||||
_wristOverlayAccessor.ReadArray(1, wristFrameBuffer, 0, WRIST_FRAME_SIZE);
|
||||
_wristOverlayAccessor.Write(0, (byte)0); // reset flag
|
||||
return wristFrameBuffer;
|
||||
_overlayAccessor.ReadArray(1, frameBuffer, 0, SHARED_FRAME_SIZE);
|
||||
_overlayAccessor.Write(0, (byte)0); // reset flag
|
||||
return frameBuffer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] GetLatestHmdOverlayFrame()
|
||||
{
|
||||
if (_hmdOverlayAccessor == null) return null;
|
||||
byte ready = _hmdOverlayAccessor.ReadByte(0);
|
||||
if (ready == 1)
|
||||
{
|
||||
_hmdOverlayAccessor.ReadArray(1, hmdFrameBuffer, 0, HMD_FRAME_SIZE);
|
||||
_hmdOverlayAccessor.Write(0, (byte)0); // reset flag
|
||||
return hmdFrameBuffer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void FlipImageVertically(byte[] imageData, int width, int height)
|
||||
private static void FlipImageVertically(ref byte[] imageData, int width, int height)
|
||||
{
|
||||
int stride = width * 4; // 4 bytes per pixel (RGBA)
|
||||
byte[] tempRow = new byte[stride];
|
||||
@@ -326,15 +304,10 @@ namespace VRCX
|
||||
}
|
||||
}
|
||||
|
||||
_wristOverlayAccessor?.Dispose();
|
||||
_wristOverlayAccessor = null;
|
||||
_wristOverlayMMF?.Dispose();
|
||||
_wristOverlayMMF = null;
|
||||
|
||||
_hmdOverlayAccessor?.Dispose();
|
||||
_hmdOverlayAccessor = null;
|
||||
_hmdOverlayMMF?.Dispose();
|
||||
_hmdOverlayMMF = null;
|
||||
_overlayAccessor?.Dispose();
|
||||
_overlayAccessor = null;
|
||||
_overlayMMF?.Dispose();
|
||||
_overlayMMF = null;
|
||||
|
||||
GLContextX11.Cleanup();
|
||||
GLContextWayland.Cleanup();
|
||||
@@ -352,35 +325,32 @@ namespace VRCX
|
||||
{
|
||||
OpenVR.Overlay.DestroyOverlay(_hmdOverlayHandle);
|
||||
_hmdOverlayHandle = 0;
|
||||
|
||||
_hmdOverlayAccessor?.Dispose();
|
||||
_hmdOverlayAccessor = null;
|
||||
_hmdOverlayMMF?.Dispose();
|
||||
_hmdOverlayMMF = null;
|
||||
}
|
||||
|
||||
_hmdOverlayWasActive = _hmdOverlayActive;
|
||||
|
||||
if (_wristOverlayActive != _wristOverlayWasActive && _wristOverlayHandle != 0)
|
||||
{
|
||||
OpenVR.Overlay.DestroyOverlay(_wristOverlayHandle);
|
||||
_wristOverlayHandle = 0;
|
||||
|
||||
_wristOverlayAccessor?.Dispose();
|
||||
_wristOverlayAccessor = null;
|
||||
_wristOverlayMMF?.Dispose();
|
||||
_wristOverlayMMF = null;
|
||||
}
|
||||
|
||||
_wristOverlayWasActive = _wristOverlayActive;
|
||||
|
||||
if (!_active)
|
||||
if (!_active || (!_hmdOverlayActive && !_wristOverlayActive))
|
||||
{
|
||||
_overlayAccessor?.Dispose();
|
||||
_overlayAccessor = null;
|
||||
_overlayMMF?.Dispose();
|
||||
_overlayMMF = null;
|
||||
GLContextX11.Cleanup();
|
||||
GLContextWayland.Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsActive()
|
||||
{
|
||||
return _active;
|
||||
}
|
||||
|
||||
public override void Refresh()
|
||||
{
|
||||
//_wristOverlay.Reload();
|
||||
@@ -606,31 +576,6 @@ namespace VRCX
|
||||
}
|
||||
}
|
||||
|
||||
var e = new VREvent_t();
|
||||
|
||||
while (overlay.PollNextOverlayEvent(dashboardHandle, ref e, (uint)Marshal.SizeOf(e)))
|
||||
{
|
||||
var type = (EVREventType)e.eventType;
|
||||
if (type == EVREventType.VREvent_MouseMove)
|
||||
{
|
||||
var m = e.data.mouse;
|
||||
//var s = _wristOverlay.Size;
|
||||
//_wristOverlay.GetBrowserHost().SendMouseMoveEvent((int)(m.x * s.Width), s.Height - (int)(m.y * s.Height), false, CefEventFlags.None);
|
||||
}
|
||||
else if (type == EVREventType.VREvent_MouseButtonDown)
|
||||
{
|
||||
var m = e.data.mouse;
|
||||
//var s = _wristOverlay.Size;
|
||||
//_wristOverlay.GetBrowserHost().SendMouseClickEvent((int)(m.x * s.Width), s.Height - (int)(m.y * s.Height), MouseButtonType.Left, false, 1, CefEventFlags.LeftMouseButton);
|
||||
}
|
||||
else if (type == EVREventType.VREvent_MouseButtonUp)
|
||||
{
|
||||
var m = e.data.mouse;
|
||||
//var s = _wristOverlay.Size;
|
||||
//_wristOverlay.GetBrowserHost().SendMouseClickEvent((int)(m.x * s.Width), s.Height - (int)(m.y * s.Height), MouseButtonType.Left, true, 1, CefEventFlags.None);
|
||||
}
|
||||
}
|
||||
|
||||
if (dashboardVisible)
|
||||
{
|
||||
//var texture = new Texture_t
|
||||
@@ -686,8 +631,8 @@ namespace VRCX
|
||||
return err;
|
||||
}
|
||||
|
||||
_wristOverlayMMF = MemoryMappedFile.CreateFromFile(WRIST_OVERLAY_SHM_PATH, FileMode.Open, null, WRIST_FRAME_SIZE + 1);
|
||||
_wristOverlayAccessor = _wristOverlayMMF.CreateViewAccessor();
|
||||
_overlayMMF = MemoryMappedFile.CreateFromFile(OVERLAY_SHM_PATH, FileMode.Open, null, SHARED_FRAME_SIZE + 1);
|
||||
_overlayAccessor = _overlayMMF.CreateViewAccessor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,16 +670,24 @@ namespace VRCX
|
||||
if (!dashboardVisible &&
|
||||
DateTime.UtcNow.CompareTo(_nextOverlayUpdate) <= 0)
|
||||
{
|
||||
if (_wristOverlayTextureWriter != null)
|
||||
if (_overlayTextureWriter != null)
|
||||
{
|
||||
byte[] imageData = GetLatestWristOverlayFrame();
|
||||
byte[] imageData = GetLatestOverlayFrame();
|
||||
if (imageData != null)
|
||||
{
|
||||
FlipImageVertically(imageData, WRIST_FRAME_WIDTH, WRIST_FRAME_HEIGHT);
|
||||
_wristOverlayTextureWriter.WriteImageToBuffer(imageData);
|
||||
_wristOverlayTextureWriter.UpdateTexture();
|
||||
FlipImageVertically(ref imageData, SHARED_FRAME_WIDTH, SHARED_FRAME_HEIGHT);
|
||||
_overlayTextureWriter.WriteImageToBuffer(imageData);
|
||||
_overlayTextureWriter.UpdateTexture();
|
||||
|
||||
Texture_t texture = _wristOverlayTextureWriter.AsTextureT();
|
||||
Texture_t texture = _overlayTextureWriter.AsTextureT();
|
||||
var bounds = new VRTextureBounds_t
|
||||
{
|
||||
uMin = 0f,
|
||||
uMax = 0.5f,
|
||||
vMin = 0f,
|
||||
vMax = (float)WRIST_FRAME_HEIGHT / SHARED_FRAME_HEIGHT
|
||||
};
|
||||
overlay.SetOverlayTextureBounds(overlayHandle, ref bounds);
|
||||
err = OpenVR.Overlay.SetOverlayTexture(overlayHandle, ref texture);
|
||||
if (err != EVROverlayError.None)
|
||||
{
|
||||
@@ -830,23 +783,31 @@ namespace VRCX
|
||||
return err;
|
||||
}
|
||||
|
||||
_hmdOverlayMMF = MemoryMappedFile.CreateFromFile(HMD_OVERLAY_SHM_PATH, FileMode.Open, null, HMD_FRAME_SIZE + 1);
|
||||
_hmdOverlayAccessor = _hmdOverlayMMF.CreateViewAccessor();
|
||||
_overlayMMF = MemoryMappedFile.CreateFromFile(OVERLAY_SHM_PATH, FileMode.Open, null, SHARED_FRAME_SIZE + 1);
|
||||
_overlayAccessor = _overlayMMF.CreateViewAccessor();
|
||||
}
|
||||
}
|
||||
|
||||
if (!dashboardVisible)
|
||||
{
|
||||
if (_hmdOverlayTextureWriter != null)
|
||||
if (_overlayTextureWriter != null)
|
||||
{
|
||||
byte[] imageData = GetLatestHmdOverlayFrame();
|
||||
byte[] imageData = GetLatestOverlayFrame();
|
||||
if (imageData != null)
|
||||
{
|
||||
FlipImageVertically(imageData, HMD_FRAME_WIDTH, HMD_FRAME_HEIGHT);
|
||||
_hmdOverlayTextureWriter.WriteImageToBuffer(imageData);
|
||||
_hmdOverlayTextureWriter.UpdateTexture();
|
||||
FlipImageVertically(ref imageData, SHARED_FRAME_WIDTH, SHARED_FRAME_HEIGHT);
|
||||
_overlayTextureWriter.WriteImageToBuffer(imageData);
|
||||
_overlayTextureWriter.UpdateTexture();
|
||||
|
||||
Texture_t texture = _hmdOverlayTextureWriter.AsTextureT();
|
||||
Texture_t texture = _overlayTextureWriter.AsTextureT();
|
||||
var bounds = new VRTextureBounds_t
|
||||
{
|
||||
uMin = 0f,
|
||||
uMax = 1f,
|
||||
vMin = (float)(SHARED_FRAME_HEIGHT - HMD_FRAME_HEIGHT) / SHARED_FRAME_HEIGHT,
|
||||
vMax = 1f
|
||||
};
|
||||
overlay.SetOverlayTextureBounds(overlayHandle, ref bounds);
|
||||
err = OpenVR.Overlay.SetOverlayTexture(overlayHandle, ref texture);
|
||||
if (err != EVROverlayError.None)
|
||||
{
|
||||
@@ -880,23 +841,9 @@ namespace VRCX
|
||||
return err;
|
||||
}
|
||||
|
||||
public override ConcurrentQueue<KeyValuePair<string, string>> GetExecuteVrFeedFunctionQueue()
|
||||
{
|
||||
return _wristFeedFunctionQueue;
|
||||
}
|
||||
|
||||
public override void ExecuteVrFeedFunction(string function, string json)
|
||||
{
|
||||
//if (_hmdOverlaySocket == null || !_hmdOverlaySocket.Connected) return;
|
||||
// if (_wristOverlay.IsLoading)
|
||||
// Restart();
|
||||
|
||||
_wristFeedFunctionQueue.Enqueue(new KeyValuePair<string, string>(function, json));
|
||||
}
|
||||
|
||||
public override ConcurrentQueue<KeyValuePair<string, string>> GetExecuteVrOverlayFunctionQueue()
|
||||
{
|
||||
return _hmdFeedFunctionQueue;
|
||||
return _overlayFunctionQueue;
|
||||
}
|
||||
|
||||
public override void ExecuteVrOverlayFunction(string function, string json)
|
||||
@@ -905,7 +852,7 @@ namespace VRCX
|
||||
// if (_hmdOverlay.IsLoading)
|
||||
// Restart();
|
||||
|
||||
_hmdFeedFunctionQueue.Enqueue(new KeyValuePair<string, string>(function, json));
|
||||
_overlayFunctionQueue.Enqueue(new KeyValuePair<string, string>(function, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user