using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Logging; using DesktopBuddy.Shared; using HarmonyLib; using InterprocessLib; using Microsoft.CodeAnalysis; using Renderite.Shared; using Renderite.Unity; using UMP; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("DesktopBuddySharedTextureBridge")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+e5251e37dd743ac1b5ffad5e3f95aa6063a3aabc")] [assembly: AssemblyProduct("DesktopBuddySharedTextureBridge")] [assembly: AssemblyTitle("DesktopBuddySharedTextureBridge")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace DesktopBuddy.Shared { internal static class SharedTextureBridgeProtocol { private const string BaseOwnerId = "DesktopBuddy.SharedTexture.v2"; private const string BaseQueueName = "DesktopBuddy.SharedTexture.v2"; private static readonly string QueueScope = GetQueueScope(); public static readonly string OwnerId = GetScopedName("DesktopBuddy.SharedTexture.v2"); public static readonly string QueueName = GetScopedName("DesktopBuddy.SharedTexture.v2"); public const string StartMessageId = "Start"; public const string StopMessageId = "Stop"; public const string RunningMessageId = "Running"; public const string StoppedMessageId = "Stopped"; public const string RendererDeviceMessageId = "RendererDevice"; public const int MaxTextureSlots = 4096; public const int MagicIndexBase = 10000; private static string GetScopedName(string baseName) { if (!string.IsNullOrEmpty(QueueScope)) { return baseName + "." + QueueScope; } return baseName; } private static string GetQueueScope() { string value = GetArgumentValue("-shmprefix") ?? GetArgumentValue("--shmprefix"); if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } return "shm" + ComputeStableHash(value).ToString("X16"); } private static string GetArgumentValue(string name) { string[] commandLineArgs; try { commandLineArgs = Environment.GetCommandLineArgs(); } catch { return null; } for (int i = 0; i < commandLineArgs.Length; i++) { string text = commandLineArgs[i]; if (text == null) { continue; } if (string.Equals(text, name, StringComparison.OrdinalIgnoreCase)) { if (i + 1 < commandLineArgs.Length) { return commandLineArgs[i + 1]; } return null; } string text2 = name + "="; if (text.StartsWith(text2, StringComparison.OrdinalIgnoreCase)) { return text.Substring(text2.Length); } } return null; } private static ulong ComputeStableHash(string value) { ulong num = 14695981039346656037uL; for (int i = 0; i < value.Length; i++) { num ^= value[i]; num *= 1099511628211L; } return num; } } internal sealed class SimpleMemoryPackerPool : IMemoryPackerEntityPool { public static readonly SimpleMemoryPackerPool Instance = new SimpleMemoryPackerPool(); private SimpleMemoryPackerPool() { } T IMemoryPackerEntityPool.Borrow() { return new T(); } void IMemoryPackerEntityPool.Return(T value) { } } internal sealed class SharedTextureStartMessage : IMemoryPackable { public int SlotId; public int Generation; public long SharedTextureHandle; public string SharedTextureName; public int SharedTextureWidth; public int SharedTextureHeight; public void Pack(ref MemoryPacker packer) { ((MemoryPacker)(ref packer)).Write(SlotId); ((MemoryPacker)(ref packer)).Write(Generation); ((MemoryPacker)(ref packer)).Write(SharedTextureHandle); ((MemoryPacker)(ref packer)).Write(SharedTextureName); ((MemoryPacker)(ref packer)).Write(SharedTextureWidth); ((MemoryPacker)(ref packer)).Write(SharedTextureHeight); } public void Unpack(ref MemoryUnpacker unpacker) { ((MemoryUnpacker)(ref unpacker)).Read(ref SlotId); ((MemoryUnpacker)(ref unpacker)).Read(ref Generation); ((MemoryUnpacker)(ref unpacker)).Read(ref SharedTextureHandle); ((MemoryUnpacker)(ref unpacker)).Read(ref SharedTextureName); ((MemoryUnpacker)(ref unpacker)).Read(ref SharedTextureWidth); ((MemoryUnpacker)(ref unpacker)).Read(ref SharedTextureHeight); } } internal sealed class SharedTextureStopMessage : IMemoryPackable { public int SlotId; public int Generation; public void Pack(ref MemoryPacker packer) { ((MemoryPacker)(ref packer)).Write(SlotId); ((MemoryPacker)(ref packer)).Write(Generation); } public void Unpack(ref MemoryUnpacker unpacker) { ((MemoryUnpacker)(ref unpacker)).Read(ref SlotId); ((MemoryUnpacker)(ref unpacker)).Read(ref Generation); } } internal sealed class SharedTextureRunningMessage : IMemoryPackable { public int SlotId; public int Generation; public int Width; public int Height; public void Pack(ref MemoryPacker packer) { ((MemoryPacker)(ref packer)).Write(SlotId); ((MemoryPacker)(ref packer)).Write(Generation); ((MemoryPacker)(ref packer)).Write(Width); ((MemoryPacker)(ref packer)).Write(Height); } public void Unpack(ref MemoryUnpacker unpacker) { ((MemoryUnpacker)(ref unpacker)).Read(ref SlotId); ((MemoryUnpacker)(ref unpacker)).Read(ref Generation); ((MemoryUnpacker)(ref unpacker)).Read(ref Width); ((MemoryUnpacker)(ref unpacker)).Read(ref Height); } } internal sealed class SharedTextureStoppedMessage : IMemoryPackable { public int SlotId; public int Generation; public void Pack(ref MemoryPacker packer) { ((MemoryPacker)(ref packer)).Write(SlotId); ((MemoryPacker)(ref packer)).Write(Generation); } public void Unpack(ref MemoryUnpacker unpacker) { ((MemoryUnpacker)(ref unpacker)).Read(ref SlotId); ((MemoryUnpacker)(ref unpacker)).Read(ref Generation); } } internal sealed class SharedTextureRendererDeviceMessage : IMemoryPackable { public long AdapterLuid; public int VendorId; public string Description; public void Pack(ref MemoryPacker packer) { ((MemoryPacker)(ref packer)).Write(AdapterLuid); ((MemoryPacker)(ref packer)).Write(VendorId); ((MemoryPacker)(ref packer)).Write(Description); } public void Unpack(ref MemoryUnpacker unpacker) { ((MemoryUnpacker)(ref unpacker)).Read(ref AdapterLuid); ((MemoryUnpacker)(ref unpacker)).Read(ref VendorId); ((MemoryUnpacker)(ref unpacker)).Read(ref Description); } } } namespace DesktopBuddySharedTextureBridge { internal interface IBridgeTextureSlot : IDisplayTextureSource, IDisposable { int Width { get; } int Height { get; } int RequestCount { get; } bool TryBind(); void Tick(); } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class LibVlcCachePatch { private struct CacheSettings { public int NetworkCachingMs; public int LiveCachingMs; public int FileCachingMs; public static CacheSettings Default => new CacheSettings { NetworkCachingMs = 300, LiveCachingMs = 300, FileCachingMs = 300 }; } private static DateTime _lastLoadUtc; private static CacheSettings _settings = CacheSettings.Default; private static bool _loggedConfigPath; private static void Prefix(PlayerOptionsStandalone options) { try { if (options != null) { CacheSettings cacheSettings = LoadSettings(); options.NetworkCaching = cacheSettings.NetworkCachingMs; options.LiveCaching = cacheSettings.LiveCachingMs; options.FileCaching = cacheSettings.FileCachingMs; SharedTextureBridgePlugin.LogInfo($"[LibVLC] Cache options: network={options.NetworkCaching}ms live={options.LiveCaching}ms file={options.FileCaching}ms"); } } catch (Exception ex) { SharedTextureBridgePlugin.LogError("[LibVLC] Prefix failed", ex); } } private static CacheSettings LoadSettings() { if ((DateTime.UtcNow - _lastLoadUtc).TotalSeconds < 2.0) { return _settings; } _lastLoadUtc = DateTime.UtcNow; string text = FindConfigPath(); if (text == null || !File.Exists(text)) { if (!_loggedConfigPath) { _loggedConfigPath = true; SharedTextureBridgePlugin.LogWarning("[LibVLC] DesktopBuddy BepInEx config not found; using low-latency cache defaults"); } _settings = CacheSettings.Default; return _settings; } if (!_loggedConfigPath) { _loggedConfigPath = true; SharedTextureBridgePlugin.LogInfo("[LibVLC] Reading DesktopBuddy cache config from " + text); } try { string json = File.ReadAllText(text); _settings = new CacheSettings { NetworkCachingMs = ReadCacheMs(json, "libVlcNetworkCachingMs", CacheSettings.Default.NetworkCachingMs), LiveCachingMs = ReadCacheMs(json, "libVlcLiveCachingMs", CacheSettings.Default.LiveCachingMs), FileCachingMs = ReadCacheMs(json, "libVlcFileCachingMs", CacheSettings.Default.FileCachingMs) }; } catch (Exception ex) { SharedTextureBridgePlugin.LogError("[LibVLC] Failed to read DesktopBuddy cache config", ex); _settings = CacheSettings.Default; } return _settings; } private static string FindConfigPath() { string gameRootPath = Paths.GameRootPath; string text = Directory.GetParent(gameRootPath)?.FullName; string currentDirectory = Directory.GetCurrentDirectory(); string baseDirectory = AppDomain.CurrentDomain.BaseDirectory; string directoryName = Path.GetDirectoryName(typeof(SharedTextureBridgePlugin).Assembly.Location); string[] array = new string[6] { Path.Combine(directoryName ?? "", "..", "..", "..", "..", "BepInEx", "config", "com.devl0rd.DesktopBuddy.cfg"), Path.Combine(gameRootPath ?? "", "BepInEx", "config", "com.devl0rd.DesktopBuddy.cfg"), Path.Combine(text ?? "", "BepInEx", "config", "com.devl0rd.DesktopBuddy.cfg"), Path.Combine(currentDirectory ?? "", "BepInEx", "config", "com.devl0rd.DesktopBuddy.cfg"), Path.Combine(baseDirectory ?? "", "..", "..", "..", "BepInEx", "config", "com.devl0rd.DesktopBuddy.cfg"), Path.Combine(baseDirectory ?? "", "..", "..", "..", "..", "BepInEx", "config", "com.devl0rd.DesktopBuddy.cfg") }; foreach (string path in array) { try { string fullPath = Path.GetFullPath(path); if (File.Exists(fullPath)) { return fullPath; } } catch { } } return null; } private static int ReadInt(string configText, string key, int fallback) { string text = Regex.Escape(key); Match match = Regex.Match(configText, "\"" + text + "\"\\s*:\\s*(-?\\d+)"); if (!match.Success) { match = Regex.Match(configText, "(?m)^\\s*" + text + "\\s*=\\s*(-?\\d+)\\s*$"); } if (!match.Success || !int.TryParse(match.Groups[1].Value, out var result)) { return fallback; } if (result < 0) { return 0; } if (result > 5000) { return 5000; } return result; } private static int ReadCacheMs(string json, string key, int fallback) { return ReadInt(json, key, fallback); } } internal sealed class LinuxCaptureTextureSlot : IBridgeTextureSlot, IDisplayTextureSource, IDisposable { private const uint DrmFormatArgb8888 = 875713089u; private const uint DrmFormatXrgb8888 = 875713112u; private const uint DrmFormatAbgr8888 = 875708993u; private const uint DrmFormatXbgr8888 = 875709016u; private readonly ManualLogSource _log; private readonly HashSet _requests = new HashSet(); private readonly LinuxNativeBridgeRenderer _bridge = new LinuxNativeBridgeRenderer(); private readonly uint _pipeWireNodeId; private Texture2D _texture; private byte[] _pixels; private bool _disposed; private bool _captureStarted; private bool _started; private int _width; private int _height; private int _pollsWithoutFrame; private int _copyFailures; private int _unsupportedFrames; private int _uploadedFrames; private bool _loggedFirstFrame; public Texture UnityTexture => (Texture)(object)_texture; public int Width => Math.Max(1, _width); public int Height => Math.Max(1, _height); public int RequestCount => _requests.Count; public bool IsValid { get { if (!_disposed && _started) { return (Object)(object)_texture != (Object)null; } return false; } } public string SourceName => "LinuxShmCapture"; internal LinuxCaptureTextureSlot(uint pipeWireNodeId, int widthHint, int heightHint, ManualLogSource log) { _pipeWireNodeId = pipeWireNodeId; _width = Math.Max(1, widthHint); _height = Math.Max(1, heightHint); _log = log; } public bool TryBind() { if (_started) { return true; } if (_disposed) { return false; } EnsureCaptureStarted(); PollAndUploadFrame(); return _started; } public void Tick() { if (_captureStarted && !_disposed) { PollAndUploadFrame(); } } private void EnsureCaptureStarted() { if (!_captureStarted && !_disposed) { SharedTextureBridgePlugin.LogInfo($"[LinuxCapture] SHM capture start calling native bridge node={_pipeWireNodeId}"); int num = _bridge.StartCapture(_pipeWireNodeId); SharedTextureBridgePlugin.LogInfo($"[LinuxCapture] SHM capture start returned {num} node={_pipeWireNodeId}"); if (num != 0) { SharedTextureBridgePlugin.LogWarning($"[LinuxCapture] SHM capture did not start cleanly: {num}"); } _captureStarted = num == 0; } } private bool PollAndUploadFrame() { //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Expected O, but got Unknown DbLinuxFrame frame; int num = _bridge.PollFrame(out frame); switch (num) { case 1: _pollsWithoutFrame++; if (_pollsWithoutFrame == 120 || _pollsWithoutFrame % 600 == 0) { SharedTextureBridgePlugin.LogInfo($"[LinuxCapture] Waiting for SHM frame ({_pollsWithoutFrame} polls)"); } return false; case 0: if (frame.Status == 0) { _pollsWithoutFrame = 0; if (!IsSupportedFourcc(frame.Fourcc) || frame.Width == 0 || frame.Height == 0) { _unsupportedFrames++; if (_unsupportedFrames <= 8 || _unsupportedFrames % 120 == 0) { SharedTextureBridgePlugin.LogWarning($"[LinuxCapture] Unsupported SHM frame count={_unsupportedFrames} fd={frame.Fd} {frame.Width}x{frame.Height} fourcc=0x{frame.Fourcc:X8} stride={frame.Stride}"); } _bridge.DiscardFrame(frame); return false; } int num2; int num3; int num5; checked { num2 = (int)frame.Width; num3 = (int)frame.Height; int num4 = num2 * 4 * num3; if (_pixels == null || _pixels.Length != num4) { _pixels = new byte[num4]; } num5 = _bridge.CopyFrameBytes(frame, _pixels); } if (num5 != 0) { _copyFailures++; if (_copyFailures <= 8 || _copyFailures % 120 == 0) { SharedTextureBridgePlugin.LogWarning($"[LinuxCapture] SHM copy failed count={_copyFailures} status={num5} fd={frame.Fd} {frame.Width}x{frame.Height} fourcc=0x{frame.Fourcc:X8} stride={frame.Stride}"); } return false; } if (frame.Fourcc == 875708993 || frame.Fourcc == 875709016) { SwapRedBlue(_pixels); } if (frame.Fourcc == 875713112 || frame.Fourcc == 875709016) { ForceOpaqueAlpha(_pixels); } bool flag = (Object)(object)_texture == (Object)null || ((Texture)_texture).width != num2 || ((Texture)_texture).height != num3; if (flag) { DestroyTexture(); _texture = new Texture2D(num2, num3, (TextureFormat)14, false, false) { wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1 }; _width = num2; _height = num3; } _texture.LoadRawTextureData(_pixels); _texture.Apply(false, false); _uploadedFrames++; if (!_started) { _started = true; } if (!_loggedFirstFrame || flag) { NotifyCallbacks(); } if (!_loggedFirstFrame) { _loggedFirstFrame = true; SharedTextureBridgePlugin.LogInfo($"[LinuxCapture] First SHM frame uploaded {_width}x{_height} fourcc=0x{frame.Fourcc:X8} stride={frame.Stride}"); } else if (_uploadedFrames % 120 == 0) { SharedTextureBridgePlugin.LogInfo($"[LinuxCapture] Uploaded SHM frames={_uploadedFrames} {_width}x{_height}"); } return true; } goto default; default: SharedTextureBridgePlugin.LogWarning($"[LinuxCapture] PollFrame status={num} frameStatus={frame.Status} fd={frame.Fd}"); return false; } } private static bool IsSupportedFourcc(uint fourcc) { if (fourcc != 875713089 && fourcc != 875713112 && fourcc != 875708993) { return fourcc == 875709016; } return true; } private static void SwapRedBlue(byte[] pixels) { for (int i = 0; i < pixels.Length; i += 4) { byte b = pixels[i]; pixels[i] = pixels[i + 2]; pixels[i + 2] = b; } } private static void ForceOpaqueAlpha(byte[] pixels) { for (int i = 3; i < pixels.Length; i += 4) { pixels[i] = byte.MaxValue; } } public void RegisterRequest(Action onTextureChanged) { try { if (onTextureChanged != null) { _requests.Add(onTextureChanged); } if ((Object)(object)_texture != (Object)null) { onTextureChanged?.Invoke(); } } catch (Exception ex) { SharedTextureBridgePlugin.LogError("[LinuxCapture] RegisterRequest failed", ex); } } public void UnregisterRequest(Action onTextureChanged) { try { if (onTextureChanged != null) { _requests.Remove(onTextureChanged); } } catch (Exception ex) { SharedTextureBridgePlugin.LogError("[LinuxCapture] UnregisterRequest failed", ex); } } public void Dispose() { if (!_disposed) { _disposed = true; DestroyTexture(); _bridge.Dispose(); _requests.Clear(); } } private void DestroyTexture() { if (!((Object)(object)_texture == (Object)null)) { try { Object.Destroy((Object)(object)_texture); } catch (Exception ex) { SharedTextureBridgePlugin.LogWarning("[LinuxCapture] Texture destroy failed: " + ex.Message); } _texture = null; } } private void NotifyCallbacks() { foreach (Action request in _requests) { try { request?.Invoke(); } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("[LinuxCapture] Callback error: " + ex.Message)); } } } } } internal sealed class LinuxNativeBridgeRenderer : IDisposable { [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate int DesktopBuddyLinuxBridgeCallDelegate(ref DbLinuxBridgeCall call); private const uint OpPoll = 2u; private const uint OpStop = 3u; private const uint OpStartNode = 6u; private const uint OpCopyFrame = 7u; private const uint OpCloseFrame = 8u; private static readonly object LoadLock = new object(); private static IntPtr SharedModule; private static DesktopBuddyLinuxBridgeCallDelegate SharedCall; private DesktopBuddyLinuxBridgeCallDelegate _call; private ulong _captureId; internal bool TryLoad() { if (_call != null) { return true; } lock (LoadLock) { if (SharedCall == null) { string text = ResolveBridgePath(); SharedModule = LoadLibraryA(text); if (SharedModule == IntPtr.Zero) { SharedTextureBridgePlugin.LogWarning($"[LinuxBridge] LoadLibrary failed path={text} err=0x{Marshal.GetLastWin32Error():X8}"); return false; } IntPtr procAddress = GetProcAddress(SharedModule, "DesktopBuddyLinuxBridgeCall"); if (procAddress == IntPtr.Zero) { SharedTextureBridgePlugin.LogWarning($"[LinuxBridge] GetProcAddress failed err=0x{Marshal.GetLastWin32Error():X8}"); FreeSharedModule(); return false; } SharedCall = (DesktopBuddyLinuxBridgeCallDelegate)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(DesktopBuddyLinuxBridgeCallDelegate)); SharedTextureBridgePlugin.LogInfo("[LinuxBridge] Loaded " + text); } _call = SharedCall; } return true; } internal int StartCapture(uint nodeId) { if (!TryLoad()) { return -1; } DbLinuxBridgeCall call = new DbLinuxBridgeCall { Op = 6u, Arg0 = nodeId }; int num = _call(ref call); if (num == 0) { _captureId = call.Arg0; } return num; } internal int PollFrame(out DbLinuxFrame frame) { frame = default(DbLinuxFrame); if (_call == null || _captureId == 0L) { return -1; } DbLinuxBridgeCall call = new DbLinuxBridgeCall { Op = 2u, Arg0 = _captureId }; int result = _call(ref call); frame = call.Frame; return result; } internal int CopyFrameBytes(DbLinuxFrame frame, byte[] destination) { if (_call == null || destination == null || destination.Length == 0) { return -1; } GCHandle gCHandle = GCHandle.Alloc(destination, GCHandleType.Pinned); try { DbLinuxBridgeCall call = new DbLinuxBridgeCall { Op = 7u, Arg0 = checked((ulong)destination.LongLength), Frame = frame, Buffer = (ulong)gCHandle.AddrOfPinnedObject().ToInt64() }; return _call(ref call); } finally { gCHandle.Free(); } } internal void DiscardFrame(DbLinuxFrame frame) { if (_call != null && frame.Fd >= 0) { DbLinuxBridgeCall call = new DbLinuxBridgeCall { Op = 8u, Frame = frame }; _call(ref call); } } internal void Stop() { if (_call != null && _captureId != 0L) { DbLinuxBridgeCall call = new DbLinuxBridgeCall { Op = 3u, Arg0 = _captureId }; _call(ref call); _captureId = 0uL; } } private static string ResolveBridgePath() { return Path.Combine(Path.GetDirectoryName(typeof(SharedTextureBridgePlugin).Assembly.Location) ?? string.Empty, "DesktopBuddyLinuxBridge.so"); } public void Dispose() { Stop(); _call = null; } private static void FreeSharedModule() { if (!(SharedModule == IntPtr.Zero)) { FreeLibrary(SharedModule); SharedModule = IntPtr.Zero; SharedCall = null; } } [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] private static extern IntPtr LoadLibraryA(string fileName); [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] private static extern IntPtr GetProcAddress(IntPtr module, string procName); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool FreeLibrary(IntPtr module); } internal struct DbLinuxFrame { public int Status; public int Fd; public uint Width; public uint Height; public uint Fourcc; public uint Offset; public int Stride; } internal struct DbLinuxBridgeCall { public uint Op; public int Status; public ulong Arg0; public DbLinuxFrame Frame; public ulong Buffer; } internal sealed class SharedTextureBridge : IDisposable { private readonly ManualLogSource _log; private readonly ConcurrentQueue _mainThreadActions = new ConcurrentQueue(); private Messenger _messenger; private float _connectRetryTimer; private float _connectLogTimer; private const float ConnectRetryInterval = 1f; private const float ConnectLogInterval = 5f; private readonly Dictionary _activeSlots = new Dictionary(); private readonly Dictionary _activeGenerations = new Dictionary(); private static readonly ConcurrentDictionary _bridgeIndexToSlot = new ConcurrentDictionary(); private readonly List<(int slot, int generation, IBridgeTextureSlot textureSlot)> _pendingBinds = new List<(int, int, IBridgeTextureSlot)>(); private bool _rendererDevicePublished; internal int ActiveSlotCount => _activeSlots.Count; internal int PendingBindCount => _pendingBinds.Count; internal int TotalTextureRequestCount { get { int num = 0; foreach (IBridgeTextureSlot value in _activeSlots.Values) { num += value.RequestCount; } return num; } } internal SharedTextureBridge(ManualLogSource log) { _log = log; SharedTextureBridgePlugin.LogInfo("[SharedTextureBridge] Constructed"); } internal static IDisplayTextureSource GetSlotForBridgeIndex(int bridgeIndex) { _bridgeIndexToSlot.TryGetValue(bridgeIndex, out var value); return value; } internal void Update() { SharedTextureSlot.ProcessDeferredNativeReleases(); TryEnsureMessenger(); TryPublishRendererDevice(); Action result; while (_mainThreadActions.TryDequeue(out result)) { try { result(); } catch (Exception ex) { SharedTextureBridgePlugin.LogError("IPC action failed", ex); } } foreach (IBridgeTextureSlot value2 in _activeSlots.Values) { try { value2.Tick(); } catch (Exception ex2) { SharedTextureBridgePlugin.LogError("Texture slot tick failed", ex2); } } for (int num = _pendingBinds.Count - 1; num >= 0; num--) { var (num2, num3, bridgeTextureSlot) = _pendingBinds[num]; if (!_activeGenerations.TryGetValue(num2, out var value) || value != num3) { _pendingBinds.RemoveAt(num); continue; } bool flag; try { flag = bridgeTextureSlot.TryBind(); } catch (Exception ex3) { SharedTextureBridgePlugin.LogError($"Pending TryBind threw slot={num2}", ex3); continue; } if (flag) { _pendingBinds.RemoveAt(num); WriteRunning(num2, num3, bridgeTextureSlot); SharedTextureBridgePlugin.LogInfo($"[SharedTextureBridge] Slot {num2} gen={num3} bound: {bridgeTextureSlot.Width}x{bridgeTextureSlot.Height}"); } } } private void TryPublishRendererDevice() { if (_rendererDevicePublished || _messenger == null) { return; } try { if (UnityD3D11Device.Initialize(_log) && UnityD3D11Device.HasAdapterInfo) { _messenger.SendObject("RendererDevice", new SharedTextureRendererDeviceMessage { AdapterLuid = UnityD3D11Device.AdapterLuid, VendorId = UnityD3D11Device.AdapterVendorId, Description = UnityD3D11Device.AdapterDescription }); _rendererDevicePublished = true; SharedTextureBridgePlugin.LogInfo($"[SharedTextureBridge] Published renderer adapter '{UnityD3D11Device.AdapterDescription}' VendorId=0x{UnityD3D11Device.AdapterVendorId:X4} LUID=0x{UnityD3D11Device.AdapterLuid:X16}"); } } catch (Exception ex) { SharedTextureBridgePlugin.LogWarning("[SharedTextureBridge] Failed to publish renderer adapter: " + ex.Message); } } private void TryEnsureMessenger() { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown if (_messenger != null) { return; } _connectRetryTimer += Time.unscaledDeltaTime; _connectLogTimer += Time.unscaledDeltaTime; if (_connectLogTimer >= 5f) { _connectLogTimer = 0f; SharedTextureBridgePlugin.LogInfo("[SharedTextureBridge] Waiting for InterprocessLib queue " + SharedTextureBridgeProtocol.QueueName); } if (_connectRetryTimer < 1f) { return; } _connectRetryTimer = 0f; try { SharedTextureBridgePlugin.LogInfo("[SharedTextureBridge] Creating Messenger"); Messenger.OnWarning += OnWarning; Messenger.OnFailure += OnFailure; _messenger = new Messenger(SharedTextureBridgeProtocol.OwnerId, false, SharedTextureBridgeProtocol.QueueName, (IMemoryPackerEntityPool)(object)SimpleMemoryPackerPool.Instance, 1048576L); RegisterMessages(); SharedTextureBridgePlugin.LogInfo("Opened InterprocessLib queue: " + SharedTextureBridgeProtocol.QueueName); } catch (Exception ex) { Messenger.OnWarning -= OnWarning; Messenger.OnFailure -= OnFailure; Messenger messenger = _messenger; if (messenger != null) { messenger.Dispose(); } _messenger = null; SharedTextureBridgePlugin.LogWarning("InterprocessLib queue not ready: " + ex.GetType().Name + ": " + ex.Message); } } private void RegisterMessages() { _messenger.ReceiveObject("Start", (Action)delegate(SharedTextureStartMessage msg) { try { if (msg != null) { SharedTextureBridgePlugin.LogInfo($"[SharedTextureBridge] Received Start slot={msg.SlotId} gen={msg.Generation} name={msg.SharedTextureName} shared=0x{msg.SharedTextureHandle:X} {msg.SharedTextureWidth}x{msg.SharedTextureHeight}"); _mainThreadActions.Enqueue(delegate { StartSharedTexture(msg.SlotId, msg.Generation, msg.SharedTextureHandle, msg.SharedTextureName, msg.SharedTextureWidth, msg.SharedTextureHeight); }); } } catch (Exception ex) { SharedTextureBridgePlugin.LogError("[SharedTextureBridge] Start callback failed", ex); } }); _messenger.ReceiveObject("Stop", (Action)delegate(SharedTextureStopMessage msg) { try { if (msg != null) { SharedTextureBridgePlugin.LogInfo($"[SharedTextureBridge] Received Stop slot={msg.SlotId} gen={msg.Generation}"); _mainThreadActions.Enqueue(delegate { StopSharedTexture(msg.SlotId, msg.Generation, sendStopped: true); }); } } catch (Exception ex) { SharedTextureBridgePlugin.LogError("[SharedTextureBridge] Stop callback failed", ex); } }); _messenger.ReceiveObject("Running", (Action)delegate { }); _messenger.ReceiveObject("Stopped", (Action)delegate { }); _messenger.ReceiveObject("RendererDevice", (Action)delegate { }); } private void StartSharedTexture(int slot, int generation, long sharedTextureHandleRaw, string sharedTextureName, int sharedTextureWidth, int sharedTextureHeight) { if (sharedTextureHandleRaw == -1 && TryParseLinuxCaptureName(sharedTextureName, out var pipeWireNodeId)) { StartLinuxCapture(slot, generation, pipeWireNodeId, sharedTextureWidth, sharedTextureHeight); return; } if (_activeSlots.ContainsKey(slot)) { StopSharedTexture(slot, _activeGenerations.TryGetValue(slot, out var value) ? value : 0, sendStopped: false); } IntPtr intPtr = new IntPtr(sharedTextureHandleRaw); SharedTextureBridgePlugin.LogInfo($"Starting shared texture slot={slot} gen={generation} name={sharedTextureName} shared=0x{sharedTextureHandleRaw:X} {sharedTextureWidth}x{sharedTextureHeight}"); if (intPtr == IntPtr.Zero || sharedTextureWidth <= 0 || sharedTextureHeight <= 0) { SharedTextureBridgePlugin.LogWarning($"Shared texture start ignored slot={slot} gen={generation}: missing handle or size"); return; } SharedTextureSlot sharedTextureSlot; try { sharedTextureSlot = new SharedTextureSlot(intPtr, sharedTextureWidth, sharedTextureHeight, _log); _activeSlots[slot] = sharedTextureSlot; _activeGenerations[slot] = generation; _bridgeIndexToSlot[10000 + slot] = (IDisplayTextureSource)(object)sharedTextureSlot; SharedTextureBridgePlugin.LogInfo($"Registered bridge index={10000 + slot} slot={slot} gen={generation}"); } catch (Exception ex) { SharedTextureBridgePlugin.LogError($"Shared texture slot construction failed slot={slot} gen={generation}", ex); return; } bool flag; try { flag = sharedTextureSlot.TryBind(); } catch (Exception ex2) { SharedTextureBridgePlugin.LogError($"Initial TryBind threw slot={slot} gen={generation}", ex2); return; } if (flag) { WriteRunning(slot, generation, sharedTextureSlot); return; } SharedTextureBridgePlugin.LogWarning($"Initial TryBind failed slot={slot} gen={generation}; adding pending bind"); _pendingBinds.Add((slot, generation, sharedTextureSlot)); } private void StopSharedTexture(int slot, int generation, bool sendStopped) { if (!_activeSlots.ContainsKey(slot)) { if (sendStopped) { WriteStopped(slot, generation); } return; } if (_activeGenerations.TryGetValue(slot, out var value) && value != generation) { SharedTextureBridgePlugin.LogWarning($"Ignoring stale shared texture stop slot={slot} gen={generation} active={value}"); return; } SharedTextureBridgePlugin.LogInfo($"Stopping shared texture slot={slot} gen={generation}"); _bridgeIndexToSlot.TryRemove(10000 + slot, out var _); SharedTextureBridgePlugin.LogInfo($"[SharedTextureBridge] Removed bridge index for slot={slot}"); try { _activeSlots[slot].Dispose(); SharedTextureBridgePlugin.LogInfo($"[SharedTextureBridge] Slot dispose completed slot={slot}"); } catch (Exception ex) { SharedTextureBridgePlugin.LogError($"[SharedTextureBridge] Slot dispose failed slot={slot}", ex); } _activeSlots.Remove(slot); _activeGenerations.Remove(slot); _pendingBinds.RemoveAll(((int slot, int generation, IBridgeTextureSlot textureSlot) p) => p.slot == slot); if (sendStopped) { WriteStopped(slot, generation); } SharedTextureBridgePlugin.LogInfo($"[SharedTextureBridge] Stop complete slot={slot}"); } private static bool TryParseLinuxCaptureName(string name, out uint pipeWireNodeId) { pipeWireNodeId = 0u; if (string.IsNullOrEmpty(name) || !name.StartsWith("DesktopBuddyLinuxCapture:", StringComparison.Ordinal)) { return false; } if (uint.TryParse(name.Substring("DesktopBuddyLinuxCapture:".Length), out pipeWireNodeId)) { return pipeWireNodeId != 0; } return false; } private void StartLinuxCapture(int slot, int generation, uint pipeWireNodeId, int widthHint, int heightHint) { StopSharedTexture(slot, _activeGenerations.TryGetValue(slot, out var value) ? value : 0, sendStopped: false); try { LinuxCaptureTextureSlot linuxCaptureTextureSlot = new LinuxCaptureTextureSlot(pipeWireNodeId, widthHint, heightHint, _log); _activeSlots[slot] = linuxCaptureTextureSlot; _activeGenerations[slot] = generation; _bridgeIndexToSlot[10000 + slot] = (IDisplayTextureSource)(object)linuxCaptureTextureSlot; if (linuxCaptureTextureSlot.TryBind()) { WriteRunning(slot, generation, linuxCaptureTextureSlot); SharedTextureBridgePlugin.LogInfo($"[SharedTextureBridge] Linux capture slot={slot} gen={generation} node={pipeWireNodeId} running {linuxCaptureTextureSlot.Width}x{linuxCaptureTextureSlot.Height}"); } else { _pendingBinds.Add((slot, generation, linuxCaptureTextureSlot)); SharedTextureBridgePlugin.LogWarning($"[SharedTextureBridge] Linux capture slot={slot} node={pipeWireNodeId} pending bind"); } } catch (Exception ex) { SharedTextureBridgePlugin.LogError($"[SharedTextureBridge] Linux capture start failed slot={slot} gen={generation}", ex); } } private void WriteRunning(int slot, int generation, IBridgeTextureSlot textureSlot) { try { Messenger messenger = _messenger; if (messenger != null) { messenger.SendObject("Running", new SharedTextureRunningMessage { SlotId = slot, Generation = generation, Width = textureSlot.Width, Height = textureSlot.Height }); } } catch (Exception ex) { SharedTextureBridgePlugin.LogWarning($"Failed to send running ack for slot {slot}: {ex.Message}"); } SharedTextureBridgePlugin.LogInfo($"Shared texture slot={slot} gen={generation} running: {textureSlot.Width}x{textureSlot.Height}"); } private void WriteStopped(int slot, int generation) { try { Messenger messenger = _messenger; if (messenger != null) { messenger.SendObject("Stopped", new SharedTextureStoppedMessage { SlotId = slot, Generation = generation }); } } catch (Exception ex) { SharedTextureBridgePlugin.LogWarning($"Failed to send stopped ack for slot {slot} gen={generation}: {ex.Message}"); } SharedTextureBridgePlugin.LogInfo($"Shared texture slot={slot} gen={generation} stopped"); } private void OnWarning(string message) { SharedTextureBridgePlugin.LogWarning("[InterprocessLib] " + message); } private void OnFailure(Exception ex) { SharedTextureBridgePlugin.LogError("[InterprocessLib]", ex); } public void Dispose() { SharedTextureBridgePlugin.LogInfo("[SharedTextureBridge] Disposing"); foreach (KeyValuePair activeSlot in _activeSlots) { try { SharedTextureBridgePlugin.LogInfo($"[SharedTextureBridge] Disposing slot={activeSlot.Key}"); activeSlot.Value.Dispose(); } catch (Exception ex) { SharedTextureBridgePlugin.LogError($"[SharedTextureBridge] Slot dispose failed during bridge dispose slot={activeSlot.Key}", ex); } } _activeSlots.Clear(); _activeGenerations.Clear(); _bridgeIndexToSlot.Clear(); _pendingBinds.Clear(); try { SharedTextureBridgePlugin.LogInfo("[SharedTextureBridge] Messenger.Dispose START"); Messenger messenger = _messenger; if (messenger != null) { messenger.Dispose(); } SharedTextureBridgePlugin.LogInfo("[SharedTextureBridge] Messenger.Dispose DONE"); } catch (Exception ex2) { SharedTextureBridgePlugin.LogError("[SharedTextureBridge] Messenger.Dispose failed", ex2); } _messenger = null; Messenger.OnWarning -= OnWarning; Messenger.OnFailure -= OnFailure; SharedTextureBridgePlugin.LogInfo("[SharedTextureBridge] Disposed"); } } [BepInPlugin("net.desktopbuddy.sharedtexturebridge", "DesktopBuddySharedTextureBridge", "1.1.0")] public class SharedTextureBridgePlugin : BaseUnityPlugin { internal static ManualLogSource Log; private SharedTextureBridge _bridge; private void Awake() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; LogInfo("DesktopBuddySharedTextureBridge starting..."); try { new Harmony("net.desktopbuddy.sharedtexturebridge").PatchAll(); LogInfo("Harmony patches applied"); } catch (Exception ex) { LogError("Harmony PatchAll failed", ex); throw; } try { _bridge = new SharedTextureBridge(Log); LogInfo("SharedTextureBridge created"); } catch (Exception ex2) { LogError("SharedTextureBridge creation failed", ex2); throw; } LogInfo("DesktopBuddySharedTextureBridge ready"); } private void Update() { try { _bridge?.Update(); } catch (Exception ex) { LogError("Update failed", ex); } } private void OnDestroy() { LogInfo("DesktopBuddySharedTextureBridge OnDestroy START"); try { _bridge?.Dispose(); LogInfo("SharedTextureBridge disposed"); } catch (Exception ex) { LogError("SharedTextureBridge dispose failed", ex); } try { UnityD3D11Device.Dispose(); LogInfo("UnityD3D11Device disposed"); } catch (Exception ex2) { LogError("UnityD3D11Device dispose failed", ex2); } LogInfo("DesktopBuddySharedTextureBridge OnDestroy DONE"); } internal static void LogInfo(string message) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)message); } } internal static void LogWarning(string message) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)message); } } internal static void LogError(string message, Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"{message}: {ex}"); } } } [HarmonyPatch(typeof(DisplayDriver), "TryGetDisplayTexture")] internal static class SharedTextureIndexPatch { private static bool Prefix(int index, ref IDisplayTextureSource __result) { try { if (index < 10000) { return true; } IDisplayTextureSource slotForBridgeIndex = SharedTextureBridge.GetSlotForBridgeIndex(index); __result = slotForBridgeIndex; return false; } catch (Exception ex) { SharedTextureBridgePlugin.LogError($"[SharedTextureIndexPatch] Prefix failed index={index}", ex); __result = null; return false; } } } internal sealed class SharedTextureSlot : IBridgeTextureSlot, IDisplayTextureSource, IDisposable { private struct DeferredNativeRelease { public IntPtr ShaderResourceView; public IntPtr OpenedTexture; public long SharedHandle; public int FramesRemaining; } private struct D3D11_TEX2D_SRV { public uint MostDetailedMip; public uint MipLevels; } private struct D3D11_SHADER_RESOURCE_VIEW_DESC { public uint Format; public uint ViewDimension; public D3D11_TEX2D_SRV Texture2D; } private const int ID3D11Device_OpenSharedResource = 28; private const int ID3D11Device_CreateShaderResourceView = 7; private const uint DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91u; private const uint D3D11_SRV_DIMENSION_TEXTURE2D = 4u; private static readonly Guid Texture2DGuid = new Guid("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); private const int DeferredReleaseFrames = 3; private static readonly ConcurrentQueue DeferredReleases = new ConcurrentQueue(); private readonly ManualLogSource _log; private readonly IntPtr _sharedHandle; private readonly HashSet _requests = new HashSet(); private IntPtr _openedTexture; private IntPtr _shaderResourceView; private Texture2D _unityTexture; private bool _started; private bool _disposed; public Texture UnityTexture => (Texture)(object)_unityTexture; public int Width { get; } public int Height { get; } public int RequestCount => _requests.Count; public bool IsValid { get { if (!_disposed && _started) { return (Object)(object)_unityTexture != (Object)null; } return false; } } public string SourceName => "SharedTexture"; internal SharedTextureSlot(IntPtr sharedHandle, int width, int height, ManualLogSource log) { _sharedHandle = sharedHandle; Width = width; Height = height; _log = log; SharedTextureBridgePlugin.LogInfo($"[SharedTexture] Constructed handle=0x{_sharedHandle.ToInt64():X} {Width}x{Height}"); } public bool TryBind() { if (_started) { return true; } if (_disposed) { return false; } if (_sharedHandle == IntPtr.Zero || Width <= 0 || Height <= 0) { return false; } if (!UnityD3D11Device.IsReady && !UnityD3D11Device.Initialize(_log)) { SharedTextureBridgePlugin.LogWarning("[SharedTexture] Renderer D3D device is not ready"); return false; } try { OpenSharedTexture(); CreateShaderResourceView(); _unityTexture = Texture2D.CreateExternalTexture(Width, Height, (TextureFormat)14, false, false, _shaderResourceView); Texture2D unityTexture = _unityTexture; IntPtr sharedHandle = _sharedHandle; ((Object)unityTexture).name = $"DesktopBuddy SharedTexture 0x{sharedHandle.ToInt64():X}"; ((Texture)_unityTexture).wrapMode = (TextureWrapMode)1; _started = true; object[] array = new object[5]; sharedHandle = _sharedHandle; array[0] = sharedHandle.ToInt64(); array[1] = _openedTexture.ToInt64(); array[2] = _shaderResourceView.ToInt64(); array[3] = Width; array[4] = Height; SharedTextureBridgePlugin.LogInfo(string.Format("[SharedTexture] Bound handle=0x{0:X} texture=0x{1:X} srv=0x{2:X} {3}x{4}", array)); NotifyCallbacks(); return true; } catch (Exception ex) { SharedTextureBridgePlugin.LogError("[SharedTexture] TryBind failed", ex); Dispose(); return false; } } public void Tick() { } public void RegisterRequest(Action onTextureChanged) { try { if (onTextureChanged != null) { _requests.Add(onTextureChanged); } if ((Object)(object)_unityTexture != (Object)null) { onTextureChanged?.Invoke(); } } catch (Exception ex) { SharedTextureBridgePlugin.LogError("[SharedTexture] RegisterRequest callback failed", ex); } } public void UnregisterRequest(Action onTextureChanged) { try { if (onTextureChanged != null) { _requests.Remove(onTextureChanged); } } catch (Exception ex) { SharedTextureBridgePlugin.LogError("[SharedTexture] UnregisterRequest failed", ex); } } public void Dispose() { object[] array = new object[7]; IntPtr sharedHandle = _sharedHandle; array[0] = sharedHandle.ToInt64(); array[1] = _disposed; array[2] = _started; array[3] = (Object)(object)_unityTexture != (Object)null; array[4] = _shaderResourceView.ToInt64(); array[5] = _openedTexture.ToInt64(); array[6] = _requests.Count; SharedTextureBridgePlugin.LogInfo(string.Format("[SharedTexture] Dispose ENTER handle=0x{0:X} disposed={1} started={2} unityTexture={3} srv=0x{4:X} texture=0x{5:X} requests={6}", array)); if (!_disposed) { _disposed = true; if ((Object)(object)_unityTexture != (Object)null) { sharedHandle = _sharedHandle; SharedTextureBridgePlugin.LogInfo($"[SharedTexture] Unity texture Destroy START handle=0x{sharedHandle.ToInt64():X}"); Object.Destroy((Object)(object)_unityTexture); _unityTexture = null; sharedHandle = _sharedHandle; SharedTextureBridgePlugin.LogInfo($"[SharedTexture] Unity texture Destroy queued handle=0x{sharedHandle.ToInt64():X}"); } if (_shaderResourceView != IntPtr.Zero || _openedTexture != IntPtr.Zero) { ConcurrentQueue deferredReleases = DeferredReleases; DeferredNativeRelease item = new DeferredNativeRelease { ShaderResourceView = _shaderResourceView, OpenedTexture = _openedTexture }; sharedHandle = _sharedHandle; item.SharedHandle = sharedHandle.ToInt64(); item.FramesRemaining = 3; deferredReleases.Enqueue(item); object[] array2 = new object[4]; sharedHandle = _sharedHandle; array2[0] = sharedHandle.ToInt64(); array2[1] = 3; array2[2] = _shaderResourceView.ToInt64(); array2[3] = _openedTexture.ToInt64(); SharedTextureBridgePlugin.LogInfo(string.Format("[SharedTexture] Deferred native release queued handle=0x{0:X} frames={1} srv=0x{2:X} texture=0x{3:X}", array2)); _shaderResourceView = IntPtr.Zero; _openedTexture = IntPtr.Zero; } _requests.Clear(); sharedHandle = _sharedHandle; SharedTextureBridgePlugin.LogInfo($"[SharedTexture] Disposed handle=0x{sharedHandle.ToInt64():X}"); } } internal static void ProcessDeferredNativeReleases() { int count = DeferredReleases.Count; for (int i = 0; i < count; i++) { if (!DeferredReleases.TryDequeue(out var result)) { break; } result.FramesRemaining--; if (result.FramesRemaining > 0) { DeferredReleases.Enqueue(result); continue; } SharedTextureBridgePlugin.LogInfo($"[SharedTexture] Deferred native release START handle=0x{result.SharedHandle:X} srv=0x{result.ShaderResourceView.ToInt64():X} texture=0x{result.OpenedTexture.ToInt64():X}"); try { if (result.ShaderResourceView != IntPtr.Zero) { Marshal.Release(result.ShaderResourceView); } if (result.OpenedTexture != IntPtr.Zero) { Marshal.Release(result.OpenedTexture); } } catch (Exception ex) { SharedTextureBridgePlugin.LogError("[SharedTexture] Deferred native release failed", ex); } SharedTextureBridgePlugin.LogInfo($"[SharedTexture] Deferred native release DONE handle=0x{result.SharedHandle:X}"); } } private unsafe void OpenSharedTexture() { delegate* unmanaged[Stdcall] delegate* = (delegate* unmanaged[Stdcall])(void*)(*(IntPtr*)((nint)(*(IntPtr*)(void*)UnityD3D11Device.D3dDevice) + (nint)28 * (nint)sizeof(IntPtr))); Guid texture2DGuid = Texture2DGuid; IntPtr intPtr = default(IntPtr); int num = delegate*(UnityD3D11Device.D3dDevice, _sharedHandle, &texture2DGuid, &intPtr); if (num < 0 || intPtr == IntPtr.Zero) { object arg = num; IntPtr sharedHandle = _sharedHandle; throw new InvalidOperationException($"OpenSharedResource failed hr=0x{arg:X8} handle=0x{sharedHandle.ToInt64():X}"); } _openedTexture = intPtr; } private unsafe void CreateShaderResourceView() { D3D11_SHADER_RESOURCE_VIEW_DESC d3D11_SHADER_RESOURCE_VIEW_DESC = new D3D11_SHADER_RESOURCE_VIEW_DESC { Format = 91u, ViewDimension = 4u, Texture2D = new D3D11_TEX2D_SRV { MostDetailedMip = 0u, MipLevels = 1u } }; IntPtr intPtr = default(IntPtr); int num = ((delegate* unmanaged[Stdcall])(void*)(*(IntPtr*)((nint)(*(IntPtr*)(void*)UnityD3D11Device.D3dDevice) + (nint)7 * (nint)sizeof(IntPtr))))(UnityD3D11Device.D3dDevice, _openedTexture, &d3D11_SHADER_RESOURCE_VIEW_DESC, &intPtr); if (num < 0 || intPtr == IntPtr.Zero) { throw new InvalidOperationException($"CreateShaderResourceView failed hr=0x{num:X8} texture=0x{_openedTexture.ToInt64():X}"); } _shaderResourceView = intPtr; } private void NotifyCallbacks() { foreach (Action request in _requests) { try { request?.Invoke(); } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("[SharedTexture] Callback error: " + ex.Message)); } } } } } internal static class UnityD3D11Device { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct DXGI_ADAPTER_DESC { public unsafe fixed char Description[128]; public uint VendorId; public uint DeviceId; public uint SubSysId; public uint Revision; public UIntPtr DedicatedVideoMemory; public UIntPtr DedicatedSystemMemory; public UIntPtr SharedSystemMemory; public long AdapterLuid; } private const int ID3D11Resource_GetDevice = 3; private const int IDXGIDevice_GetAdapter = 7; private const int IDXGIAdapter_GetDesc = 8; private static readonly object Lock = new object(); private static IntPtr _d3dDevice; private static bool _initialized; private static long _adapterLuid; private static int _adapterVendorId; private static string _adapterDescription; internal static IntPtr D3dDevice => _d3dDevice; internal static bool IsReady { get { if (_initialized) { return _d3dDevice != IntPtr.Zero; } return false; } } internal static long AdapterLuid => _adapterLuid; internal static int AdapterVendorId => _adapterVendorId; internal static string AdapterDescription => _adapterDescription; internal static bool HasAdapterInfo => _adapterLuid != 0; internal unsafe static bool Initialize(ManualLogSource log) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown Info(log, "[UnityD3D11] Initialize entered"); if (IsReady) { return true; } lock (Lock) { Info(log, "[UnityD3D11] Initialize lock entered"); if (IsReady) { return true; } try { Texture2D val = null; Info(log, "[UnityD3D11] Creating Unity probe texture"); try { val = new Texture2D(1, 1, (TextureFormat)14, false, true); Info(log, "[UnityD3D11] Applying Unity probe texture"); val.Apply(false, true); Info(log, "[UnityD3D11] Getting native texture pointer"); IntPtr nativeTexturePtr = ((Texture)val).GetNativeTexturePtr(); if (nativeTexturePtr == IntPtr.Zero) { Error(log, "[UnityD3D11] Unity probe texture native pointer is null"); return false; } Info(log, $"[UnityD3D11] Probe native texture=0x{nativeTexturePtr.ToInt64():X}"); Info(log, "[UnityD3D11] Reading D3D resource vtable"); delegate* unmanaged[Stdcall] delegate* = (delegate* unmanaged[Stdcall])(void*)(*(IntPtr*)((nint)(*(IntPtr*)(void*)nativeTexturePtr) + (nint)3 * (nint)sizeof(IntPtr))); Info(log, "[UnityD3D11] Calling ID3D11Resource.GetDevice"); IntPtr d3dDevice = default(IntPtr); delegate*(nativeTexturePtr, &d3dDevice); _d3dDevice = d3dDevice; } finally { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } if (_d3dDevice == IntPtr.Zero) { Error(log, "[UnityD3D11] ID3D11Resource.GetDevice returned null"); return false; } Info(log, $"[UnityD3D11] D3D device=0x{_d3dDevice.ToInt64():X}"); TryReadAdapterInfo(log); _initialized = true; Info(log, $"[UnityD3D11] Unity D3D11 device ready device=0x{_d3dDevice.ToInt64():X} adapter='{_adapterDescription}' vendor=0x{_adapterVendorId:X4} LUID=0x{_adapterLuid:X16}"); return true; } catch (Exception arg) { if (log != null) { log.LogError((object)$"[UnityD3D11] Renderer device init failed: {arg}"); } return false; } } } private unsafe static void TryReadAdapterInfo(ManualLogSource log) { Guid iid = new Guid("54ec77fa-1377-44e6-8c32-88fd5f44c84c"); IntPtr ppv = IntPtr.Zero; IntPtr zero = IntPtr.Zero; try { int num = Marshal.QueryInterface(_d3dDevice, in iid, out ppv); if (num < 0 || ppv == IntPtr.Zero) { Info(log, $"[UnityD3D11] IDXGIDevice QueryInterface failed hr=0x{num:X8}"); return; } num = ((delegate* unmanaged[Stdcall])(void*)(*(IntPtr*)((nint)(*(IntPtr*)(void*)ppv) + (nint)7 * (nint)sizeof(IntPtr))))(ppv, &zero); if (num < 0 || zero == IntPtr.Zero) { Info(log, $"[UnityD3D11] IDXGIDevice.GetAdapter failed hr=0x{num:X8}"); return; } DXGI_ADAPTER_DESC dXGI_ADAPTER_DESC = default(DXGI_ADAPTER_DESC); num = ((delegate* unmanaged[Stdcall])(void*)(*(IntPtr*)((nint)(*(IntPtr*)(void*)zero) + (nint)8 * (nint)sizeof(IntPtr))))(zero, &dXGI_ADAPTER_DESC); if (num < 0) { Info(log, $"[UnityD3D11] IDXGIAdapter.GetDesc failed hr=0x{num:X8}"); return; } _adapterLuid = dXGI_ADAPTER_DESC.AdapterLuid; _adapterVendorId = (int)dXGI_ADAPTER_DESC.VendorId; _adapterDescription = new string(dXGI_ADAPTER_DESC.Description).TrimEnd(new char[1]); Info(log, $"[UnityD3D11] Adapter '{_adapterDescription}' VendorId=0x{dXGI_ADAPTER_DESC.VendorId:X4} LUID=0x{dXGI_ADAPTER_DESC.AdapterLuid:X16}"); } catch (Exception ex) { if (log != null) { log.LogWarning((object)("[UnityD3D11] Failed to read adapter info: " + ex.Message)); } } finally { if (zero != IntPtr.Zero) { Marshal.Release(zero); } if (ppv != IntPtr.Zero) { Marshal.Release(ppv); } } } internal static void Dispose() { SharedTextureBridgePlugin.LogInfo($"[UnityD3D11] Dispose ENTER initialized={_initialized} device=0x{_d3dDevice.ToInt64():X}"); lock (Lock) { SharedTextureBridgePlugin.LogInfo("[UnityD3D11] Dispose lock entered"); if (_d3dDevice != IntPtr.Zero) { try { Marshal.Release(_d3dDevice); } catch (Exception ex) { SharedTextureBridgePlugin.LogError("[UnityD3D11] Device release failed", ex); } _d3dDevice = IntPtr.Zero; } _initialized = false; _adapterLuid = 0L; _adapterVendorId = 0; _adapterDescription = null; } SharedTextureBridgePlugin.LogInfo("[UnityD3D11] Dispose EXIT"); } private static void Info(ManualLogSource log, string message) { if (log != null) { log.LogInfo((object)message); } } private static void Error(ManualLogSource log, string message) { if (log != null) { log.LogError((object)message); } } } internal static class BridgeVersionInfo { internal const string Version = "1.1.0"; } }